Redis 为什么响应快
1. 数据保存在内存中
Redis 数据保存在内存中,读写操作只要访问内存,不需要磁盘 IO。
2. 底层数据结构
如下:
Redis 的数据以 key:value 的格式存储在散列表中,时间复杂度 o(1)。
Redis 为 value 定义了丰富的数据结构,包括动态字符串、双向链表、压缩列表、hash、跳表和整数数组,可以根据 value 的特性选择选择最高效的数据结构。
3. 单线程模型
Redis 的网络 IO 和数据读写使用单线程模型,可以绑定 CPU,这避免了线程上下文切换带来的开销。
注意:Redis 6.0 对网络请求引入了多线程模型,读写操作还是用单线程。
Redis 底层数据结构
Redis 有 5 种数据类型,包括:字符串、列表、集合、有序集合和字典。
Redis 底层的数据结构有 6 种,包括:动态字符串、双向链表、压缩列表(ziplist)、hash 表、跳表(skip list)和整数数组。
1. 字符串类型
底层数据结构是动态字符串。
2. 列表
如果同时满足下面条件,就使用压缩列表,否则使用双向链表:
列表中单个元素小于 64 字节
列表中元素个数少于 512
压缩列表在内存中是一块儿连续的内存空间,
压缩列表查找时间复杂度是 o(n)。
3. 集合
如果同时满足下面条件,就使用有序整数数组,否则使用 hash 表:
集合中元素都是整数类型
集合中元素个数不超过 512 个
4. 有序集合
如果同时满足下面 2 个条件,就使用压缩列表,否则使用跳表:
集合中元素都小于 64 字节
集合中元素个数小于 128 个
注意:有序集合还有一个 HASH 表用于保存集合中元素的分数,做 ZSCORE 操作时,查询的就是这个 HASH 表,所以效率很高。如果不加索引,查找 10 这个数字需要查询 10 次,使用了二级索引,查找 10 这个数字需要 5 次,而使用一级索引,需要查询 3 次。跳表的每一层都是一个有序链表,最下面一层保存了全部数据。跳表插入、删除、查询的时间复杂度是 o(logN)。跳表需要存储额外的索引节点,会增加额外的空间开销。
5. 字典
如果同时满足下面 2 个条件,就使用压缩列表,否则使用 hash 表:
字典中每个 entry 的 key/value 都小于 64 字节
字典中元素个数小于 512 个
Redis 缓存淘汰策略,Redis 总共有 8 种淘汰策略,如下图:
volatile-lfu 和 allkeys-lfu 策略是 4.0 版本新增的:
lru:是按照数据的最近最少访问原则来淘汰数据,可能存在的问题是如果大批量冷数据最近被访问了一次,就会占用大量内存空间,如果缓存满了,部分热数据就会被淘汰掉。
lfu:是按照数据的最小访问频率访问次数原则来淘汰数据,如果两个数据的访问次数相同,则把访问时间较早的数据淘汰。
Redis 数据持久化
Redis 持久化的方式有 2 种,一种是写后日志(AOF),一种是内存快照(RDB)。
1. AOF 日志
AOF 日志记录了每一条收到的命令,Redis 故障宕机恢复时,可以加载 AOF 日志中的命令进行重放来进行故障恢复。
AOF 有 3 种同步策略:
Always:执行命令同步写盘;
everysec:每秒写一次盘;
no:操作系统控制写盘
如果不是对丢失数据特别敏感的业务,推荐使用 everysec,对主线程的阻塞少,故障后丢失数据只有 1s。
2. RDB 快照
RDB 快照是一个内存快照,记录了 Redis 某一时刻的全部数据。
3. 混合日志
从 Redis 4.0 开始,AOF 文件也可以保存 RDB 快照,AOF 重写的时候 Redis 会把 AOF 文件内容清空,先记录一份 RDB 快照,这份数据以"REDIS"开头。
记录 RDB 内容后,AOF 文件会接着记录 AOF 命令。故障恢复时,先加载 AOF 文件中 RDB 快照,然后回放 AOF 文件中后面的命令。
4. 主从同步
Redis 主从同步时,主节点会先生成一份 RDB 快照发送给从节点,把快照之后的命令写入主从同步缓存区(replication buffer),从节点把 RDB 文件加载完成后,主节点把缓存区命令发送给从节点。
5. AOF 重写
AOF 日志是用记录命令的方式追加的,这样可能存在对同一个 key 的多条命令,这些命令是可以合并成 1 条的。比如对同一个 key 的多个 set 操作日志,可以合成一条。
6. 阻塞
AOF 重写和 RDB 快照执行的过程中,Redis 都会 Fork 一个子进程来执行操作,子进程执行过程中是不是阻塞主线程的。
但是要注意 2 点:
Fork 子进程的过程中,Redis 主线程会拷贝一份内存页表(记录了虚拟内存和物理内存的映射关系)给子进程,这个过程是阻塞的,Redis 主线程内存越大,阻塞时间越长。
子进程和 Redis 主线程共用一块儿物理内存,如果新的请求到来,必须使用 copy on write 的方式,拷贝要修改的数据页到新的内存空间进行修改。
注意:如果开启了内存大页,每次拷贝都需要分配 2MB 的内存。
Redis 高可用
从图我们可以看到哨兵之间、哨兵和主从节点之间、哨兵和客户端之间都建立了连接。
如果主节点挂了,哨兵集群需要完成主从切换:判断主节点下线--->选举新主---->选举哨兵leader---->执行主从切换:
1. 判断主节点下线
当一个哨兵监控到主节点下线时,就会给其他哨兵发送确认命令,其他命令会根据自己的判断回复"Y"或"N"。
如果有 n/2+1 以上数量的哨兵都认为主节点下线了,才会判定主节点下线。这里的n是哨兵集群的数量。
n/2+1 这个参数由 quorum 参数配置,比如有 5 个哨兵,这里一般配置成 3。也可以配置成其他值。
2. 选举新主节点
主节点被判定下线后,哨兵集群会重新选择新的主节点。
淘汰不稳定从节点:根据配置参数 down-after-milliseconds * 10 来淘汰。
down-after-milliseconds 表示主从节点断开时间,10 表示次数,如果从节点跟主节点断开时间超过 down-after-milliseconds 的次数达到了 10 次以上,从节点就被淘汰了。
slave-priority 参数:配置了从节点的优先级,选择从节点时哨兵会优先选择优先级高的从节点。
复制进度:Redis 有一个记录主从增量复制的缓存区叫 repl_backlog_buffer。
主节点有一个写偏移量 master_repl_offset,从节点也有一个偏移量 slave_repl_offset。
优先选择 slave_repl_offset 最接近 master_repl_offset 的从节点作为新的主节点。
所以,上图中偏移量为 114 的从节点优先被选为新的主节点。
ID 编号:优先级和参数都一样的情况下,ID 编号小的从节点优先被选为新主节点。
3. 选举哨兵 Leader
第一个判断主节点下线的哨兵节点收到其他节点的回复并确定主节点下线后,就会给其他哨兵发送命令申请成为哨兵 Leader。
成为 Leader 的条件如下:
收到赞成票必须大于等 quorum 值
必须拿到半数以上的赞成票
如果集群配置了 5 个哨兵,quorum 的值设置为 3,其中一个哨兵节点挂了,很有可能会判断到主节点下线,但是因为选举不出哨兵 Leader 而不能切换。
如果集群有 2 个哨兵,其中一个挂了,那必定选不出哨兵 Leader。
4. 主节点切换
选出新主节点和哨兵 Leader 后,哨兵 Leader 会执行主从切换的操作。
完成后会做一些事件通知:
通知其他哨兵新主节点地址
通知所有从节点新的主节点地址,从节点收到后向新主节点请求主从同步
通知客户端连接新主节点
5. 主从切换过程中请求处理
如果客户端的读请求会发送到从节点,可以正常处理。在客户端收到新主节点地址通知前写请求会失败。客户端可以采取一些应急措施应对主节点下线,比如缓存写请求。
为了能够及时获取到新主节点信息,客户端可以订阅哨兵的主节点下线事件和新主节点变更事件。
Redis 实现分布式锁
1. Redis 单节点的分布式锁
一个服务部署了 2 个客户端,获取分布式锁时一个成功,另一个就失败了。
Redis 一般使用 setnx 实现分布式锁,命令如下:
SETNX KEY_NAME VALUE
设置成功返回 1,设置失败返回 0。使用单节点分布式锁存在一些问题。
客户端 1 获取锁后发生了故障:结果锁就不能释放了,其他客户端永远获取不到锁。
解决方法是用下面命令对 key 设置过期时间:
SET key value [EX seconds] [PX milliseconds] NX
客户端 2 误删除了锁:解决方法是对 key 设置 value 时加入一个客户端表示,比如在客户端 1 设置 key 时在 value 前拼接一个字符串 application1,删除的时候做一下判断。
2. Redis 红锁
Redis 单节点会有可靠性问题,节点故障后锁操作就会失败。Redis 为了应对单点故障的问题,设计了多节点的分布式锁,也叫红锁。
主要思想是客户端跟多个 Redis 实例请求加锁,只有超过半数的实例加锁成功,才认为成功获取了分布式锁。
更多精彩干货分享
点击下方名片关注
IT那活儿
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/129777.html
摘要:哨兵是社区版本推出的原生高可用解决方案,部署架构主要包括两部分集群和数据集群,其中集群是由若干节点组成的分布式集群。自研推荐推荐自研的高可用解决方案,主要体现在配置中心故障探测和的处理机制上,通常需要根据企业业务的实际线上环境来定制化。 最近很多朋友向我咨询关于高可用的方案的优缺点以及如何选择合适的方案线上使用,刚好最近在给宜人贷,光大银行做企业内训的时候也详细讲过,这里我再整理发出来...
摘要:哨兵是社区版本推出的原生高可用解决方案,部署架构主要包括两部分集群和数据集群,其中集群是由若干节点组成的分布式集群。自研推荐推荐自研的高可用解决方案,主要体现在配置中心故障探测和的处理机制上,通常需要根据企业业务的实际线上环境来定制化。 最近很多朋友向我咨询关于高可用的方案的优缺点以及如何选择合适的方案线上使用,刚好最近在给宜人贷,光大银行做企业内训的时候也详细讲过,这里我再整理发出来...
摘要:而今天主要讲用得比较多的三个。支持持久化操作,可以进行及数据持久化到磁盘,从而进行数据备份或数据恢复等操作,较好的防止数据丢失的手段。单线程请求,所有命令串行执行,并发情况下不需要考虑数据一致性问题。 前言 NoSQL,泛指非关系型的数据库。随着互联网不断的发展,传统的关系数据库在应付新互联网模式的网站,特别是超大规模和高并发的SNS类型的纯动态网站已经显得力不从心,暴露了很多难以克服...
摘要:而今天主要讲用得比较多的三个。支持持久化操作,可以进行及数据持久化到磁盘,从而进行数据备份或数据恢复等操作,较好的防止数据丢失的手段。单线程请求,所有命令串行执行,并发情况下不需要考虑数据一致性问题。 前言 NoSQL,泛指非关系型的数据库。随着互联网不断的发展,传统的关系数据库在应付新互联网模式的网站,特别是超大规模和高并发的SNS类型的纯动态网站已经显得力不从心,暴露了很多难以克服...
摘要:而今天主要讲用得比较多的三个。支持持久化操作,可以进行及数据持久化到磁盘,从而进行数据备份或数据恢复等操作,较好的防止数据丢失的手段。单线程请求,所有命令串行执行,并发情况下不需要考虑数据一致性问题。 前言 NoSQL,泛指非关系型的数据库。随着互联网不断的发展,传统的关系数据库在应付新互联网模式的网站,特别是超大规模和高并发的SNS类型的纯动态网站已经显得力不从心,暴露了很多难以克服...
阅读 1250·2023-01-11 13:20
阅读 1559·2023-01-11 13:20
阅读 1013·2023-01-11 13:20
阅读 1680·2023-01-11 13:20
阅读 3972·2023-01-11 13:20
阅读 2520·2023-01-11 13:20
阅读 1356·2023-01-11 13:20
阅读 3486·2023-01-11 13:20