IO模型
- 使用多路复用IO模型
- 一次接收多个读写描述符,并且每一个描述符提供一个timeout超时参数
- 通过描述符要读取的内容和大小,去进行IO读写
- 一旦timeout超时才会将事件标记为处理完成,并且将结果返回。
- 如果没有超时,线程会继续循环为每一个事件IO读写
- 一次接收多个读写描述符,并且每一个描述符提供一个timeout超时参数
- 使用指令队列排序客户端指令
- redis会将每个客户端套接字都关联一个指令队列,客户端的指令通过队列来排队进行顺序处理,先到先得。
- 使用响应队列排序响应结果
- 和指令队列相似,将指令队列响应的结果返回给客户端的队列。
通信协议
RESP(Redis Serialization Protocol)
- Redis序列化协议,实现过程简单,解析性能极好
- 将传输的结构数据分为5种最小单元类型,单元结束统一加上换行回车\r\n
- 每个最小单元类型都有不同的开头和结构
客户端与服务端通信
- 客户端向服务器发送指令使用多行字符串数组
- 一个set指令
set author codehole
序列化如下*3\r\n$3\r\nset\r\n$6\r\nauthor\r\n$8\r\ncodehole\r\n
- 一个set指令
- 服务端向客户端发送指令和RESP通信序列化协议结构类似
持久化
- RDB快照
- 是一种全量备份
- 存储上非常紧凑
- 使用操作系统的多进程COW(Copy On Write)机制实现快照持久化
- 子进程只负责循环读取内存中的数据,而父进程在响应客户端请求时如果对数据进行了修改,就会将使用写时复制技术
- AOF增量日志
- 记录内存数据修改的指令记录文本
- 需要定时重写瘦身
- bgrewriteaof指令用于AOF文件瘦身,就是合并AOF中的无用的指令操作,替代原本的AOF文件
混合持久化
- RDB和AOF各有优缺点,所以Redis4.0提供了一个新的持久化选项——混合持久化
- 大概就是:RDB不是因为不能随时或者短时间频繁持久化吗,因为每次都会消耗大量的资源,那就就必定会有一段时间的数据可能会丢失,那么我就使用AOF来存储你这部分时间的增量指令。
- ![[Pasted image 20231106140904.png]]
管道
- 管道并不是Redis服务器提供的技术,而是客户端提供的
- 类似与Mysql的merge insert,将读写操作都进行合并,避免多次请求的发出
- 客户端的write本质上就是将数据放到套接字发送缓冲区,将缓冲区数据发送到网卡,网卡在通过路由交换机将数据发送到服务器
- 客户端的read本质就是,发送请求后,等待套接字返回数据到缓冲区,再从缓冲区拿取数据
事务
- 事务指令为
- multi表示事务开始
- exec表示事务指令
- discard表示事务丢弃
- exec才代表事务中的命令开始执行
- Redis的事务执行时不具备原子性,只是满足了事务的隔离性的串行化。
- 提供discard指令,用于在exec执行之前取消multi排队
发布订阅
- 消息多播的概念:将生产者生产的消息有中间件发送到多个消息队列,是分布式系统常用的解耦方式
- redis为了消息多播,不再依赖原本的5种基本数据类型,而是使用PubSub来支持消息多播
- 消费者订阅主题必须明确指定主题的名称,Redis提供了模型订阅功能,解决了多个主题一次一次订阅的麻烦,可以一次性订阅多个主题
小对象压缩
- 为了节约Redis的内存,防止Redis崩溃,Redis作者增加了许多优化点来优化数据结构的内存占用
- Redis很多类型都有小对象压缩技术
- ziplist
- u64在可以的时候可以压缩到u16
- 但是当压缩的对象不能存储新加入的数据时,就会将整个存储转变为正常大小
内存使用
- redis中删除了1GB的key后却发现redis占用的内存没有变化,因为操作系统回收是以页为单位回收内存,删除的1GB的key并不是都在同一个页中
- 可以想象如果使用标记整理回收算法,那么就不会出现这种情况,因为标记整理可以腾出大量的连续空间,但是由于Redis的内存结构并不是单纯的堆,而是根据hash槽存储key和value的结构,所以使用标记整理是不合理的
- redis的内存分配是使用的第三方内存分配库实现,目前使用的是jemalloc(fackbook)库,可以切换到tcmalloc(google)库