001-redis-01

redis和memcached什么区别?为什么高并发下有时单线程的redis比多线程的memcached效率要高?

区别:

1 me可缓存图片和视频。rd支持除k/v更多的数据结构;

2. rd可以使用虚拟内存,rd可持久化和aof灾难恢复,rd通过主从支持数据备份**;**

3. rd可以做消息队列。

原因**:me**多线程模型引入了缓存一致性和锁,加锁带来了性能损耗

Redis为什么那么快

1、完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);

2、数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;

3、采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;

4、使用多路I/O复用模型,非阻塞IO;

5、使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;

(1)多路 I/O 复用模型

多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。

**这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。**采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),且 Redis 在内存中操作数据的速度非常快,也就是说内存内的操作不会成为影响Redis性能的瓶颈,主要由以上几点造就了 Redis 具有很高的吞吐量。

Pipeline有什么好处,为什么要用pipeline?

可以将多次IO往返的时间缩减为一次,前提是pipeline执行的指令之间没有因果相关性。使用redis-benchmark进行压测的时候可以发现影响redis的QPS峰值的一个重要因素是pipeline批次指令的数目。

Redis中的Multi和Pipleline都可以一次性执行多个命令,但是Pipeline只是把多个redis指令一起发出去,redis并没有保证这些指令执行的顺序,且减少了多次网络传递的开销,因而其执行效率很高;Multi相当于一个redis的transaction,保证整个操作的有序性,通过watch这些key,可以避免这些key在事务的执行过程中被其它的命令修改,从而导致得的到结果不是所期望的。

redis主从复制如何实现的?redis的集群模式如何实现?redis的key 是如何寻址的?

主从复制实现**:主节点将自己内存中的数据做一份快照,将快照发给从节点,从节点将数据恢复到内存中。之后再每次增加新数据的时候,主节点以类似于mysql**的二进制日志方式将语句发送给从节点,从节点拿到主节点发送过来的语句进行重放。

分片方式:

客户端分片

基于代理的分片

Twemproxy

codis

路由查询分片

Redis-cluster(本身提供了自动将数据分散到Redis Cluster不同节点的能力,整个数据集合的某个数据子集存储在哪个节点对于用户来说是透明的**) redis-cluster分片原理:Cluster中有一个16384长度的槽(虚拟槽),编号分别为0-16383**。每个Master节点都会负责一部分的槽,当有某个key被 映射到某个Maste「负责的槽,那么这个Maste「负责为这个key提供服 务,至于哪个Master节点负责哪个槽,可以由用户指定,也可以在初始 化的时候自动生成,只有Master才拥有槽的所有权。Master节点维护着 一个16384/8字节的位序列,Master节点用bit来标识对于某个槽自己是 否拥有。比如对于编号为1的槽,Master只要判断序列的第二位(索引从 开始**)是不是为1即可。这种结构很容易添加或者删除节点。比如如果我 想新添加个节点D**,我需要从节点ABC中得部分槽到D上。

集群模式:

  • 主从复制模式:在主从复制模式中,一个 Redis 实例充当主节点,其他实例充当从节点。主节点负责写入数据,从节点负责读取数据。主节点发生故障时,从节点可以自动晋升为主节点。

  • 哨兵模式:在哨兵模式中,多个哨兵实例共同监控主节点和从节点的状态。当主节点发生故障时,哨兵会自动选举一个从节点为新的主节点。

  • 集群模式:在集群模式中,多个 Redis 实例组成一个集群。数据会均匀分布在各个节点上。集群模式可以有效提高 Redis 的性能和可用性。

在单机模式下,Redis 使用哈希表来存储 key-value 数据。哈希表是根据 key 的哈希值来进行寻址的。

使用redis如何设计分布式锁?说一下实现思路?使用zk可以吗?如何实现?这两种有什么区别?

redis:

我们加锁就一行代码:jedis.set(String key, String value, String nxxx, String expx, int time),这个set()方法一共有五个形参:

  • 第一个为key,我们使用key来当锁,因为key是唯一的。

  • 第二个为value,我们传的是requestId,很多童鞋可能不明白,有key作为锁不就够了吗,为什么还要用到value?原因就是我们在上面讲到可靠性时,分布式锁要满足第四个条件解铃还须系铃人,通过给value赋值为requestId,我们就知道这把锁是哪个请求加的了,在解锁的时候就可以有依据。requestId可以使用UUID.randomUUID().toString()方法生成。

  • 第三个为nxxx,这个参数我们填的是NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;

  • 第四个为expx,这个参数我们传的是PX,意思是我们要给这个key加一个过期的设置,具体时间由第五个参数决定。

  • 第五个为time,与第四个参数相呼应,代表key的过期时间。

总的来说,执行上面的set()方法就只会导致两种结果:1. 当前没有锁(key不存在),那么就进行加锁操作,并对锁设置个有效期,同时value表示加锁的客户端。2. 已有锁存在,不做任何操作。

上面是高版本的redis的功能,如果是低版本的:

1:SETNX value 值=当前时间+过期超时时间,返回1 则获得锁,返回0则没有获得锁。转2。

2:GET 获取 value 的值 。判断锁是否过期超时。如果超时,转3。

3:**GETSET(将给定 key 的值设为 value ,并返回 key 的旧值),**GETSET value 值=当前时间+过期超时时间, 判断得到的value 如果仍然是超时的,那就说明得到锁,否则没有得到锁。

从2并发进到3 的操作,会多次改写超时时间,但这个不会有什么影响。

解锁:

 String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId));

可以看到,我们解锁只需要两行代码就搞定了!第一行代码,我们写了一个简单的Lua脚本代码,上一次见到这个编程语言还是在《黑客与画家》里,没想到这次居然用上了。第二行代码,我们将Lua代码传到jedis.eval()方法里,并使参数KEYS[1]赋值为lockKey,ARGV[1]赋值为requestId。eval()方法是将Lua代码交给Redis服务端执行。

那么这段Lua代码的功能是什么呢?其实很简单,首先获取锁对应的value值,检查是否与requestId相等,如果相等则删除锁(解锁)。那么为什么要使用Lua语言来实现呢?因为要确保上述操作是原子性的。。那么为什么执行eval()方法可以确保原子性,源于Redis的特性,下面是官网对eval命令的部分解释:

简单来说,就是在eval命令执行Lua代码的时候,Lua代码将被当成一个命令去执行,并且直到eval命令执行完成,Redis才会执行其他命令。

zk:

1. 客户端对某个方法加锁时,在zk上的与该方法对应的指定节点的目录 下,生成一个唯一的瞬时有序节点nodel;

2. 客户端获取该路径下所有已经创建的子节点,如果发现自己创建的 nodel的序号是最小的,就认为这个客户端获得了锁。

3. 如果发现nodel不是最小的,则监听比自己创建节点序号小的最大的节点,进入等待。

4. 获取锁后,处理完逻辑,删除自己创建的nodel即可。区别**:zk**性能差 一些,开销大,实现简单。

知道redis的持久化吗?底层如何实现的?有什么优点缺点?

RDB(Redis DataBase:在不同的时间点将redis的数据生成的快照同步到磁 盘等介质上**):内存到硬盘的快照,定期更新。缺点:耗时,耗性能(fork+io操 作)**,易丟失数据。

AOF(Append Only File:将redis所执行过的所有指令都记录下来,在下次 redis重启时,只需要执行指令就可以了**):**写日志。缺点:体积九恢复速度慢。

bgsave做镜像全量持久化,aof做增量持久化。因为bgsave会消耗比较 长的时间,不够实时,在停机的时候会导致大量的数据丟失,需要aof来配合,在redis实例重启时,优先使用aof来恢复内存的状态,如果没有aof日志,就会使用rdb文件来恢复。Redis会定期做aof重写,压缩aof文件日志大小。Redis4.0之后有了混合持久化的功能,将bgsave的全量和aof的增量做了融合处理,这样既保证了恢复的效率又兼顾了数据的安全性

redis过期策略都有哪些?LRU算法知道吗?写一下java代码实现?

过期策略:

定时过期(一key—定时器**),惰性过期:只有使用key时才判断key是否已 过期,过期则清除。定期过期:**前两者折中

LRU算法实现:

1. 通过双向链表来实现,新数据插入到链表头部;

2. 每当缓存命中**(即缓存数据被访问)**,则将数据移到链表头部;

3. 当链表满的时候,将链表尾部的数据丟弃。

LinkedHashMap:HashMap 和双向链表合二为一即是 LinkedHashMapHashMap是无序的,LinkedHashMap通过维护一个额外的双向链表保证了迭代顺序。该迭代顺序可以是插入顺序**(默认)**,也可以是访问顺序。

缓存穿透、缓存击穿、缓存雪崩解决方案?

缓存穿透:指查询一个一定不存在的数据,如果从存储层查不到数据则不写入缓存,这将导致这个不存在的数据每次请求都要到DB去查询,可能导致DB挂掉。

解决方案:

1. 查询返回的数据为空,仍把这个空结果进行缓存,但过期时间会比较短;

2. 布隆过滤器:将所有可能存在的数据哈希到一个足够大的bitmap中,一个一定不存在的数据会被这个bitmap拦截掉,从而避免了对DB的查询。

缓存击穿:对于设置了过期时间的key,缓存在某个时间点过期的时候,恰好这时间点对这个Key有大量的并发请求过来,这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存,这个时候大并发的请求可能会瞬间把DB压垮。

解决方案:

1.使用互斥锁:当缓存失效时,不立即去loaddb,先使用如Redissetnx去设置一个互斥锁,当操作成功返回时再进行load db的操作并回设缓存,否则重试get缓存的方法。

**2.**永远不过期:物理不过期,但逻辑过期(后台异步线程去刷新)。

缓存雪崩:设置缓存时采用了相同的过期时间,导致缓存在某一时刻同时失效,请求全部转发到DB,DB瞬时压力过重雪崩。与缓存击穿的区别:雪崩是很多key,击穿是某一个key缓存。

解决方案**:缓存失效时间分散开**,比如可以在原有的失效时间基础上增加一个随机值,比如1-5分钟随机,这样每一个缓存的过期时间的重复率就 会降低,就很难引发集体失效的事件。

在选择缓存时,什么时候选择redis,什么时候选择memcached

选择redis的情况:

1、 复杂数据结构,value的数据是哈希,列表,集合,有序集合等这种情况下,会选择redis,因为memcache无法满足这些数据结构,最典型的使用场景是,用户订单列表,用户消息,帖子评论等。

2、 需要进行数据的持久化功能,但是注意,不要把redis当成数据库使用,如果redis挂了,内存能够快速恢复热数据,不会将压力瞬间压在数据库上,没有cache预热的过程。对于只读和数据一致性要求不高的场景可以采用持久化存储

3、 高可用,redis支持集群,可以实现主动复制,读写分离,而对于memcache如果想要实现高可用,需要进行二次开发。

4、 存储的内容比较memcache存储的value最大为1M

选择memcache的场景:

1、纯KV,数据量非常大的业务,使用memcache更合适,原因是

a) memcache的内存分配采用的是预分配内存池的管理方式,能够省去内存分配的时间,redis是临时申请空间,可能导致碎片化。

b) 虚拟内存使用,memcache将所有的数据存储在物理内存里,redis有自己的vm机制,理论上能够存储比物理内存更多的数据,当数据超量时, 引发swap,把冷数据刷新到磁盘上,从这点上,数据量大时**,memcache** 更快

c) 网络模型,memcache使用非阻塞的IO复用模型,redis也是使用非阻 塞的IO复用模型,但是redis还提供了一些非KV存储之外的排序,聚合 功能,复杂的CPU计算,会阻塞整个IO调度,从这点上由于redis提供 的功能较多,memcache更快些

d) 线程模型,memcache使用多线程,主线程监听,worker子线程接受请求,执行读写,这个过程可能存在锁冲突。redis使用的单线程,虽然无锁冲突,但是难以利用多核的特性提升吞吐量。

缓存与数据库不一致怎么办

假设采用的主存分离,读写分离的数据库,

如果一个线程A先删除缓存数据,然后将数据写入到主库当中,这个时候,主库和从库同步没有完成,线程B从缓存当中读取数据失败,从从库当中读取到旧数据,然后更新至缓存,这个时候,缓存当中的就是旧的数据。

发生上述不一致的原因在于,主从库数据不一致问题,加入了缓存之后, 主从不一致的时间被拉长了

处理思路**:**在从库有数据更新之后,将缓存当中的数据也同时进行更新,即当从库发生了数据更新之后,向缓存发出删除,淘汰这段时间写入的旧数据。

主从数据库不一致如何解决

场景描述,对于主从库,读写分离,如果主从库更新同步有时差,就会导致主从库数据的不一致

1、 忽略这个数据不一致,在数据一致性要求不高的业务下,未必需要时一致性

2、 强制读主库,使用一个高可用的主库,数据库读写都在主库,添加一个缓存,提升数据读取的性能。

3、 选择性读主库,添加一个缓存,用来记录必须读主库的数据,将哪个库,哪个表,哪个主键,作为缓存的key,设置缓存失效的时间为主从库同步的时间,如果缓存当中有这个数据,直接读取主库,如果缓存当中没有这个主键,就到对应的从库中读取。

Redis常见的性能问题和解决方案

1master最好不要做持久化工作,如RDB内存快照和AOF日志文件

2、 如果数据比较重要,某个slave开启AOF备份,策略设置成每秒同步一次

3、 为了主从复制的速度和连接的稳定性,masterSlave最好在一个局域网内

4、 尽量避免在压力大得主库上增加从库

5、 主从复制不要米用网状结构,尽星是线性结构,Master<--Slave1 <— Slave2 ....

Redis的数据淘汰策略有哪些

voltile-lru从已经设置过期时间的数据集中挑选最近最少使用的数据淘汰

voltile-ttl 在设置了过期时间的key中,把最早要过期的key优先删除 TTL(Time To Live)

voltile-random从已经设置过期时间的数据集任意选择淘汰数据

allkeys-lru 从数据集中挑选最近最少使用的数据淘汰

allkeys-random从数据集中任意选择淘汰的数据no-eviction禁止驱逐数据

Redis当中有哪些数据结构

字符串String、字典Hash、列表List、集合Set、有序集合SortedSet

  1. 字符串操作:

    • 设置字符串值:SET key value

    • 获取字符串值:GET key

    • 设置哈希表字段的值:HSET key field value

    • 获取哈希表字段的值:HGET key field

  2. 列表操作:

    • 在列表头部添加元素:LPUSH key value

    • 在列表尾部添加元素:RPUSH key value

    • 获取列表头部元素:LRANGE key 0 0

    • 获取列表尾部元素:LRANGE key -1 -1

  3. 集合操作:

    • 添加元素到集合:SADD key member

    • 移除集合中的元素:SREM key member

    • 检查集合中是否存在元素:SISMEMBER key member

  4. 有序集合操作:

    • 添加带有分数的元素到有序集合:ZADD key score member

    • 获取有序集合中特定分数范围内的元素:ZRANGE key start stop

    • 根据分数对有序集合进行排序:ZRANGE key withscores

如果是高级用户,那么还会有,如果你是Redis中级用户,还需要加上 下面几种数据结构HyperLogLogGeoBitmap (位存储)。

HyperLogLog(HLL)是一种基数估计算法,用于估算一个集合中不重复元素的个数,即基数。

HyperLogLog是用来做基数统计的算法,HyperLogLog的有点是,在输入元素的数量或者体积非常大时,基数所需的空间总是固定的,并且很小的。

每个HyperLogLog键只需要花费12kb的内存,就可以计算接近2^64个不同元素的基数。

因为HyperLogLog只会根据输入元素来计算基数,而不会存储输入元素本身,所以HyperLogLog不能像集合那样,返回输入的各个元素。

什么是基数?

比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。

PFADD key element... 添加指定元素到HyperLogLog中

PFCOUNT key.... 返回给定HyperLogLog的基数估算值

PFMERGE destkey sourcekey... 合并多个HyperLogLog为指定HYperLogLog

Redis GEO 主要用于存储地理位置信息,并对存储的信息进行操作,该功能在 Redis 3.2 版本新增。

Redis GEO 操作方法有:

  • geoadd:添加地理位置的坐标。

  • geopos:获取地理位置的坐标。

  • geodist:计算两个位置之间的距离。

  • georadius:根据用户给定的经纬度坐标来获取指定范围内的地理位置集合。

  • georadiusbymember:根据储存在位置集合里面的某个地点获取指定范围内的地理位置集合。

  • geohash:返回一个或多个位置对象的 geohash 值。

Redis 事务

Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证:

  • 批量操作在发送 EXEC 命令前被放入队列缓存。

  • 收到 EXEC 命令后进入事务执行,事务中任意命令执行失败,其余的命令依然被执行。

  • 在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。

以下是一个事务的例子, 它先以 MULTI 开始一个事务, 然后将多个命令入队到事务中, 最后由 EXEC 命令触发事务, 一并执行事务中的所有命令

单个 Redis 命令的执行是原子性的,但 Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的。

事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作,中间某条指令的失败不会导致前面已做指令的回滚,也不会造成后续的指令不做。

Redis 脚本

Redis 脚本使用 Lua 解释器来执行脚本。 Redis 2.6 版本通过内嵌支持 Lua 环境。执行脚本的常用命令为 EVAL

语法

Eval 命令的基本语法如下:

redis 127.0.0.1:6379> EVAL script numkeys key [key ...] arg [arg ...]

假如Redis里面有1亿个key,其中有10w个key是以某个固定的 已知的前缀开头的,如果将它们全部找出来?

使用keys指令可以扫出指定模式的key列表。

对方接着追问**:如果这个redis正在给线上的业务提供服务,那使用keys** 指令会有什么问题?

这个时候你要回答redis关键的一个特性**:redis的单线程的。keys指令会 导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能 恢复。这个时候可以使用scan指令,scan指令可以无阻塞的提取出指定 模式的key列表,但是会有一定的重复概率,在客户端做一次去重就可以 了,但是整体所花费的时间会比直接用keys**指令长。

使用Redis做过异步队列吗,是如何实现的

使用list类型保存数据信息,rpush生产消息,lpop消费消息,当lpop没 有消息时,可以sleep —段时间,然后再检查有没有信息,如果不想 sleep的话,可以使用lpop,在没有信息的时候,会一直阻塞,直到信息 的到来。

redis可以通过pub/sub主题订阅模式实现一个生产者,多个消 费者,当然也存在一定的缺点,当消费者下线时,生产的消息会丟失。

Redis如何实现延时队列

使用sortedset,使用时间戳做score,消息内容作为key,调用zadd来生产消息,消费者使用zrangbyscore获取n秒之前的数据做轮询处理。

什么是Redis?简述它的优缺点?

Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整 个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据 flush到硬盘上进行保存。

因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过10万次读 写操作,是已知性能最快的Key-Value DB

Redis的出色之处不仅仅是性能,Redis最大的魅力是支持保存多种数据结构,此外单个value的最大限制是1GB,不像memcached只能保存 1 MB的数据,因此Redis可以用来实现很多有用的功能。

比方说用他的List来做FIFO双向链表,实现一个轻量级的高性能消息队 列服务,用他的Set可以做高性能的tag系统等等。

另外Redis也可以对存入的Key-Value设置expire时间,因此也可以被当作一 个功能加强版的memcached来用。Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场 景主要局限在较小数据量的高性能操作和运算上。

Redis相比memcached有哪些优势?

(1) memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型

(2) redis的速度比memcached快很多

(3) redis可以持久化其数据

Redis的全称是什么?

Remote Dictionary Server。

一个字符串类型的值能存储最大容量是多少?

512M

Redis集群方案应该怎么做?都有哪些方案?

  1. twemproxy,大概概念是,它类似于一个代理方式,使用方法和普通Redis 无任何区别,设置好它下属的多个Redis 实例后, 使用时在本需要连接Redis 的地方改为连接twemproxy,它会以一个代理的身份接收请求并使用一致性hash 算法,将请求转接到具体Redis,将结果再返回twemproxy。使用方式简便(相对Redis 只需修改连接端口),对旧项目扩展的首选。问题:twemproxy 自身单端口实例的压力,使用一致性hash 后,对Redis 节点数量改变时候的计算值的改变,数据无法自动移动到新的节点。

  2. codis 目前用的最多的集群方案,基本和twemproxy—致的效果,但它支持在节 点数量改变情况下,旧节点数据可恢复到新hash节点。

Codis是一整套缓存解决方案,包含高可用、数据分片、监控、动态扩态 etc.。 走的是 Apps->代理->redis cluster

Redis cluster 单纯的数据分片,无其他功能。 走的是 Apps->redis server jump redis server

2. redis cluster 自带的集群,特点在于他的分布式算法不是一致性 hash,而是hash槽的概念,以及自身支持节点设置从节点。

3. 在业务代码层实现,起几个毫无关联的redis实例,在代码层,对key进行hash计算,然后去对应的redis实例操作数据。这种方式对hash层代码 要求比较高,考虑部分包括,节点失效后的替代算法方案,数据震荡后的自动脚本恢复,实例的监控,等等。

Redis Sentinel着眼于高可用,在master宕机时会自动将slave提升为master,继续提供服务。

Redis Cluster着眼于扩展性,在单个redis内存不足时,使用Cluster进行分片存储。

Twemproxy 是什么?

Twemproxy 是Twitter 维护的(缓存)代理系统,代理Memcached 的ASCII 协议和Redis协议。它是单线程程序,使用c 语言编写,运行起来非常快。它是采用Apache 2.0 license的开源软件。

Twemproxy 支持自动分区,如果其代理的其中一个Redis节点不可用时,会自动将该节点排除(这将改变原来的keys-instances 的映射关系,所以你应该仅在把Redis 当缓存时使用Twemproxy)。

Twemproxy 本身不存在单点问题,因为你可以启动多个Twemproxy 实例,然后让你的客户端去连接任意一个Twemproxy 实例。

Twemproxy 是Redis 客户端和服务器端的一个中间层,由它来处理分区功能应该不算复杂,并且应该算比较可靠的。

支持一致性哈希的客户端有哪些?

Redis-rb、PRedis 等

Redis 有哪些适合的场景?

(1)、会话缓存(Session Cache)

最常用的一种使用Redis 的情景是会话缓存(session cache)。用Redis 缓存会话比其他存储(如Memcached)的优势在于:Redis 提供持久化。当维护一个不是严格要求一致性的缓存时,如果用户的购物车信息全部丢失,大部分人都会不高兴的

(2)、全页缓存(FPC)

除基本的会话token 之外,Redis 还提供很简便的FPC 平台。回到一致性问题,即使重启了Redis 实例,因为有磁盘的持久化,用户也不会看到页面加载速度的下降,这是一个极大改进,类似PHP 本地FPC。

(3)、队列

Reids 在内存存储引擎领域的一大优点是提供list 和set 操作,这使得Redis 能作为一个很好的消息队列平台来使用。Redis 作为队列使用的操作,就类似于本地程序语言(如Python)对list 的push/pop 操作。

(4)、排行榜/计数器

Redis 在内存中对数字进行递增或递减的操作实现的非常好。集合(Set)和有序集合(SortedSet)也使得我们在执行这些操作的时候变的非常简单,Redis 只是正好提供了这两种数据结构。

(5)、发布/订阅

最后(但肯定不是最不重要的)是Redis 的发布/订阅功能。发布/订阅的使用场景确实非常多。我已看见人们在社交网络连接中使用,还可作为基于发布/订阅的脚本触发器,甚至用Redis 的发布/订阅功能来建立聊天系统!

两类channel:普通channel,pattern channel.

Redis和Redisson有什么关系?

Redisson是一个基于Redis的Java客户端库,提供了对Redis的丰富功能的高级封装和抽象,使Java开发者可以更轻松地与Redis进行交互。Redisson提供了分布式数据结构,如分布式锁、分布式队列、分布式集合、分布式映射等,这些数据结构可以在分布式环境下安全地使用,而无需开发者手动管理同步和并发。此外,Redisson还可以与Spring、Spring Boot和其他常见Java框架和库集成,简化了在Java应用程序中使用Redis的流程。

Jedis与Redisson对比有什么优缺点?

JedisRedis的Java实现的客户端,其API提供了比较全面的Redis命令的支持;

Redisson实现了分布式和可扩展的Java数据结构,适合分布式开发,和Jedis相比,功能 较为简单,不支持字符串操作,不支持排序、事务、管道、分区等Redis 特性。

说说Redis哈希槽的概念?

Redis集群没有使用一致性hash,而是引入了哈希槽的概念,Redis集群有 16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置 哪个槽,集群的每个节点负责一部分hash槽。

Redis集群会有写操作丢失吗?为什么?

Redis并不能保证数据的强一致性,这意味这在实际中集群在特定的条件 下可能会丟失写操作。

redis是异步复制,当master宕机后,slave升为主的期间,会丢失部分数据,应该是可以做一些配置降低数据丢失的概率。

Redis Cluster 不保证强一致性,存在丢失数据的场景:

  • 异步复制

在 master 写成功,但 slave 同步完成之前,master 宕机了,slave 变为 master,数据丢失。

wait 命令可以给为同步复制,但也无法完全保证数据不丢,而且影响性能。

  • 网络分区

分区后一个 master 继续接收写请求,分区恢复后这个 master 可能会变为 slave,那么之前写入的数据就丢了。

可以设置节点过期时间,减少 master 在分区期间接收的写入数量,降低数据丢失的损失。

Redis集群如何选择数据库?

Redis集群目前无法做数据库选择,默认在0数据库。

Redis Cluster 的读写分离

redis cluster引入了READONLY命令,客户端向slave发送该命令后,slave对于读操作将不会再MOVED回master而是直接处理。这叫做slave的READONLY模式,通过READWRITE命令,可以将slave的readonly模式重置。

Redis Cluster单点保护

可以自动维持集群每个master都有一个slave节点。

3.0之前的Redis集群扩容问题

redis集群分为服务端集群和客户端分片,redis3.0以上版本实现了集群机制,即服务端集群,3.0以下使用客户端分片(Sharding)。redis3.0服务端集群使用哈希槽,计算key的CRC16结果再模16834。3.0以下版本采用Key的一致性hash算法来区分key存储在哪个Redis实例上。

Pre-Sharding的方式。即事先部署足够多的Redis服务,当容量不够时将多个实例拆分到不同的机器上,这样实际就达到了扩容的效果。

你知道有哪些Redis 分区实现方案?

客户端分区就是在客户端就已经决定数据会被存储到哪个Redis 节点或者从哪个Redis 节点读取。大多数客户端已经实现了客户端分区。

代理分区意味着客户端将请求发送给代理,然后代理决定去哪个节点写数据或者读数据。代理根据分区规则决定请求哪些Redis 实例,然后根据Redis 的响应结果返回给客户端。Redis 和memcached 的一种代理实现就是Twemproxy。

查询路由(Query routing) 的意思是客户端随机地请求任意一个Redis 实例,然后由Redis将请求转发给正确的Redis 节点。Redis Cluster 实现了一种混合形式的查询路由,但并不是直接将请求从一个Redis 节点转发到另一个Redis 节点,而是在客户端的帮助下直接redirected 到正确的Redis 节点。

Redis 分区有什么缺点?

涉及多个key 的操作通常不会被支持。例如你不能对两个集合求交集,因为他们可能被存储到不同的Redis 实例(实际上这种情况也有办法,但是不能直接使用交集指令)。

同时操作多个key,则不能使用Redis 事务.

Multi-Key(多 key)是什么意思?某些情况是多 key 的操作,例如:

  • SUNION,这类命令会操作多个 key —— 返回给定集合的并集

  • 事务,会在一个事务中操作多个 key

  • LUA 脚本,也会操作多个 key

分区使用的粒度是key,不能使用一个非常长的排序key 存储一个数据。

当使用分区的时候,数据处理会非常复杂,例如为了备份你必须从不同的Redis 实例和主机同时收集RDB / AOF 文件。

分区时动态扩容或缩容可能非常复杂。Redis 集群在运行时增加或者删除Redis 节点,能做到最大程度对用户透明地数据再平衡,但其他一些客户端分区或者代理分区方法则不支持这种特性。然而,有一种预分片的技术也可以较好的解决这个问题。

Redis cluster对多key操作有限,要求命令中所有的key都属于一个slot,才可以被执行。客户端可以对multi-key命令进行拆分,再发给redis。

Redis 持久化数据和缓存怎么做扩容?

如果Redis 被当做缓存使用,使用一致性哈希实现动态扩容缩容。

如果Redis 被当做一个持久化存储使用,必须使用固定的keys-to-nodes 映射关系,节点的数量一旦确定不能变化。否则的话(即Redis 节点需要动态变化的情况),必须使用可以在运行时进行数据再平衡的一套系统,而当前只有Redis 集群可以做到这样。

Redis事务相关的命令有哪几个?

MULTIEXECDISCARD、WATCH

可以在 multi 命令之前使用 watch 命令监控某些键值对,然后使用 multi 命令开启事务,执行各类对数据结构进行操作的命令,这个时候这些命令就会进入队列。

当 Redis 使用 exec 命令执行事务的时候,它首先会去比对被 watch 命令所监控的键值对,如果没有发生变化,那么它会执行事务队列中的命令,提交事务;如果发生变化,那么它不会执行任何事务中的命令,而去事务回滚。无论事务是否回滚,Redis 都会去取消执行事务前的 watch 命令

Redis key的过期时间和永久有效分别怎么设置?

EXPIREPERSIST 命令

都有哪些办法可以降低Redis 的内存使用情况呢?

如果你使用的是32 位的Redis 实例,可以好好利用Hash,list,sorted set,set 等集合类型数据,因为通常情况下很多小的Key-Value 可以用更紧凑的方式存放到一起。

尽可能使用散列表(hashes),散列表(是说散列表里面存储的数少)使用的内存非常小,所以你应该尽可能的将你的数据模型抽象到一个散列表里面。

比如你的web系统中有一个用户对象,不要为这个用户的名称,姓氏,邮箱,密码设置单独的key,而是应该把这个用户的所有信息存储到一张散列表里面。

最后更新于