Redis 原理
1 线程模型
redis是一个单线程模型
非阻塞IO
非阻塞 IO 在套接字对象上提供了 个选项 Non_Blocking ,当这个选项打开时,读写方法不会阻塞,而是能读多少读多少,能写多少写多少 能读多少取决于内核为套接字分配的读缓冲区内部的数据字节数,能写多少取决于内核为套接宇分配的写缓冲区的空闲空间字节数 读方法和写方法都会通过返回值来告知程序实际读写了多少字节。
有了非阻塞 IO 意昧着线程在读写 IO 时可以不必再阻塞了,读写可以瞬间完成,然后线程就可以继续干别的事了
事件轮询(多路复用)
事件轮询 API 用来通知线程读写数据。
输入是读写描述符列表 read_fds & write fds, 输出是与之对应的可读可写事件 同时还提供了一个 timeout 参数,如果没有任何事件到来,那么就最多等待 timeout 的值的时间,线程处于阻塞状态 一旦期间有任何事件到来,就可以立即返回 时间过了之后还是没有任何事件到来,也会主即返回。
2 通信协议
RESP是 Redis 序列化协议( Redis Serialization rotocol )的简写 。它是种直观的文本协议,优势在于实现过程异常简单,解析性能极好。
Redis 协议将传输的结构数据分为 种最小单元类型,单元结束时统一加上回车
换行符号 \r\n
-
单行字符串以“+”符号开头。
-
多行字符串以“$”符号开头,后跟字符串长度
-
整数值以“:”符号开头,后跟整数的字符串形式
-
错误消息以“-”符号开头。
-
数组以“*”号开头,后跟数组的长度
通过在字符串中拼接这些符号,redis可以快速解析数据。
3 管道
在部分应用场景中,读写操作并没有严格的顺序限制时,redis使用了管道来减少网络请求数。
(客户端) 写-》读 (reids)
(客户端) 写-》读 (reids)
若两个操作之间可以简化为
(客户端) 写-写-读-读 (reids)
则可以减少一次网络连接。
客户端通过对管道中的指令列表改变读
客户端通过对管道中的指令列表改变读写顺序就可以大幅节省 IO 时间。管道中指令越多,效果越好。
4 事务
每个事务的操作指令都有 egin commit rollback, begin 指示事务的开始,
commit 指示事务的提交, rollback 指示事务的回滚。
Redis 事务在形式上看起来也差不多,指令分别是 multi exec、 discard。
multi 指示事务的开始,
exec 指示事务的执行,
discard 指示事务的丢弃
redis 事务不具备原子性
事务的原子性一般指事务中的操作要么都做,要么都不做。
redis并没有实现上述的原子性。
它只保证了事务的”隔离性“中的串行化,当前执行的事务有着不被其他事务打断的权利。
discard 丢弃之前的操作
redis 为事务提供了一个 discard 指令,用于丢弃在exec 执行之前 的 事务缓存队列中的所有指令。
为什么 Redis 的事务不能支持回滚?
5 小道消息一-PubSub
Redis 消息队列的不足之处,那就是它不支持消息的多播机制。
消息多播
消息多播允许生产者只生产一次消息,由中间件负责将消息复制到多个消息队列,每个消息队列由相应的消费组进行消费
为了支持消息多播, Redis 不能再依赖于那5种基本数据类型了,它单独使用了个模块来支持消息多播,这个模块的名字叫作 PubSub 。
PubSub 的缺点
Pub Sub 的生产者传递过来一个消患, Redis 会直接找到相应的消费者传递过去。如果一个消费者都没有,那么消息会被直接丢弃。如果开始有三个消费者, 个消费者突然挂掉了,生产者会继续发送消息,另外两个消费者可以持续收到消息,但是当挂掉的消费者重新连上的时候,在断连期间生产者发送的消息,对于这个消费者来说就是彻底丢失了
如果 Redis 停机重启, PubSub 的消息是不会持久化的,毕竟 Redis 看机就相当于一个消费者都没有,所有的消息会被直接丢弃。
因为PubSub 的消息没有持久化,如果消费者挂掉了,在挂掉期间的消息是没有办法接收的。
6 性能优化
redis所有数据都在内存中,redis使用了很多方式 来优化内存占用。
32bit 和 64bit
Red is 如果使用 32bit 进行编译,内部所有数据结构所使用的指针空间占用会少一半,如果你的 Redis 使用内存不超过 4GB ,可以考虑使用 32bit 进行编译,能够节约大量内存。 4GB 的容量作为一些小型站点的缓存数据库是绰绰有余的,如果不足还可以通过增加实例的方式来解决。
小对象压缩存储( ziplist)
Red is的ziplist 个紧凑的字节数组结构。每个元素之间都是紧挨着的。
内存回收机制
Redis 并不总是将空闲内存立即归还给操作系统。
如果当前 Redis 内存有 lOGB ,当你删除了 lGB key 后,再去观察内存,你会发现内存变化不会太大。
原因是操作系统是以页为单位来回收内存的,这个页上只
要还有一个 key 在使用,那么它就不能被回收。
Redis 虽然删除了 lGB key ,但是这些 key 分散到了很多页面中,每个页面都还有其他 key 存在,这就导致了内存不被立即回收。
不过,如果你执行 flushdb 然后再观察内存,会发现内存确实被回收了 。原因是所有的 key 都被干掉了,大部分之前使用的页面都完全干净了,就会立即被操作系统回收。
Redis 然无法保证立即回收已经删除的 ey 的内存,但是它会重新使用那些尚未回收的空闲内存。这就好比电影院里虽然一拨观众走了,但是座位还在,下一拨观众来了,直接坐上就行,而操作系统回收内存就好比把座位也都给搬走了。
内存分配算法
目前 Redis 可以使用 jemalloc ( facebook) 库采管理内存,也 可以切换到 tcmalloc ( google )库。因为 jemalloc 的性能相比tcmalloc 要稍好些,所以 Red is 默认使用了 jemalloc