好友
阅读权限 10
听众
最后登录 1970-1-1
11、原理1 线程IO模型
1、Redis是个单线程程序 。
2、Redis采用非阻塞IO 。读写方法不会阻塞,能读多少读多少,能写多少写多少。能读多少取决于内核为套接字分配的读缓冲区内部的数据字节数,能写多少取决于内核为套接字分配的写缓冲区的空闲空间字节数。
3、事件轮询(多路复用) 。事件轮询API处于轮询状态。当有事件到来时,根据读写描述符通过select函数找到相应的事件返回,然后线程挨个处理事件。处理完了之后继续轮询。因为我们通过select系统调用同时处理多个通道描述符的读写事件,因此这类系统调用称为多路复用API。现代多路复用API已经不再使用select系统调用,而改用epoll(linux)和kqueue(freebsd & macosx),因为select系统调用的性能在描述符特别多时性能会非常差。
4、指令队列 。Redis会将每个客户端套接字关联一个指令队列。客户端的指令通过指令队列来排队进行顺序处理,先到先服务。
5、响应队列 。Redis同样也为每个客户端套接字关联一个响应队列,Redis服务器通过响应队列来将指令的返回结果回复给客户端。如果队列为空,意味着连接暂时处于空闲状态,不需要去获取写事件,也就可以将当前的客户端描述符从write_fds里面移出来。等到队列有数据了,再将描述符放进去。避免select系统调用立即返回写事件,结果发现没什么数据可以写。此时线程会飙高CPU。
6、定时任务 。Redis的定时任务会记录在一个称为最小堆的数据结构中,最快要执行的任务放在最上方。在每个循环周期,会将最小堆里面最上方的到时的任务立即处理完毕。处理完毕后,将最快要执行的任务还需多少时间记录下来,这个时间就是select系统调用的timeout参数,此时在这个tomeout时间段内,可以睡眠timeout的时间。
12、原理2 通信协议
RESP(Redis Serialization Protocol)
Redis 协议将传输的结构数据分为 5 种最小单元类型,单元结束时统一加上回车换行符
号\r\n。
1、单行字符串 以 + 符号开头。
hello world
+hello world\r\n
2、多行字符串 以 $ 符号开头,后跟字符串长度。
hello world
$11\r\nhello world\r\n
3、整数值 以 : 符号开头,后跟整数的字符串形式。
1024
:1024\r\n
4、错误消息 以 - 符号开头。
参数类型错误
-WRONGTYPE Operation against a key holding the wrong kind of value
5、数组 以 * 号开头,后跟数组的长度。
[1,2,3]
*3\r\n:1\r\n:2\r\n:3\r\n
6、NULL 用多行字符串表示,不过长度要写成-1。
$-1\r\n
7、空串 用多行字符串表示,长度填 0。
$0\r\n\r\n
注意这里有两个\r\n。为什么是两个? 因为两个\r\n 之间,隔的是空串。
例: set author codehole 序列化后为: *3\r\n$3\r\nset\r\n$6\r\nauthor\r\n$8\r\ncodehole\r\n
13、原理3 持久化
持久化机制分为两种:快照 和 AOF日志 。
快照是一次性全量备份 ,AOF日志是连续的增量备份 。快照是内存数据的二进制序列化形式,在存储上非常紧凑,而AOF日志记录的是内存数据修改的指令记录文本。
13.1、快照
Redis采用操作系统的多进程COW(Copy On Write)机制 来实现快照持久化。Redis在持久化时,会调用glibc的函数fork产生一个子进程 ,快照持久化交给子进程来处理,主进程继续处理客户端请求。子进程在刚刚产生时,它和父进程共享内存里面的代码段和数据段。
子进程 做数据持久化,它不会修改现有的内存数据结构 ,它只是对数据结构进行遍历读取,然后序列化写到磁盘中。但是父进程不一样,它必须持续客户端请求,然后对内存数据结构进行不间断的修改。
13.2、AOF原理
AOF日志存储的是Redis服务器的顺序指令序列,AOF日志只记录对内存进行修改的指令记录。对于一个空的Redis实例,可以通过回放AOF日志实现对数据的恢复。AOF处理时,先把指令存储起来,再执行指令,这样在服务器突然宕机时,数据也不至于丢失。
如果Redis长时间执行,那么AOF日志就可能会很长,当恢复数据时,就会非常耗时。因此需要对AOF日志进行瘦身,即AOF重写。
Redis 提供了 bgrewriteaof 指令用于对 AOF 日志进行瘦身。其原理就是开辟一个子进程对内存进行遍历转换成一系列 Redis 的操作指令,序列化到一个新的 AOF 日志文件中。序列化完毕后再将操作期间发生的增量 AOF 日志追加到这个新的 AOF 日志文件中,追加完毕后就立即替代旧的 AOF 日志文件了,瘦身工作就完成了。
AOF日志是以文件形式存在的。先写入到内存中,再写入到磁盘文件中。那么还没有写入到磁盘文件的时候,系统宕机了,那么就会出现日志丢失。为了不丢失日志,Linux的glibc提供了fsync(int fd) 函数可以将指定文件的内容强制从内核刷到磁盘。只要Redis进程实时调用fsync函数就可以保证AOF日志不丢失。但是fsync函数是磁盘IO操作,会比较慢,因此fsync一般都不会每次都调用,可以通过配置设置,通常设置为1s。折中方案,保持高性能的同时,尽可能减少数据丢失。
通常环境中,都是使用主从的方式部署Redis,然后在主节点一般不做持久化处理,专心处理客户端的请求,持久化交给从节点来做。
13.4、混合持久化
了解了上面两种方式,那么很容易就想到如果两种方式一起用是不是就会比较好一点?Redis 4.0 为了解决这个问题,带来了一个新的持久化选项——混合持久化。将某个时间点的全量快照和AOF日志放在一起,那么AOF日志也不是全量日志,恢复不用全量回放,那么效率就会高了。
发帖前要善用【论坛搜索 】 功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。