Redis-基本原理

redis是单线程的,IO模型采用多路复用

redis的线程模型

redis的io是单线程多路复用。

为客户端关键字关联一个请求队列和响应队列,用于处理请求和响应。

定时任务采用最小堆的方案,俩个定时任务的间隔时间就是select(timeout)的timeout

redis的备份

快照用于全量备份,AOF是命令重放用于增量备份

快照的备份原理CoW

由于redis的快照备份需要用到磁盘IO是没有NIO的,为了不影响业务的正常请求,redis的磁盘备份采用了系统的COW。方法如下

  1. 父进程调用glibc,fork出一个子进程,这时候子进程和父进程会同时返回。父进程pid大于0,子进程pid等于0,如果小于0说明没有资源,由于是fork出的子进程,俩者共享代码段和数据段
  2. 父进程依然处理正常请求,子进程处理快照的持久化
  3. 对于写的请求,会用到系统的cow。父进程在写的时候会将数据页复制一份,在复制的这份里进行修改,子进程对应的数据页不做变更,在fork出来的那一刻就固定下来了

注意,随着父进程写请求的增多,内存会持续增长,不过也不会超过原数据段的2倍。

AOF

随着日积月累,AOF的体积会逐渐增大,一旦重放会导致redis长期无法对外提供服务

瘦身:bgrewriteaof,

  1. 开辟一个新的进程遍历内存的数据,转换成一些列的指令,写到新的aof文件中
  2. 新增的aof指令追加的这个文件中
  3. 替换原文件完成瘦身

    fsync,redis写aof文件是些到一个内核的内存中,通过调用fsync刷到磁盘中,如果这段时间服务器宕机会丢失数据。redis采用glibc的fsync(int fd)方法每隔一段时间刷盘一次。(每次命令都刷盘会降低吞吐)

运维

由于aof的fsync和快照的大块写文件都影响性能,建议持久化放在从库中做,为了防止磁盘分区还建议有多个从库。

pipeline和事物

pipeline简称管道,为了减少网络的开销,piplline可以一次提交多条指令给redis一起执行,本质上是client的行为。

事物:redis支持事物,但是无法解决事物的原子性,只能保证事物的隔离性。redis事物的隔离性是通过管道来实现的

为了节省性能,redis的事物往往配合pipeline一起使用。

事物命令
multi、exec、discard

pubsub

redis自身的消息队列,缺点很明显:消息不能持久化,发送的消息只能被当时在线的消费者消费,如果消费者宕机是无法消费消息的。这个特性可以做服务发现。后续5.0提供了stream来取代它

支持模式订阅,message订阅

对象的压缩

如果小于4G可以采用32位进行编译,内存会少一半

zipList

压缩列表,redis在zset、hash、intset这些数据结构存储数据时候在一定阈值下回采用ziplist来存储。

ziplist实际上是数组的一个变种,有点是:元素长度不固定节省空间,内存地址连续对缓存友好。

zset:元素个数不超过128,k/v长度不超过64

hash、list:元素个数不超过512,k/v长度不超过64

set:不超过512,数据是intset时候,会进行升级,随着元素的增大会从uint16–>uint32–>unint64

内存管理

redis是以页为单位回收内存的,如果该数据页有一个key,也不会被回收。但是可以被重复使用,这点和mysql很类似

redis的内存管理采用第三方库默认是jemallloc