Redis深度历险:核心原理和应用实践(三)

1.线程IO模型

Redis 是个单线程程序。

Redis所有的数据都在内存中,所有的运算都是内存级别的运算。对于那些时间复杂度为 O(n) 级别的指令,一定要谨慎使用,避免 Redis 卡顿。

1.1 非阻塞IO

非阻塞 IO 在套接字对象上提供了一个选项 Non_Blocking,当这个选项打开时,读写方 法不会阻塞,而是能读多少读多少,能写多少写多少。能读多少取决于内核为套接字分配的 读缓冲区内部的数据字节数,能写多少取决于内核为套接字分配的写缓冲区的空闲空间字节 数。读方法和写方法都会通过返回值来告知程序实际读写了多少字节。

1.2 事件轮询(多路复用)

最简单的事件轮询 API 是 select 函数,它是 操作系统提供给用户程序的 API。

输入是读写描述符列表 read_fds & write_fds,输出是与之对应的可读可写事件。同时还提供了一个 timeout 参数,如果没有任何事件到来,那么就最多等待 timeout 时间,线程处于阻塞状态。一旦期间有任何事件到来,就可以立即返回。时间过 了之后还是没有任何事件到来,也会立即返回。拿到事件后,线程就可以继续挨个处理相应 的事件。处理完了继续过来轮询。

因为通过 select 系统调用同时处理多个通道描述符的读写事件,因此将这类系统调用称为多路复用 API。

现在不用select,改用 epoll(linux)和 kqueue(freebsd & macosx)。

事件轮询 API 就是 Java 语言里面的 NIO 技术。

1.3 指令队列:Redis 会将每个客户端套接字都关联一个指令队列。客户端的指令通过队列来排队进行顺序处理,先到先服务。

1.4响应队列:Redis 为每个客户端套接字关联一个响应队列。Redis 服务器通过响应队列来将指令的返回结果回复给客户端。

1.5 定时任务:Redis 的定时任务会记录在一个最小堆数据结构中。这个堆中,最快要执行的任务排在堆的最上方。在每个循环周期,Redis 都会将最小堆里面已经到点的任务立即进行处理。处理完毕后,将最快要执行的任务还需要的时间记录下来,这个时间就是 select 系统调用的 timeout 参数。因为 Redis 知道未来 timeout 时间内,没有其它定时任务需要处理,所以可以安心睡眠 timeout 的时间。

2. 通信协议

RESP(Redis Serialization Protocol)是 Redis 序列化协议。它是一种直观的文本协议,实现异常简单,解析性能极好。

客户端向服务器发送的指令只有一种格式,多行字符串数组。

服务器向客户端回复的响应要支持多种数据结构(单行字符串,多行字符串,整数值,错误消息,数组)

3.持久化

Redis 的持久化机制保证Redis存在内存里的数据不丢失。

(1)快照,一次全量备份。内存数据的二进制序列化形式,在存储上非常紧凑。

Redis 使用操作系统的多进程 COW(Copy On Write) 机制来实现快照持久化。

Redis 在持久化时会调用 glibc 的函数 fork 产生一个子进程,快照持久化完全交给子进程来处理,父进程继续处理客户端请求。

子进程做数据持久化,不会修改现有的内存数据结构,只是对数据结构进行遍历读取,然后序列化写到磁盘中。父进程持续服务客户端请求,对内存数据结构进行不间断的修改。

(2)AOF日志,连续的增量备份。记录的是内存数据修改的指令记录文本。需要定期进行 AOF 重写,给 AOF 日志进行瘦身。

AOF 日志只记录对内存进行修改的指令记录。

Redis 会在收到客户端修改指令后,先进行参数校验,如果没问题,就立即将该指令文本存储到 AOF 日志中,也就是先存到磁盘,然后再执行指令。即使遇到突发宕机,已存储到 AOF 日志的指令进行重放一下就可以恢复到宕机前的状态。

Redis 提供了bgrewriteaof 指令用于对 AOF 日志进行瘦身。其原理是开辟一个子进程对内存进行遍历转换成一系列 Redis 的操作指令,序列化到一个新的 AOF 日志文件中。 序列化完毕后再将操作期间发生的增量 AOF 日志追加到这个新的 AOF 日志文件中。

4.管道pipeline

客户端通过对管道中的指令列表改变读写顺序就可以大幅节省 IO 时间。管道中指令越多,效果越好。

5.事务

每个事务的操作都有 begin、commit 和 rollback,begin 指示事务的开始,commit 指示事务的提交,rollback 指示事务的回滚。

Redis 对应的是 multi/exec/discard。multi 指示事务的开始,exec 指示事务的执行,discard 指示事务的丢弃(丢弃事务缓存队列中的所有指令)。

Redis事务没有真正的原子性,是隔离性中的串行化,当前执行的事务不被其它事务打断。

Redis的分布式锁是一种悲观锁。Redis 的watch 的机制是一种乐观锁。

watch 会在事务开始之前盯住 1 个或多个关键变量,当事务执行时,就是服务器收到 了 exec 指令要顺序执行缓存的事务队列时,Redis 会检查关键变量自 watch 之后,是否被修改了 (包括当前事务所在的客户端)。如果关键变量被人动过了,exec 指令就会返回 null 回复告知客户端事务执行失败,这个时候客户端一般会选择重试。

Redis 禁止在 multi 和 exec 之间执行 watch 指令,必须在 multi 之前做好盯住关键变量,否则会出错。

6.PubSub

Redis 消息队列不支持消息的多播机制。

Redis使用PubSub(PublisherSubscriber,发布者订阅者模型)支持消息多播。(不常用)

Stream 数据结构给 Redis 带来了持久化消息队列。

7.小对象压缩

如果 Redis 内部管理的集合数据结构很小,它会使用紧凑存储形式压缩存储。

Redis 的 ziplist 是一个紧凑的字节数组结构,每个元素之间都是紧挨着的。

Redis 的 intset 是一个紧凑的整数数组结构,它用于存放元素都是整数的并且元素个数 较少的 set 集合。

如果 set 里存储的是字符串,那么 sadd 立即升级为 hashtable 结构。

当集合对象的元素不断增加,或者某个 value 值过大,超出存储界限时,这种小对象存储就会被升级为标准存储的结构。

8.主从同步

CAP原理:C - Consistent ,一致性。A - Availability ,可用性。P - Partition tolerance ,分区容忍性。

网络分区(主从服务器间的网络断开)发生时,一致性和可用性两难全。

Redis 的主从数据是异步同步的,所以分布式的 Redis 系统并不满足一致性,但是满足可用性。

Redis 保证最终一致性,从节点会努力追赶主节点,最终从节点的状态会和主节点 的状态将保持一致。

增量同步和快照同步

当从节点刚刚加入到集群时,它必须先要进行一次快照同步,同步完成后再继续进行增量同步。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值