《Redis 深度历险:核心原理与应用实践》学习

1、Redis的5种基础数据结构

Redis的5种基础数据结构:string (字符串)、list (列表 )、hash (字典)、 set (集合)、zset (有序集合)。

Redis所有的数据结构都以唯一的key字符串作为名称, 然后通过这个唯一key值来获取相应的value数据。

1.1 string (字符串)

Redis字符串是可以修改的字符串,在内存中是以字节数组的形式存在,字符串最大长度为512MB。

Redis字符串的结构叫“SDS”(Simple Dynamic String),是一个带长度信息的字节数组。

struct SDS<T> {
	T capacity; // 数组容量,表示所分配的数组长度
	T len; // 内容的实际长度
	byte flags; // 特殊标志位,不用理睬它
	byte[] content; // 数组的内容
}

在这里插入图片描述

1.2 list (列表)

Redis列表的内存结构:在列表元素较少的时候,所有的元素彼此紧挨着—起存储,分配的是一块连续的内存,这个结构是“压缩列表”(ziplist)。
在这里插入图片描述
当列表元素较多的时候,会改成“快速链表”(quicklist)。因为普通的链表需要的附加指针空间太大,会浪费空间,还会加重内存的碎片化,比如某普通链表里存的只是int类型的数据,结构上还需要两个额外的指针prev和next。所以Redis将多个ziplist使用双向指针串起来使用,既满足了快速的插入删除性能, 又不会出现太大的空间冗余。
在这里插入图片描述
Redis列表插入和删除操作非常快,时间复杂度为O(1),但是索引定位很慢,时间复杂度为O(n)。

1.3 hash(字典)

Redis字典类似于Java语言里面的 HashMap,它是无序字典,内部存储了很多键值对,结构为 “数组+链表” 。

Redis字典的值只能是字符串。

在这里插入图片描述

1.4 set (集合)

Redis集合相当于Java 语言里面的 HasbSet,它内部的键值对是无序的、唯一的。它的内部实现相当于一个特殊的字典,字典中所有的value都是一个值NULL。

1.5 zset (有序列表)

Redis有序列表的内部实现用的是 “ 跳跃列表” ,一方面它是一个 set,保证了内部 value 的唯一性,另一方面它可以给每个 value 赋予一个 score,代表这个 value 的排序权重。
在这里插入图片描述

1.6

list、set、hash、zset 这四种数据结构是容器型数据结构,它们共享下面两条通用规则:

  1. create if not exists:如果容器不存在,那就创建一个,再进行操作。比如rpush操作刚开始是没有列表的,Redis就会自动创建一个,然后再rpush进去新元素。
  2. drop if no elements:如果容器里的元素没有了,那么立即删除容器,释放内存。

Redis所有的数据结构都可以设置过期时间, 时间到了,Redis会自动删除相应的对象。


2、Redis原理

2.1 高性能

  • Redis是基于内存的数据库,所有数据都在内存中,内存的读写速度远高于硬盘。
  • Redis是单线程操作,避免了不必要的上下文切换和竞争锁机制,也不存在线程频繁切换导致CPU的消耗,所以Redis的操作速度非常快。Redis6.0引入了多线程,但执行请求命令的仍然是单线程。
  • Redis采用了非阻塞I/O和多路复用(事件轮询)。非阻塞I/O意味着线程在读写I/O时可以不再阻塞,读写可以瞬间完成。多路复用是利用epoll(linux)、kqueue(FreeBSD和macosx)可以同时监听多个流的I/O事件的能力,在空闲的时候,会把当前网络线程阻塞掉,当某些网线程有I/O事件时,就从阻塞态中唤醒,按照轮询的顺序Redis依次进行处理,这样就可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗),所以Redis具有很高的吞吐量。此处“多路”指的是多个网络请求,“复用”指的是复用同一个线程。

2.2 Redis的持久化机制

2.2.1 快照

  • 快照持久化是由Redis的一个子进程来处理,子进程对数据(子进程产生时那一瞬间的数据)进行遍历读取,然后序列化写到磁盘中。
  • 快照是一次全量备份,是内存数据的二进制序列化形式。

2.2.2 AOF日志

  • AOF日志是Redis服务器的顺序指令序列,AOF日志只记录对内存进行修改的指令记录。
  • Redis会在收到客户端修改指令后,进行参数校验、逻辑处理,如果没问题,就立即将该指令文本存储到AOF日志中,也就是说,先执行指令才将日志存盘。
  • 如果机器突然宫机,AOF日志内容可能还没有来得及完全刷到磁盘中,这个时候就会出现日志丢失。
  • AOF日志在长期的运行过程中会变得无比庞大,数据库重启时需要加载AOF日志进行指令重放,这个时间就会无比漫长。

2.2.3 混合持久化

  • 重启Redis时,如果用快照恢复内存状态,会丢失大量数据,如果用AOF日志重放,会非常慢。
  • 混合持久化(Redis4.0之后提供)是将快照内容和增量的AOF日志文件存在一起,在Redis重启的时候,可以先加载快照的内容,然后再重放增量的AOF日志,重启效率得到大幅提升。

3、Redis集群

3.1 主从复制

在这里插入图片描述
主从复制模式是指将一台Redis服务器作为主节点,一台或多台Redis服务器作为从节点。主节点支持数据的写入和读取等各项操作,而从节点只支持与主节点数据同步和数据读取操作。数据是异步同步,并且数据的复制是单向的,只能由主节点到从节点。

优点:
主节点能自动同步数据到从节点,实现读写分离,提高了整体性能;
主节点、从节点之间的同步是以非阻塞的方式进行的,同步期间,任能为客户端提供服务。

缺点:
不具备自动容错能力,主节点宕机会导致部分数据未同步到从节点,造成数据不一致;
不具备自动恢复能力,主节点或从节点宕机都需要人工手动介入恢复;
难以支持在线扩容,Redis的容量受限于单机配置;

其中,主从数据同步分为了两个阶段,一个是快照同步(即全量同步),一个是增量同步。

3.1.1 快照同步

快照同步首先需要主节点将当前内存的数据全部快照到磁盘文件中,然后再将快照文件的内容全部传送到从节点。从节点将快照文件接受完毕后,立即执行一次全量加载。加载之前先要将当前内存的数据清空,加载完毕后通知主节点继续进行增量同步。在整个快照同步进行的过程中,主节点的复制buffer还在不停地往前移动,如果快照同步的时间过长或者复制buffer太小,都会导致同步期间的增量指令在复制buffer中被覆盖,这样就会导致快照同步完成后无法进行增最复制,然后会再次发起快照同步,如此极有可能会陷入快照同步的死循环,所以需要配置一个合适的复制buffer大小参数。

3.1.2 增量同步

Redis增量同步的是指令流,主节点会将那些对自己的状态产生修改性影响的指令记录在本地的内存buffer中,然后异步将buffer中的指令同步到从节点,从节点一边执行同步的指令流来达到和主节点一样的状态,一边向主节点反馈自己同步到哪里了(偏移量)。因为内存的buffer是有限的,所以Redis主节点不能将所有的指令都记录在内存buffer中。Redis的复制内存buffer是—个定长的环形数组,如果数组内容满了,就会从头开始覆盖前面的内容。如果因为网络状况不好,从节点在短时间内无法和主节点进行同步,那么当网络状况恢复时,Redis的主节点中那些没有同步的指令在buffer中有可能已经被后续的指令覆盖掉了,从节点将无法直接通过指令流来进行同步。

3.1.2 快照同步

快照同步首先需要在主节点上将当前内存的数据全部快照到磁盘文件中,然后再将快照文件的内容全部传送到从节点。从节点将快照文件接受完毕后,立即执行一次全量加载。加载之前先要将当前内存的数据清空,加载完毕后通知主节点继续进行增量同步。在整个快照同步进行的过程中,主节点的复制buffer还在不停地往前移动,如果快照同步的时间过长或者复制buffer太小,都会导致同步期间的增量指令在复制buffer中被覆盖,这样就会导致快照同步完成后无法进行增最复制,然后会再次发起快照同步,如此极有可能会陷入快照同步的死循环。所以需要配置一个合适的复制buffer大小参数,避免快照复制的死循环。

3.2 Redis Sentinel(Redis哨兵)

在这里插入图片描述
Sentinel负责持续监控主从节点的健康,当主节点挂掉时,自动选择—个最优的从节点切换成为主节点。客户端来连接集群时, 会首先连接Sentinel,通过Sentinel来查询主节点的地址,然后再连接主节点进行数据交互。当主节点发生故障时, 客户端会重新向Sentinel要地址,Sentinel会将最新的主节点地址告诉客户端。如此应用程序将无须重启即可自动完成节点切换。

优点:
哨兵模式基于主从复制模式,所以主从复制模式有的优点,哨兵模式也有。
哨兵模式下,主节点宕机可以自动进行切换,更高了系统可用性。

缺点:
不具备自动容错能力,主节点宕机会导致部分数据未同步到从节点,造成数据不一致;
难以支持在线扩容,Redis的容量受限于单机配置;
需要额外的Sentinel资源,实现相对复杂一些。同时slave节点只作为备份节点不提供服务。

Redis主从采用异步复制,意味着当主节点挂掉时,从节点可能没有收到全部的同步消息,这部分未同步的消息就丢失了。
如果主从延迟特别大,那么丢失的数据就可能会特别多。Sentinel无法保证消息完全不丢失,但是也能尽量保证消息少丢失。
下面两个选项可以限制主从延迟过大:

min-slaves-to-write 1
min-slaves-max-lag 10

第一个参数表示主节点必须至少有一个从节点在进行正常复制,否则就停止对外写服务,丧失可用性。
第二个参数控制是否为正常复制,它的单位是秒(s),表示如果在10s内没有收到从节点的反馈,就意味着从节点同步不正常,要么是网络断开了,要么是一直没有给反馈。

3.3 Redis Cluster

在这里插入图片描述
Redis Cluster是Redis的“亲儿子”,它是Redis作者自己提供的Redis集群化方案,它去中心化的。

Redis Cluster将所有数据划分为16384个槽位,集群的每个节点负责其中一部分槽位,槽位的信息存储于每个节点中,当Redis Cluster的客户端来连接集群时,也会得到一份集群的槽位配置信息。这样当客户端要查找某个key时,可以直接定位到目标节点。

Redis Cluster可以为每个主节点设置若干个从节点, 当主节点发生故陓时,集群会自动将其中某个从节点提升为主节点。如果某个主节点没有从节点,那么当它发生故障时,集群将完全处千不可用状态。

Redis Cluster是去中心化的,一个节点认为某个节点失联了并不代表所有的节点都认为它失联了,所以集群还得经过一次协商的过程,只有当大多数节点都认定某个节点失联了,集群才认为该节点需要进行主从切换来容错。

Redis集群节点采用Gossip协议来广播自己的状态以及改变对整个集群的认知。
比如一个节点发现某个节点失联了(PFail即Possibly Fail),它会将这条信息向整个集群广播,其他节点就可以收到这点的失联信息。如果收到了某个节点失联的节点数量(PFail Count)已经达到了集群的大多数,就可以标记该失联节点为确定下线状态(Fail),然后向整个集群广播,强迫其他节点也接受该节点已经下线的事实,并立即对该失联节点进行主从切换。

优点:
无中心架构,数据按照slot分布在多个节点。
集群中的每个节点都是平等的关系,每个节点都保存各自的数据和整个集群的状态。每个节点都和其他所有节点连接,而且这些连接保持活跃,这样就保证了我们只需要连接集群中的任意一个节点,就可以获取到其他节点的数据。
在线扩容简单,节点可动态添加或删除。
能够实现自动故障转移,节点之间通过Gossip协议交换状态信息,用投票机制完成slave到master的角色转换。

缺点:
数据通过异步复制,不保证数据的强一致性。
主节点提供读写操作,从节点只作为备份节点不提供服务,只作为故障转移使用,因此不能缓解读压力。
批量操作限制,目前只支持具有相同slot值的key执行批量操作。
不支持多数据库空间。单机下Redis支持16个数据库,集群模式下只能使用一个数据库空间,即db0。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值