原理篇
线程IO模型
首先说明,Redis是中间件是单线程的。那么redis是如何保证高并发的呢。这就设计到一个词汇“多路复用”
阻塞IO
与Java的IO/NIO类似,IO本身是一个阻塞方法。
- 当客户端发起write时,操作系统将write请求套接字存储在send buffer中
- 客户端操作系统将缓冲区内容发送至网卡,网卡通过硬件“网际路由”将数据送到服务器网卡
- 服务器操作系统将网卡数据放到接受缓冲的recv buffer中
- 服务器read数据
以上是一个单项操作,反之亦然。由此可见,阻塞IO最大的时间浪费在等待网际路由的链路上。于是,为了减少网际路由带来的影响,出现了非阻塞IO
非阻塞IO
非阻塞IO流程和阻塞IO流程大致一样,不过颠倒了执行顺序。当客户端发起write请求时,实际上,当操作系统将套接字发送到网卡时,write流程已经结束了。此时,阻塞IO不会执行任何操作,而是等待服务端返回数据,读取recv buffer。
如果我们换一种顺序,当write方法执行到网卡时,不等待服务端响应,继续发送数据。就会降低网际路由带来的影响,因为网际路由的延迟时间只有第一次发送的时候才会影响我们。
事件轮询(多路复用)
非阻塞IO有个问题,我们读取数据的时候,如果只读取一部分就返回了,那么线程如何知道何时应该继续读?这里就提到了事件轮询API,事件轮询API是操作系统提供的接口,最简单的事件轮询是select接口。输入read_fds & write_fds描述符,输出与之对应的可读可写事件。同时还提供timeout参数,如果没有任何事件到来,最多等待timeout值的时间。
通信协议
Redis通信协议虽然很浪费流量,但是依然不影响它的访问性能。
RESP
RESP是Redis的序列化协议的简写,是一种直观的文本协议,实现过程简单,解析性能好。
- 单行字符串以 ”+“ 开头
- 多行字符串以 ”$" 开头,后跟字符串长度
- 整数值以 ”:“ 开头,后跟整数的字符串形式
- 错误消息以 ”-“ 开头
- 数组以 ”*“ 开头,后跟数组的长度
持久化
Redis如果突然宕机,内存数据就会全部丢失,此时就必须有一份机制保证redis发数据不会丢失,这种机制就是持久化
Redis的持久化方式有两种,第一种是快照,第二种是AOF日志。
快照原理
Redis是单线程程序,这个线程要与多个客户端套接字并发读写操作。而内存快照要求Redis必须进行IO操作,可文件IO不支持多路复用API。为了不阻止线上的业务,redis使用操作系统多进程COW(Copy On Write)机制实现快照的持久化。
Redis持久化时会调用fork函数产生子进程,快照产生时,父进程所有的读操作不受影响,所有的写操作都从原有的页面上复制一份进行修改,完全不影响正在运行的子进程,所以说,当子进程诞生的一刻,数据已经确定了。
AOF原理
AOF存储的是Redis服务器的顺序指令序列,AOF只记录对内存进行修改的指令记录。可以通过对AOF的重放来恢复Redis当前实例的数据结构状态。AOF本身会随着时间变的越来越庞大,Redis提供了bgrewiteaof指令为AOF日志瘦身。AOF以文件形式存在,势必会出现缓冲区数据没被写入的情况导致数据丢失。Linux提供了fsync函数,这个参数是可配置的。
事务
Redis的事务不确保原子性,因为其单线程特性,只能保证线程执行时不被其他线程打断。
Redis提供了multi、exec、discard。来开启、执行、丢弃线程。
Redis还提供了watch机制,它会在事务开启前盯住一个变量,当事务执行时,会检查这期间变量有没有被修改过,如果被人动过了,则返回NULL告知执行失败。
集群篇
主从同步
CAP原理
Cansistent:一致性
Availability:可用性
Partition tolerance:分区容忍性
当出现网络分区时,一致性和可用性难两全
最终一致性
Redis主从数据时异步同步的,所以Redis并不满足一致性要求,但保证了Redis的可用性
Redis会保持最终一致性,从节点会尽力追赶主节点,最终和主节点的状态保持一致。
增量同步
Redis同步的是指令流,主节点会将修改指令存在buffer中,而buffer是一个定长的环形数组,当数据超出数组长度时,数组头部的值就将被覆盖,此时,Redis就将进行更加复杂的快照同步。如果快照同步期间,buffer再次覆盖,就容易形成同步的死循环。
哨兵
当Redis宕机时,程序如何切换主从节点?于是就出现了Redis哨兵,程序向哨兵询问应该访问哪个节点,被告之后再访问对应的Redis节点
集群
集群目前有两种解决方案: Codis以及Redis Cluster
Redis Cluster
Redis Cluster 将所有数据划分为16384的槽位。当Redis Cluster客户端连接集群时,会获取到一份槽位配置信息,这样当用户查找某个key时,可以直接定位节点所在。如果客户端槽位与服务端不一致,这就需要槽位纠正机制。
当客户端发送key到一个不存在的节点时,节点会返回-MOVED [IP] 来指定正确的槽位,这时,客户端要立即纠正槽位映射表。