缓存
Redis 是速度非常快的非关系型(NoSQL)内存键值数据库:
绝大部分请求是纯粹的内存操作
采用单线程,避免了不必要的上下文切换和竞争条件
非阻塞IO
1. 数据类型
常见数据类型
数据类型
可以存储的值
STRING
字符串、整数或者浮点数
LIST
列表
SET
无序集合
ZSET
有序集合
HASH
包含键值对的无序散列表
存储结构
Redis底层数据结构包括:简单动态字符串、链表、字典、跳跃表、整数集合、压缩列表、对象。
字典
Reids的字典采用链地址法来处理冲突。考虑到扩容时大量数据迁移带来的CPU繁忙(可能导致一段时间内停止服务),所以Redis采用了渐进式rehash的方案:
Redis 的字典 dict 中包含两个哈希表 dictht,一个为数据表,一个为空表。每次对字典增删改查时,将数据表 dictht 上的键值对 rehash 到另一个 dictht 上面,完成之后释放空间并交换两个 dictht 的角色。Redis 采用分而治之的思想,将庞大的数据迁移工作平摊到每一次查询操作中,避免了服务中断。
跳跃表
跳跃表(SkipList)是有序集合(ZSet)的底层实现之一,是基于多指针有序链表实现的,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。查找、删除、添加等操作时间复杂度为O(logn).

在查找时,从上层指针开始查找,找到对应的区间之后再到下一层去查找。下图演示了查找 22 的过程。

与红黑树等平衡树相比,跳跃表具有以下优点:
支持范围查询
简单,容易实现
支持无锁操作
2. Redis与Memcache区别
Redis
Memcache
数据类型
支持五种不同的数据类型
仅支持字符串类型
数据持久化
支持RDB 和 AOF 两种持久化策略
不支持
分布式
支持主从和分片
不支持
3. pipeline 与 mget 比较
pipeline
一般情况下,Redis Client端发出一个请求后,通常会阻塞并等待Redis Server处理,Redis Server处理完后请求命令后会将结果通过响应报文返回给Redis Client. 当要连续执行多个命令时,主要的时延在于 RTT (Round-Trip Time),而且还要频繁调用系统 I / O 接口。
使用 Pipeline 可以将多个命令一次性写入到服务器,然后等待服务器返回结果。Pipeline可以同时执行多个命令,但是各个命令之间不能有数据依赖,因为 Pipeline 执行命令时不能保证执行顺序与写入时的命令顺序相同。
mget
mget 和 mset 也是为了减少网络连接和传输时间设置的,当 key 的数目较多时, mget 和 mset 性能要高于 pipeline,但是 mget 和 mset 也仅限与同时对多个 key 进行操作。
4. 持久化机制
Redis 是内存型数据库,为了保证数据在断电后不会丢失,需要将内存中的数据持久化到硬盘上。
RDB (Redis Database Backup)
保存某个时间点的全量数据快照
如果系统发生故障,将会丢失最后一次创建快照之后的数据
如果数据量很大,保存快照的时间会很长
两种数据保存方式
SAVE:阻塞Redis服务器进程,直到RDB文件创建完毕
BGSAVE:Fork一个子进程来创建RDB文件,不阻塞服务器进程
AOF (Append Only Files)
将写命令添加到 AOF 文件的末尾
可以使用日志重写解决AOF文件不断增大的问题
使用 AOF 持久化需要设置同步选项,从而确定写命令同步到磁盘文件上的时机。有以下同步选项:
选项
同步频率
解释
always
每个写命令都同步
会严重减低服务器的性能
everysec
每秒同步一次
比较合适,可以保证系统崩溃时只会丢失一秒左右的数据,并且 Redis 每秒执行一次同步对服务器性能几乎没有任何影响
no
让操作系统来决定何时同步
不能给服务器性能带来多大的提升,而且也会增加系统崩溃时数据丢失的数量
RDB与AOF比较
RDB
AOF
优点
全量数据快照,文件小,恢复快
可读性好,适合保存增量数据,数据不易丢失
缺点
无法保存最近一次快照之后的数据
文件体积大,恢复时间长
5. 数据过期策略
LRU
最近最少使用策略 (Least Recently Used),优先淘汰最久未使用的数据,也就是上次被访问时间距离现在最久的数据。该策略可以保证内存中的数据都是热点数据,也就是经常被访问的数据,从而保证缓存命中率。
假设内存只能容纳3个页,内存按照栈的方式来描述访问时间,最上面的是最近访问的,最下面的最远时间访问的。 如果按照 7 0 1 2 0 3 0 4 的次序访问页:

以下是基于 双向链表 + HashMap 的 LRU 算法实现,对算法的解释如下:
访问某个节点时,将其从原来的位置删除,并重新插入到链表头部,这样就能保证链表尾部存储的就是最近最久未使用的节点
当节点数量大于缓存最大空间时就淘汰链表尾部的节点
为了使删除操作时间复杂度为 O(1),使用HashMap 存储 Key 到节点的映射,通过 Key 就能以 O(1) 的时间得到节点,然后再以 O(1) 的时间将其从双向链表中删除

LFU
最不经常使用策略(Least Frequently Used),优先淘汰一段时间内使用次数最少的数据。
Redis 的数据过期策略
策略
描述
volatile-lru
从已设置过期时间的数据集中挑选最近最少使用的数据淘汰
volatile-lfu
从已设置过期时间的数据集中驱逐使用频率最少的键
volatile-ttl
从已设置过期时间的数据集中挑选将要过期的数据淘汰
volatile-random
从已设置过期时间的数据集中任意选择数据淘汰
allkeys-lru
从所有数据集中挑选最近最少使用的数据淘汰
allkeys-lfu
从所有键中驱逐使用频率最少的键
allkeys-random
从所有数据集中任意选择数据进行淘汰
noeviction
禁止驱逐数据
6. 应用场景
分布式锁
在单机场景下,可以使用语言的内置锁来实现进程同步。但是在分布式场景下,需要同步的进程可能位于不同的节点上,那么就需要使用分布式锁。
可以使用带NX参数的SET指令实现:
EX seconds: 设定过期时间,单位为秒
PX milliseconds: 设定过期时间,单位为毫秒
NX: 仅当key不存在时设置值
XX: 仅当key存在时设置值
消息队列
使用List作为队列,可以通过 lpush 和 rpop 写入和读取消息。
7. 缓存常见问题
缓存穿透
原因
解决方案
请求不存在的数据,该请求会穿透缓存到达数据库
对这些不存在的数据缓存一个空数据
对这类请求进行过滤
缓存雪崩
在有缓存的系统中,系统非常依赖于缓存,缓存分担了很大一部分的数据请求。当发生缓存雪崩时,数据库无法处理这么大的请求,导致数据库崩溃。
原因
解决方案
系统刚启动,数据未加载到缓存中
进行缓存预热
缓存在同一时间大面积过期
合理设置缓存过期时间
缓存服务器宕机
使用分布式缓存
缓存一致性
缓存一致性要求数据更新的同时缓存数据也能够实时更新。
解决方案:
在数据更新的同时立即去更新缓存
在读缓存之前先判断缓存是否是最新的,如果不是最新的先进行更新
要保证缓存一致性需要付出很大的代价,缓存数据最好是那些对一致性要求不高的数据,允许缓存数据存在一些脏数据。
8. 高可用策略
主从同步
可以使用 slaveof host port 命令来让一个服务器成为另一个服务器的从服务器。
随着负载不断上升,主服务器可能无法很快地更新所有从服务器,为了解决这个问题,可以创建一个中间层来分担主服务器的复制工作。

全同步过程
Master创建快照文件,发送给Slave,并在发送期间使用缓冲区记录执行的写命令。快照文件发送完毕之后,Master开始向Slave发送存储在缓冲区中的写命令
Slave丢弃所有旧数据,载入Master发来的快照文件
Master将这期间的增量写命令发送给Slave
增量同步过程
Master接收到用户的操作指令(增删改查),判断是否需要传播到Slave
Master将增删改的操作指令传播到其他Slave:
对齐主从库
Master往响应缓存中写入指令,将缓存中的数据发送给Slave
Redis Sentinel
主从模式并不具备高可用性,当主节点出现问题,需要通过人工干预的方式把从节点设为主节点,还要通知应用程序更新主节点地址,这种方式非常繁琐,Redis引入了哨兵机制来解决这个问题:
监控:哨兵(sentinel) 会不断地检查你的Master和Slave是否运作正常
提醒:当被监控的某个 Redis出现问题时, 哨兵(sentinel) 可以通过 API 向管理员或者其他应用程序发送通知
自动故障迁移:当主数据库出现故障时自动将从数据库转换为主数据库

分片
分片是指按照某种规则去划分数据,分散存储在多个节点上,降低单节点服务器的压力。
哈希分布
将数据计算哈希值之后,按照哈希值分配到不同的节点上。例如有 N 个节点,数据的主键为 key,则将该数据分配的节点序号为:hash(key) % N.
缺陷:当节点数量变化时,几乎所有的数据都需要重新分布,将导致大量的数据迁移。
一致性哈希
将哈希空间 [0, 2^32 - 1] 看成一个哈希环,每个服务器节点都配置到哈希环上。每个数据对象通过哈希取模得到哈希值之后,存放到哈希环中顺时针方向第一个大于等于该哈希值的节点上。

节点宕机
在一致性哈希算法中,如果一台服务器不可用,则受影响的数据仅仅是此服务器到其环空间中前一台服务器(即沿着逆时针方向行走遇到的第一台服务器)之间数据。例如中节点C宕机,此时节点A、B、D不会受影响,只节点有C的数据被重定位到Node D。
新增节点
一致性哈希在增加或者删除节点时只会影响到哈希环中相邻的节点,例如下图中新增节点 X,只需要将它前一个节点 C 上的数据重新进行分布即可,对于节点 A、B、D 都没有影响。

数据倾斜
一致性哈希存在数据分布不均匀的问题,在节点数量很少且分布不均的情况下,各节点存储的数据量可能有很大的不同。
解决方式是通过增加虚拟节点,然后将虚拟节点映射到真实节点上。虚拟节点的数量比真实节点来得多,那么虚拟节点在哈希环上分布的均匀性就会比原来的真实节点好,从而使得数据分布也更加均匀。

最后更新于
这有帮助吗?