Redis随手记(二)原理篇
date
Feb 13, 2021
slug
redis-fundamentals
status
Published
tags
Programming
Redis
summary
type
Page
Year
2021
Redis是个单线程程序
- redis所有的数据都在内存中, 所有运算都是内存级别的运算
- 因此对于时间复杂度为O(n)的指令, 需要小心使用redis, 否则会造成redis卡顿
- redis使用非阻塞IO
- 使用事件轮询(a.k.a 多路复用)来获取剩余数据
- 最简单的事件轮询API是select
- 指令队列
- redis为每个客户端socket都关联一个指令队列
- 客户端的指令通过队列来排队进行顺序处理, 先到先服务
- 响应队列
- redis为每个客户端socket都关联一个响应队列
- redis服务器通过响应队列将指令的返回结果回复给客户端
- 如果队列为空, 则目前不需要write, 会将当前客户端描述符从write_fds中移出来
- 避免select系统调用立即返回写
- 定时任务
- redis的定时任务会记录在MinHeap上
- 最快要执行的任务排在上方
- 在每个循环周期里redis都会对最小堆里已经到时间点的任务进行处理
- 处理完后将下一个最快要执行的任务的所需时间记录下来
- 这个时间就是select系统调用中的timeout参数
通信协议
- RESP (short for Redis Serialization Protocol)
- 将传输的结构数据分为5种最小单元类型
- 单行字符串1以“+”符号开头
- 多行字符串以“$"开头, 后面跟字符串长度
- $11\r\nhello world\r\n
- $-1\r\n -> 代表NULL
- $0\r\n\r\n -> 代表空字符串
- 整数值以“:”符号开头, 后面跟整数的字符串形式
- 错误信息以“-”符号开头
- 数组以“*”开头, 后跟数组的长度
- Client -> Server
- 发送指令只有一种格式, 多行字符串数据
- Server -> Client
- 包含五种格式
持久化
- 有两种, Snapshot是全量备份, AOF log是增量备份
- snapshot
- 内存快照必须使用文件IO操作, 但是文件IO操作不能使用多路复用API
- 为了不阻塞线上业务, redis使用Multiprocessing Copy-On-Write
- 在持久化的时候, redis调用gblic函数fork产生一个子进程
- 父进程继续处理线上业务, 子进程负责持久化
- 子进程不会修改现有的内存数据结构, 只是对数据结构进行遍历读取, 然后序列化写到磁盘中
- 父进程持续服务客户端请求, 对内存数据结构进行不间断的修改
- 此时使用COW, 当父进程需要对数据段某页进行修改时, 就会复制一份共享页面出来, 父进程对其进行修改, 子进程相应的页面没有变化
- AOF log
- 记录的是Redis服务器顺序指令序列
- AOF log只记录对内存进行修改的指令记录
- 这样, 通过对一个空的redis实例replay, 可以恢复redis当前实例的内存数据结构的状态
- 当redis收到客户端修改指令时, 先进行参数校验、逻辑处理, 再将此指令存放到log里, 是属于先执行再存盘;
- 与leveldb, hbase相反
- AOF重写
- redis提供了bgrewriteaof指令
- 原理是fork一个子进程对内存进行遍历, 转换成一系列Redis操作指令, 序列化到新的AOF log file
- 序列化完毕后再将此操作期间发生的增量AOF log 追加到新的log file中
- 追加完毕后立即代替旧的AOF log file
- fsync
- AOF log以文件形式存在, 因此当程序对AOF log file进行写操作时, 实际上是将内容写到了内核为文件描述符分配的一个内存缓存中, 然后内核会将异步地将数据刷回到磁盘中
- 强制将在内核中的AOF log刷回到磁盘的AOF log file中, 实际生产环境中一秒一次的频率比较好
管道
- redis的管道本质上是改变客户端读/写顺序以更好地使用read/wirte buffer, 从而达到
- 减少远程通信次数
- 减少read/write的等待数据到来 /等待buffer清空以继续写入的耗时