持久化
redis有两种方式实现持久化:RDB和AOF
AOF:类似数据库WAL 机制,但是redis是先执行命令,然后在记录AOF日志,是一种写后日志 而不是咱们常说的写前日志 (这样做主要是为了redis在写入日志之前不会对命令进行语法检查,只记录成功的命令,避免了出现记录错误命令的情况,并且命令执行完之后在记录不会阻塞当前的写操作,但是这样也会带来风险:数据可能丢失(执行完命令宕机)、可能阻塞其他他操作(AOF日志需要写磁盘) ) 另外,如果运行时间较长,AOF日志比较快,在重启恢复的时候,较大的AOF会导致恢复较慢,redis通过AOF恢复,相当于是吧AOF日志中命令在执行一遍 AOF首先是会写到AOF缓冲区,然后按照不同策略在写入到aof文件 :
- always 每次数据变更都立即写入到磁盘,性能较差,但是数据完整性好
- everysec 每秒将aof缓冲区写入到磁盘,如果宕机,会有1s数据丢失风险
- no 将缓冲区写入到磁盘交给系统处理,性能最好,但是数据可靠性最差
相关参数:
# 是否开启AOF
appendonly yes
appendfilename "appendonly.aof"
#aof文件比上次重写时增长100%触发重写
auto-aof-rewrite-percentage 100
#aof文件大小超过64MB触发重写
auto-aof-rewrite-min-size 64mb
# aof刷盘策略
#appendfsync always
#appendfsync everysec
appendfsync no
RDB: 内存快照持久化,通过对某一时刻redis中内存数据进行一个快照,将数据持久化下来,RDB快照时可以通过save和bgsave,save在主线程执行,会阻塞主线程处理,而bgsave通过fork一个子线程进行创建,不会阻塞主线程。
RDB在bgsave的时候通过fork一个子线程,通过copy-on-right机制,能够复制主线程的内存数据,同时主线程依然能够修改数据. 相关参数:
save 900 1
save 300 10
save 60 10000
#当RDB保存数据失败,是否继续写入,yes表示继续写入
stop-writes-on-bgsave-error yes
# 是否压缩
rdbcompression yes
# 是否进行数据校验,如果为了提升性能,可以关闭
rdbchecksum yes
# rdb快照文件名称
dbfilename dump.rdb
RDB操作默认有三种:1. 在60秒内有10000次操作触发RDB持久化,2. 如果没有满足第一条,在900秒内有1次操作触发RDB持久化 3. 如果没有满足第二条,在300秒内有10次操作触发RDB持久话
相比AOF,RDB崩溃恢复会快很多。
RDB只是一个快照,如果在生成RDB之后,过几秒redis宕机,那么这时候恢复就会丢失这几秒钟的数据,
REDIS 4之后提供了混合持久化机制,先通过RDB写入,然后在RDB的基础上在进行AOF写入。
#开启AOF+RDB混合持久化
aof-use-rdb-preamble yes
主从同步
redis提供了主从机制,可以进行一主多从,达到读写分离。 主从之间进行同步复制的时候,分为三步:1. 连接建立 2. 数据同步 3. 命令传播
数据同步复制,根据主节点的状态分为全量复制和部分复制。
- 全量复制:当主从连接建立成功后初次复制或者无法进行部分复制的时候,将主节点中的所有数据都发送给从节点,比部分复制耗时。全量复制时,主节点会启动一个后台线程生成一份RDB快照文件同时还会吧收到的所有写命令缓存到内存中,当RDB文件生成之后,主节点会将RDB文件发送给从节点,从节点会写入本地磁盘,然后从本地磁盘加载到内存中,接着主节点会将内存中缓存的写命令发送给从节点,从节点会同步这些数据。
- 部分复制:主要在网络中断等情况后的复制,只是将中断期间主节点执行的写命令发送给从节点,比全量复制更加高效。redis从2.8开始支持主从复制的断点续传,如果主从复制过程中网络中断,那么可以接着上次复制的地方继续复制,而不是从头开始复制。
部分复制主要依赖如下三个属性:
复制偏移量 ,主节点和从节点会维护一个复制偏移量 ,表示主节点向从节点传送的字节数,主节点每次向从节点传播N字节的数据时,主节点偏移量增加N,从节点每次收到主节点传来的N字节数据时,从节点偏移量增加N。通过偏移量可以知道主从节点数据是否一致,以及如果不一致,主节点需要发送哪些数据给从节点。复制积压缓冲区 ,主节点中维护了一个固定长度、先进先出的队列(默认带下1MB ),这个就是复制积压缓冲区,其作用是备份主节点发送给从节点的数据,不管有几个从节点,主节点只会维护一个复制积压缓冲区。在命令的传播阶段,主节点除了将写命令发送给从节点还会发送一份给复制积压缓冲区作为备份,该备份还记录了每个字节对应的复制偏移量,因为是先进先出的,所以保存的是主节点最近执行的命令,时间较早的写命令则会被移除缓冲区。复制积压缓冲区大小是有限制的,如果主节点和从节点的复制偏移量超过了缓冲区的长度这时将无法进行部分不止,只能执行全量复制,为了提高网络中断时部分复制执行的概率,可以修改复制积压缓冲区大小 (设置为repl-backlog-szie )- 服务器运行ID,每个redis节点(无论主节点、从节点)都会自动生成一个司机ID(每次启动都不一样),主从初次复制的时候,主节点会将自己的ID发送给从节点,从节点会保存起来,当短线重连时,从节点会将这个ID发送给主节点,主节点这个这个ID判断是否进行部分复制。如果从节点保存的主节点ID与当前主节点ID相同,则说明主从节点之前同步过,主节点会继续判断尝试部分复制。如果不一致,则之前的主节点与当前主节点不是同一个,进行全量复制。
命令传播
在数据同步完成之后,主节点会进行命令传播,主节点会将自己执行的写命令发送给从节点,从节点接收命令并执行,保证主从节点数据的一致性。
由于命令传播是异步的过程,主节点发送写命令后并不会等待从节点的恢复,实际上主从节点之间很难保证实时的一致性,延迟在所难免 数据不一致的程度与主从之间的网络状况、主从节点写命令的执行频率以及主节点的TCP delay配置有关。 repl-disable-tcp-nodelay :这个配置与tcp_nodelay的含义基本一致,当关闭时,无论数据大小都会同步到从节点,耗费带宽,适用于主从网络稳定的场景(多数情况下关闭,使用该配置 ),开启时,主节点每隔指定时间把数据合并为TCP包,节省带宽,默认40ms同步一致
正常情况下,主从全量同步是需要在磁盘上创建一个RDB文件,然后加载到内存中,从节点以此进行数据同步,redis 2.8之后提供了无磁盘复制,在此设置下,子进程直接发送RDB数据给从节点,无需使用磁盘作为中间存储介质,配置如下 :
#是否无磁盘
repl-diskless-sync no
#主节点发送RDB数据给从节点等待一定时长开始复制,这是为了等更多的从节点连接到主节点,更好利用资源提升性能
repl-diskless-sync-delay 5s
在主从模式下,默认情况下从节点默认开启只读模式,redis中从节点也是可以接受其他从节点的连接
在主从模式下,有可能会出现主从延时比较滞后的情况(redis使用异步复制 ),在redis 2.8之后,提供了只有当至少N个节点连接到主节点才有可能配置redis主节点接受写命令,如果至少有N个从节点且滞后小于M秒,那么写命令才会执行,配置如下:
min-slaves-to-write slave数量
min-slaves-max-lag 秒数
如果上述配置为 3 和 5表示从节点数量小于3个或者从节点的延迟都大于5秒,那么主节点拒绝执行写命令并返回客户端错误
主从复制中,对于键过期,从节点不会让键过期,而是等待主节点让键过期,当主节点键到期,会传递一个del命令到所有从节点 为了防止主节点无法及时提供del,从节点使用逻辑时钟保证在不读取的时候如果键过期了则不会返回给客户端
repl-timeout
哨兵机制
redis主从在master节点崩溃之后,需要手动进行恢复,而redis提供了哨兵机制,在master崩溃之后,会自动选举出一个新的master节点进行服务。在seneinel架构下,redis中分为哨兵节点和数据节点,哨兵节点不存储数据。 哨兵节点不断监控主节点和从节点,当主节点出现问题而下线时,选举一个从节点为主节点,剩下的从节点设置新的主节点为主节点。 sentinel中有两种概念下线:主观下线,客观下线
- 主观下线 一个哨兵觉得一个主节点宕机了,比如哨兵ping主节点超过了is-mater-down-after-milliseconds指定的毫秒之后就可以认为主节点宕机了
- 客观下线 比如quorum数量的哨兵都觉得一个主节点宕机了
当sentinel进行故障转移时(重新选举一个主节点),会根据一下信息来评估哪个从节点为新的主节点:
- 与主节点断开时间
- 从节点的优先级
- 复制偏移量的处理
- 运行ID
哨兵节点会按照如下逻辑重新选择主节点
- 首先会根据配置文件中的
slave-priority 进行优先级排序 - 如果优先级相同,然后检察复制偏移量,最新的复制偏移量从节点会被优先选择
- 如果多个从节点优先级和复制偏移量都相同的话,那么选择有更小运行ID节点的从节点。
redis中sentinel机制保证了最终一致性,redis中可以通过如下配置来限制节点之间数据不一致的范围 :
min-slaves-to-write 1
min-slaves-max-lag 5
上述的配置表示 在主从下,至少有一个从节点数据复制和同步的延迟不能超过5秒,一旦所有从节点和主节点的数据复制和同步超过了5秒,那么从节点就不会在接收任何命令请求 . 使用上述配置后能够:1. 减少异步复制数据丢失 2. 减少脑裂的数据丢失(如果主节点发生脑裂,一个主节点与从节点连接都丢失了,这时候如果达到上述限制,将不再接收客户端命令)
集群
redis的主从和sentinel哨兵机制能够保证故障切换,读写分离,但是都是写入都是单个节点,如果我们数据量比较大,单个节点是无法容纳这么多数据的, 因此redis加提供了集群方式,能够最大程度提升系统的容量以及高可用。
redis集群至少需要三个主节点且最大1000个主节点(1000个官方给出的原因是考虑节点间通信带来的消耗)
redis集群使用哈希槽进行数据分区,redis集群总共有16384 个哈希槽,每个键通过CRC16校验后对16384进行取模来决定键属于哪个槽
redis集群之间通信采用的是Gossip协议 ,节点之间以每秒10次的固定频率定时任务进行判断,当集群状态发生改变时,会通过节点间通信同步集群状态。
redis集群中对节点使用主从复制,每个节点可以有1个到多个从节点,如果主节点和从节点全部下线时,整个集群将不能正常运行
在集群中,每个节点维护了一个epoch 的概念,当发生新的选举之后,从节点提升为新的主节点会向其他节点发送自己的的信息,包含了epoch,其他主节点判断当前节点的epoch比之前的epoch大,就会更新该主节点的信息。
redis中内存淘汰策略:
- volatile-lru:设置了过期时间的key使用LRU算法淘汰;
- allkeys-lru:所有key使用LRU算法淘汰;
- volatile-lfu:设置了过期时间的key使用LFU算法淘汰;
- allkeys-lfu:所有key使用LFU算法淘汰;
- volatile-random:设置了过期时间的key使用随机淘汰;
- allkeys-random:所有key使用随机淘汰;
- volatile-ttl:设置了过期时间的key根据过期时间淘汰,越早过期越早淘汰;
- noeviction:默认策略,当内存达到设置的最大值时,所有申请内存的操作都会报错(如set,lpush等),只读操作如get命令可以正常执行;
LRU(Least Recently Used)表示最近最少使用,该算法根据数据的历史访问记录来进行淘汰数据,其核心思想是“如果数据最近被访问过,那么将来被访问的几率也更高”。
LFU(Least Frequently Used)表示最不经常使用,它是根据数据的历史访问频率来淘汰数据,其核心思想是“如果数据过去被访问多次,那么将来被访问的频率也更高”。LFU算法反映了一个key的热度情况,不会因LRU算法的偶尔一次被访问被误认为是热点数据。
==== 持续更新 。。。
|