导图社区 Redis
这是一个REDIS的思维导图,从为什么效率这么快、为什么变异、生产常见问题、应用场景、内存淘汰策略、事物、常见面试问题、集群方案、数据结构、分布式锁、主从复制、持久化几个方面作了一个介绍,带我们更好的了解REDIS。
编辑于2021-08-22 03:40:38这是一个MYSQL的思维导图,从理论篇、高可用、主从复制模式、SQL优化、逻辑架构、面试常考、MySQL日志、索引、非关系型数据库等几个方面作了介绍,非常的全面,快收藏起来吧!
这是一个REDIS的思维导图,从为什么效率这么快、为什么变异、生产常见问题、应用场景、内存淘汰策略、事物、常见面试问题、集群方案、数据结构、分布式锁、主从复制、持久化几个方面作了一个介绍,带我们更好的了解REDIS。
这是一个TCP/IP的思维导图,介绍了网路层、数据链路层、面试常考题、拥塞控制机制、TCP重传、如何解决2MSL产生的问题、等待2MSL会产生的问题、为什么要等待2MSL、TCP挥手为什么要4次、初始序列号ISN的取值等一系列的问题,非常的全面哦!
社区模板帮助中心,点此进入>>
这是一个MYSQL的思维导图,从理论篇、高可用、主从复制模式、SQL优化、逻辑架构、面试常考、MySQL日志、索引、非关系型数据库等几个方面作了介绍,非常的全面,快收藏起来吧!
这是一个REDIS的思维导图,从为什么效率这么快、为什么变异、生产常见问题、应用场景、内存淘汰策略、事物、常见面试问题、集群方案、数据结构、分布式锁、主从复制、持久化几个方面作了一个介绍,带我们更好的了解REDIS。
这是一个TCP/IP的思维导图,介绍了网路层、数据链路层、面试常考题、拥塞控制机制、TCP重传、如何解决2MSL产生的问题、等待2MSL会产生的问题、为什么要等待2MSL、TCP挥手为什么要4次、初始序列号ISN的取值等一系列的问题,非常的全面哦!
REDIS
为什么效率这么快
数据保存在内存中
底层数据结构
单线程模型
IO多路复用,非阻塞IO
为什么变慢
阻塞主线程
AOF重写和RDB快照
开启内存大页
命令复杂度高
bigkey操作
从节点全量同步
AOF同步写盘
内存达到maxmemory
操作系统
开启swap
网络问题
线程上下文切换
磁盘性能低
生产常见问题
缓存雪崩
缓存同一时间大面积的失效,所以,后面的请求都会落到数据库上,造成数据库短时间内承受大量请求而崩掉
解决方案
缓存数据的过期时间设置随机,防止同一时间大量数据过期现象发生
一般并发量不是特别多的时候,使用最多的解决方案是加锁排队
给每一个缓存数据增加相应的缓存标记,记录缓存的是否失效,如果缓存标记失效,则更新数据缓存
缓存穿透
指缓存和数据库中都没有的数据,导致所有的请求都落到数据库上,造成数据库短时间内承受大量请求而崩掉
解决方案
接口层增加校验,如用户鉴权校验,id做基础校验,id<=0的直接拦截
从缓存取不到的数据,在数据库中也没有取到,这时也可以将key-value对写为key-null,缓存有效时间可以设置短点,如30秒(设置太长会导致正常情况也没法使用)。这样可以防止攻击用户反复用同一个id暴力攻击
采用布隆过滤器,将所有可能存在的数据哈希到一个足够大的 bitmap 中,一个一定不存在的数据会被这个 bitmap 拦截掉,从而避免了对底层存储系统的查询压力
本质上布隆过滤器是一种数据结构,比较巧妙的概率型数据结构(probabilistic data structure)
特点
高效地插入和查询
用途
可以用来告诉你 “某样东西一定不存在或者可能存在”
相比于传统的 List、Set、Map 等数据结构的优缺点
优点
更高效、占用空间更少
缺点
其返回的结果是概率性的,而不是确切的
缓存击穿
缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。和缓存雪崩不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库
解决方案
设置热点数据永远不过期
加互斥锁
应用场景
缓存
全页缓存(FPC)
除基本的会话token之外,Redis还提供很简便的FPC平台。以Magento为例,Magento提供一个插件来使用Redis作为全页缓存后端。此外,对WordPress的用户来说,Pantheon有一个非常好的插件 wp-redis,这个插件能帮助你以最快速度加载你曾浏览过的页面
会话缓存
数据库缓存
分布式锁
在分布式场景下,无法使用单机环境下的锁来对多个节点上的进程进行同步。可以使用 Redis 自带的 SETNX 命令实现分布式锁,除此之外,还可以使用官方提供的 RedLock 分布式锁实现。
计数器
可以对 String 进行自增自减运算,从而实现计数器功能。Redis 这种内存型数据库的读写性能非常高,很适合存储频繁读写的计数量。
消息队列
内存淘汰策略
全局
noeviction
当内存不足以容纳新写入数据时,新写入操作会报错
allkeys-lru
当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。(这个是最常用的)
allkeys-random
当内存不足以容纳新写入数据时,在键空间中,随机移除某个key
针对设置过期时间的键
volatile-lru
当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key
volatile-random
当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key
volatile-ttl
当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除
事务
事务开始
MULTI
命令入队
事务执行过程中,如果服务端收到有EXEC、DISCARD、WATCH、MULTI之外的请求,将会把请求放入队列中排队
事务执行
EXEC
清空事务队列,并放弃执行事务, 并且客户端会从事务状态中退出
DISCARD
ACID
原子性(Atomicity)
原子性是指事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生
一致性(Consistency)
一致性是指事务执行结束后,数据库的完整性约束没有被破坏,事务执行的前后都是合法的数据状态
隔离性(Isolation)
多个事务并发执行时,一个事务的执行不应影响其他事务的执行
持久性(Durability)
持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响
常见面试问题
redis事务保证原子性吗,支持回滚吗
单条命令是原子性执行的,但事务不保证原子性,且没有回滚。事务中任意命令执行失败,其余的命令仍会被执行
Redis如何实现延时队列?
使用sortedset,使用时间戳做score, 消息内容作为key,调用zadd来生产消息,消费者使用zrangbyscore获取n秒之前的数据做轮询处理
一个字符串类型的值能存储最大容量是多少?
512MB
集群方案
Sentinel哨兵
Sentinel进程功能
集群监控
负责监控 redis master 和 slave 进程是否正常工作
消息通知
如果某个 redis 实例有故障,那么哨兵负责发送消息作为报警通知给管理员
故障转移
如果 master node 挂掉了,会自动转移到 slave node 上
配置中心
如果故障转移发生了,通知 client 客户端新的 master 地址
Sentinel定时任务
通过向主从节点发送info命令获取最新的主从结构
通过发布订阅功能获取其他哨兵节点的信息
通过向其他节点发送ping命令进行心跳检测,判断是否下线
主库下线判断
主观下线(s_down)
一个哨兵在设定的时间(down-after-milliseconds:默认30s)内,没有收到主库的响应,那么此时是这个哨兵主观的认为主库下线了,并不能认为真正的下线
客观下线(o_down)
当多数哨兵在设定的时间内,都没有收到主库的响应,那么此时就可以认为主库真的下线,称为客观事实了
重新选主
从库筛选
筛去标记为下线的从库
筛去主从之间网络不顺畅的从库
在主从模式下,如果主从之间网络断开的次数和超时时间超过 down-after-milliseconds 的设置,那么就会认为该从库网络不健康了,不稳定了
剩余从库打分
先比较从库的优先级
可以手动设置从库的优先级,通过 slave-priority 进行设置,数字越小,级别越高。
再比较与主库的同步进度越接近
从库数据越新,优先级越高,通过对比从库的slave_repl_offset参数值,越大则越新
最后比较ID号越小,得分越高
哨兵选举Leader
主导者:发现主观下线,并发送is-master-down-by-addr给其他哨兵
得到的票数要达到配置的quorum阀值
获得哨兵半数以上的票数
通知更换主库
通知其他从库执行replicaof 到新的Master
并且通知连接redis 的客户端,告诉它们新的Master的地址和端口
客户端订阅主从切换switch master事件
Cluster集群(服务端路由查询)
优点
无中心架构,支持动态扩容,对业务透明
具备Sentinel的监控和自动Failover(故障转移)能力
客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可
高性能,客户端直连redis服务,免去了proxy代理的损耗
缺点
运维也很复杂,数据迁移需要人工干预
只能使用0号数据库
不支持批量操作(pipeline管道操作)
分布式逻辑和存储模块耦合等
集群部署
手动执行redis命令部署
1.启动节点:将节点以集群模式启动,此时节点是独立的,并没有建立联系
2.节点握手:让独立的节点连成一个网络
cluster meet {ip} {port}
3.分配槽:将16384个槽分配给主节点
cluster addslots
4.指定主从关系:为从节点指定主节点
cluster replicate
使用Ruby脚本部署(推荐)
1.安装Ruby环境
apt-get install ruby gem install redis
2.启动节点
3.创建集群
./redis-trib.rbcreate --replicas 1 ${集群全部节点ip:port信息}
数据分区方案
顺序分区
哈希分区
哈希取余分区
分区思路
计算key的hash值,然后对节点数量进行取余,从而决定数据映射到哪个节点上
不足
当新增或删减节点时,节点数量发生变化,系统中所有的数据都需要重新计算映射关系,引发大规模数据迁移
一致性哈希分区
分区思路
一致性哈希算法将整个哈希值空间组织成一个虚拟的圆环,范围为0-2^32-1;对于每个数据,根据key计算hash值,确定数据在环上的位置,然后从此位置沿环顺时针行走,找到的第一台服务器就是其应该映射到的服务器
不足
当节点数量较少时,增加或删减节点,对单个节点的影响可能很大,造成数据的严重不平衡
带虚拟节点的一致性哈希分区(redis集群选用的方案)
分区思路
在一致性哈希分区的基础上,引入了虚拟节点的概念
虚拟节点称为槽(slot)。槽是介于数据和实际节点之间的虚拟概念;每个实际节点包含一定数量的槽,每个槽包含哈希值在一定范围内的数据。引入槽以后,数据的映射关系由数据hash->实际节点,变成了数据hash->槽->实际节点
Gossip协议
方法论支撑
在一个处于有界网络的集群里,如果每个节点都随机与其他节点交换特定信息,经过足够长的时间后,集群各个节点对该份信息的认知终将收敛到一致
特点
符合BASE理论,适用于最终一致性的业务场景
它可以很方便地实现弹性集群,允许节点随时上下线,提供快捷的失败检测和动态负载均衡等
即使集群节点的数量增加,每个节点的负载也不会增加很多,几乎是恒定的。这就允许 Redis Cluster 或者 Consul 集群管理的节点规模能横向扩展到数千个
节点维护信息
当前集群状态
集群中各节点所负责的 slots 信息,及其 migrate 状态
集群中各节点的 master-slave 状态
集群中各节点的存活状态及怀疑 Fail 状态
节点通信消息类型
MEET
通过「cluster meet ip port」命令,已有集群的节点会向新的节点发送邀请,加入现有集群,然后新节点就会开始与其他节点进行通信
PING
节点按照配置的时间间隔向集群中其他节点发送 ping 消息,消息中带有自己的状态,还有自己维护的集群元数据,和部分其他节点的元数据
PONG
节点用于回应 PING 和 MEET 的消息,结构和 PING 消息类似,也包含自己的状态和其他信息,也可以用于信息广播和更新
FAIL
节点 PING 不通某节点后,会向集群所有节点广播该节点挂掉的消息。其他节点收到消息后标记已下线
PUBLISH
通过发布与订阅功能广播消息
FAILOVER_AUTH_REQUEST
请求进行故障转移操作,要求消息的接收者通过投票来支持消息的发送者
FAILOVER_AUTH_ACK
消息的接收者同意向消息的发送者投票
UPDATE
slots 已经发生变化,消息发送者要求消息接收者进行相应的更新
MFSTART
为了进行手动故障转移,暂停各个客户端
COUNT
消息总数
基于客户端分配(Redis Sharding)
优点
优势在于非常简单,服务端的Redis实例彼此独立,相互无关联,每个Redis实例像单服务器一样运行,非常容易线性扩展,系统的灵活性很强
缺点
由于sharding处理放到客户端,规模进一步扩大时给运维带来挑战。 客户端sharding不支持动态增删节点。服务端Redis实例群拓扑结构有变化时,每个客户端都需要更新调整。连接不能共享,当应用规模增大时,资源浪费制约优化
基于代理服务器分片
Twemproxy
Codis
优点
透明接入,业务程序不用关心后端Redis实例,切换成本低
Proxy 的逻辑和存储的逻辑是隔离的
缺点
代理层多了一次转发,性能有所损耗
分布式寻址算法
hash 算法(大量缓存重建)
一致性 hash 算法(自动缓存迁移)+ 虚拟节点(自动负载均衡)
redis cluster 的 hash slot 算法
常见java客户端
Redission(官方推荐)
Jedis
Letture
数据结构
string字符串
SDS(simple dynamic string)
数据结构构成
len
表示buf已使用的长度
free
表示buf未使用的长度
buf
表示字节数组,用来存储字符串
SDS与C字符串的比较
获取字符串长度的时间复杂度
SDS是O(1)
C字符串是O(n)
缓冲区溢出
使用C字符串的API时,如果字符串长度增加(如strcat操作)而忘记重新分配内存,很容易造成缓冲区的溢出
SDS由于记录了长度,相应的API在可能造成缓冲区溢出时会自动重新分配内存,杜绝了缓冲区溢出
修改字符串时内存的重分配
对于C字符串,如果要修改字符串,必须要重新分配内存(先释放再申请),因为如果没有重新分配,字符串长度增大时会造成内存缓冲区溢出,字符串长度减小时会造成内存泄露
对于SDS,由于可以记录len和free,因此解除了字符串长度和空间数组长度之间的关联,可以在此基础上进行优化:空间预分配策略(即分配内存时比实际需要的多)使得字符串长度增大时重新分配内存的概率大大减小;惰性空间释放策略使得字符串长度减小时重新分配内存的概率大大减小
存取二进制数据
SDS可以
SDS以字符串长度len来作为字符串结束标识,因此没有这个问题
C字符串不可以
因为C字符串以空字符作为字符串结束的标识,而对于一些二进制文件(如图片等),内容可能包括空字符串,因此C字符串无法正确存取
list列表
双向链表
压缩表
hash散列
压缩表
哈希表
set集合
哈希表
整数列表
zset有序集合
压缩表
跳表
redisObject(16bytes)
type(4bits):表示对象的类型
REDIS_STRING(字符串)
最长不超过512MB
REDIS_LIST (列表)
最多存储2^32-1个元素
REDIS_HASH(哈希)
REDIS_SET(集合)
最多可以存储2^32-1个元素
REDIS_ZSET(有序集合)
encoding(4bits):表示对象的内部编码
获取key的编码:object encoding ${key}
对于string字符串
REDIS_ENCODING_INT
使用整数值实现的字符串对象
字符串值是整型
REDIS_ENCODING_EMBSTR
使用embstr编码的简单动态字符串实现的字符串对象
<=39字节的字符串
REDIS_ENCODING_RAW
使用简单动态字符串实现的字符串对象
大于39个字节的字符串
对于list列表
REDIS_ENCODING_ZIPLIST
使用压缩列表实现的列表对象
列表中元素数量小于512个,且列表中所有字符串对象都不足64字节
REDIS_ENCODING_LINKEDLIST
使用双端链表实现的列表对象
对于hash散列
REDIS_ENCODING_ZIPLIST
使用压缩列表实现的哈希对象
哈希中元素数量小于512个;哈希中所有键值对的键和值字符串长度都小于64字节
REDIS_ENCODING_HT
使用字典实现的哈希对象
对于set集合
REDIS_ENCODING_INTSET
使用整数集合实现的集合对象
集合中元素数量小于512个;集合中所有元素都是整数值
REDIS_ENCODING_HT
使用字典实现的集合对象
对于zset有序集合
REDIS_ENCODING_ZIPLIST
使用压缩列表实现的有序集合对象
有序集合中元素数量小于128个;有序集合中所有成员长度都不足64字节
REDIS_ENCODING_SKIPLIST
使用跳跃表和字典实现的有序集合对象
lru:记录的是对象最后一次被命令程序访问的时间
占据的比特数不同的版本有所不同(如4.0版本占24比特,2.6版本占22比特)
获取key的空转时间:object idletime ${key}
refcount(4bytes):记录的是该对象被引用的次数
ptr:ptr指针指向具体的数据
ptr指针占据的字节数与系统有关,例如64位系统中占8个字节
分布式锁
RedLock
Redission
主从复制
2.8 版本之前
阶段一:同步
1.从服务器向主服务器发送 SYNC 命令
2.主服务器收到 SYNC 命令之后,开始执行 BGSAVE 命令生成 RDB 文件。在生成 RDB 文件期间,将写命令记录在一个缓冲区中
3.服务器发送 RDB 文件文件给从服务器,从服务器载入该 RDB 文件
4.主服务器发送生成 RDB 文件期间的写命令给从服务器,从服务器重放这些命令。此时从服务器状态和主服务器状态一致,同步操作完成
阶段二:命令传播
在同步操作完成之后,后续主服务器执行的写命令会以命令传播的方式发送给从服务器,从而保证主从数据库状态一致
缺陷
当初次复制时,旧版复制功能没有任何问题。但是当因为网络问题,主从服务器短暂掉线重连之后,此时依然会触发复制。虽然这种方式可以让主从数据库状态重新一致,但是性能和效率都会很低。因为重连之后的复制,仍然要执行同步操作,生成完整的 RDB 文件,从服务器依然要载入该 RDB 文件。实际上,在掉线期间,只是丢失了少量写命令,此时执行完整的复制操作,显然效率太低了
2.8版本开始
1.从节点发起psync同步
如果从节点之前没有复制过任何主节点,或者之前执行过slaveof no one命令,从节点就会向主节点发送psync命令,请求主节点进行数据的全量同步
如果前面从节点已经同步过部分数据,此时从节点就会发送psync {runid} {offset}命令给主节点,其中runid是上一次主节点的运行ID,offset是当前从节点的复制偏移量
2.主节点收到psync命令后,会出现以下三种可能
主节点返回 fullresync {runid} {offset}回复,表示主节点要求与从节点进行数据的完整全量复制,其中runid表示主节点的运行ID,offset表示当前主节点的复制偏移量
如果主服务器返回 +continue,表示主节点与从节点会进行部分数据的同步操作,将主服务器缺失的数据复制过来即可
如果主服务器返回 -err,表示主服务器的Redis版本低于2.8,无法识别psync命令,此时从服务器会向主服务器发送sync命令,进行完整的数据全量复制
全量复制
1.从节点判断无法进行部分复制,向主节点发送全量复制的请求;或从节点发送部分复制的请求,但主节点判断无法进行部分复制
2.主节点收到全量复制的命令后,执行bgsave,在后台生成RDB文件,并使用一个缓冲区(称为复制缓冲区)记录从现在开始执行的所有写命令
3.主节点的bgsave执行完成后,将RDB文件发送给从节点;从节点首先清除自己的旧数据,然后载入接收的RDB文件,将数据库状态更新至主节点执行bgsave时的数据库状态
4.主节点将前述复制缓冲区中的所有写命令发送给从节点,从节点执行这些写命令,将数据库状态更新至主节点的最新状态
5.如果从节点开启了AOF,则会触发bgrewriteaof的执行,从而保证AOF文件更新至主节点的最新状态
部分复制
1.复制偏移量
主节点和从节点分别维护一个复制偏移量(offset),代表的是主节点向从节点传递的字节数;主节点每次向从节点传播N个字节数据时,主节点的offset增加N;从节点每次收到主节点传来的N个字节数据时,从节点的offset增加N
offset用于判断主从节点的数据库状态是否一致:如果二者offset相同,则一致;如果offset不同,则不一致,此时可以根据两个offset找出从节点缺少的那部分数据
例如,如果主节点的offset是1000,而从节点的offset是500,那么部分复制就需要将offset为501-1000的数据传递给从节点。而offset为501-1000的数据存储的位置,就是下面要介绍的复制积压缓冲区
2.复制积压缓冲区
准备知识:复制积压缓冲区是由主节点维护的、固定长度的、先进先出(FIFO)队列,默认大小1MB;当主节点开始有从节点时创建,其作用是备份主节点最近发送给从节点的数据。注意,无论主节点有一个还是多个从节点,都只需要一个复制积压缓冲区
准备知识:在命令传播阶段,主节点除了将写命令发送给从节点,还会发送一份给复制积压缓冲区,作为写命令的备份;除了存储写命令,复制积压缓冲区中还存储了其中的每个字节对应的复制偏移量(offset)。由于复制积压缓冲区定长且是先进先出,所以它保存的是主节点最近执行的写命令;时间较早的写命令会被挤出缓冲区
由于该缓冲区长度固定且有限,因此可以备份的写命令也有限,当主从节点offset的差距过大超过缓冲区长度时,将无法执行部分复制,只能执行全量复制。反过来说,为了提高网络中断时部分复制执行的概率,可以根据需要增大复制积压缓冲区的大小(通过配置repl-backlog-size);例如如果网络中断的平均时间是60s,而主节点平均每秒产生的写命令(特定协议格式)所占的字节数为100KB,则复制积压缓冲区的平均需求为6MB,保险起见,可以设置为12MB,来保证绝大多数断线情况都可以使用部分复制
从节点将offset发送给主节点后,主节点根据offset和缓冲区大小决定能否执行部分复制
如果offset偏移量之后的数据,仍然都在复制积压缓冲区里,则执行部分复制
如果offset偏移量之后的数据已不在复制积压缓冲区中(数据已被挤出),则执行全量复制
3.服务器运行ID(runid)
准备知识:每个Redis节点(无论主从),在启动时都会自动生成一个随机ID(每次启动都不一样),由40个随机的十六进制字符组成;runid用来唯一识别一个Redis节点
主从节点初次复制时,主节点将自己的runid发送给从节点,从节点将这个runid保存起来;当断线重连时,从节点会将这个runid发送给主节点;主节点根据runid判断能否进行部分复制
如果从节点保存的runid与主节点现在的runid相同,说明主从节点之前同步过,主节点会继续尝试使用部分复制(到底能不能部分复制还要看offset和复制积压缓冲区的情况)
如果从节点保存的runid与主节点现在的runid不同,说明从节点在断线前同步的Redis节点并不是当前的主节点,只能进行全量复制
持久化
RDB(将当前数据保存到硬盘,相当于快照)
触发条件
手动触发
执行命令save
会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在Redis服务器阻塞期间,服务器不能处理任何命令请求
执行命令bgsave
会创建一个子进程,由子进程来负责创建RDB文件,父进程(即Redis主进程)则继续处理请求
bgsave命令执行过程中,只有fork子进程时会阻塞服务器,而对于save命令,整个过程都会阻塞服务器,因此save已基本被废弃,线上环境要杜绝save的使用
自动触发
在配置文件中通过save m n配置,指定当m秒内发生n次变化时,会触发bgsave
save m n的原理如下:每隔100ms,执行serverCron函数;在serverCron函数中,遍历save m n配置的保存条件,只要有一个条件满足,就进行bgsave
其他自动触发机制
主从复制场景下,如果从节点执行全量复制操作,则主节点会执行bgsave命令,并将rdb文件发送给从节点
执行shutdown命令时,自动执行rdb持久化
压缩
Redis默认采用LZF算法对RDB文件进行压缩
RDB文件的压缩并不是针对整个文件进行的,而是对数据库中的字符串进行的,且只有在字符串达到一定长度(20字节)时才会进行
AOF(将每次执行的写命令保存到硬盘(类似于MySQL的binlog))
执行流程
命令追加(append)
将Redis的写命令追加到缓冲区aof_buf
Redis先将写命令追加到缓冲区,而不是直接写入文件,主要是为了避免每次有写命令都直接写入硬盘,导致硬盘IO成为Redis负载的瓶颈
命令追加的格式是Redis命令请求的协议格式,它是一种纯文本格式,具有兼容性好、可读性强、容易处理、操作简单避免二次开销等优点
文件写入(write)和文件同步(sync)
根据不同的同步策略将aof_buf中的内容同步到硬盘
AOF缓存刷盘策略
always
命令写入aof_buf后立即调用系统fsync操作同步到AOF文件,fsync完成后线程返回。这种情况下,每次有写命令都要同步到AOF文件,硬盘IO成为性能瓶颈,Redis只能支持大约几百TPS写入,严重降低了Redis的性能;即便是使用固态硬盘(SSD),每秒大约也只能处理几万个命令,而且会大大降低SSD的寿命
no
命令写入aof_buf后调用系统write操作,不对AOF文件做fsync同步;同步由操作系统负责,通常同步周期为30秒。这种情况下,文件同步的时间不可控,且缓冲区中堆积的数据会很多,数据安全性无法保证
everysec
命令写入aof_buf后调用系统write操作,write完成后线程返回;fsync同步文件操作由专门的线程每秒调用一次。everysec是前述两种策略的折中,是性能和数据安全性的平衡,因此是Redis的默认配置,也是我们推荐的配置
AOF文件重写(rewrite)
定期重写AOF文件,达到压缩的目的。AOF重写是把Redis进程内的数据转化为写命令,同步到新的AOF文件;不会对旧的AOF文件进行任何读取、写入操作
触发行为
自动触发
根据auto-aof-rewrite-min-size和auto-aof-rewrite-percentage参数,以及aof_current_size和aof_base_size状态确定触发时机
手动触发
直接调用bgrewriteaof命令,该命令的执行与bgsave有些类似:都是fork子进程进行具体的工作,且都只有在fork时阻塞
RDB和AOF优缺点对比
RDB
优点
RDB文件紧凑,体积小,网络传输快,适合全量复制;恢复速度比AOF快很多。当然,与AOF相比,RDB最重要的优点之一是对性能的影响相对较小
缺点
RDB文件的致命缺点在于其数据快照的持久化方式决定了必然做不到实时持久化,而在数据越来越重要的今天,数据的大量丢失很多时候是无法接受的,因此AOF持久化成为主流。此外,RDB文件需要满足特定格式,兼容性差(如老版本的Redis不兼容新版本的RDB文件)
AOF
优点
秒级持久化、兼容性好
缺点
文件大、恢复速度慢、对性能影响大
监控指标
性能监控(Performance)
latency
Redis响应一个请求的时间
instantaneous_ops_per_sec
平均每秒处理请求总数
hit rate(calculated)
缓存命中率(计算出来的)
内存指标(Memory)
used_memory
已使用内存
mem_fragmentation_ratio
内存碎片率
evicted_keys
由于最大内存限制被移除的key的数量
blocked_clients
由于BLPOP,BRPOP,or BRPOPLPUSH而备阻塞的客户端
基本活动指标(Basic activity)
connected_clients
客户端连接数
conected_laves
slave数量
master_last_io_seconds_ago
最近一次主从交互之后的秒数
keyspace
数据库中的key值总数
持久性指标(Persistence)
rdb_last_save_time
最后一次持久化保存磁盘的时间戳
rdb_changes_sice_last_save
自最后一次持久化以来数据库的更改数
错误指标(Error)
rejected_connections
由于达到maxclient限制而被拒绝的连接数
keyspace_misses
key值查找失败(没有命中)次数
master_link_down_since_seconds
主从断开的持续时间(以秒为单位)