目录
[一.Redis 持久化](#一.Redis 持久化)
[(3) 执行流程](#(3) 执行流程)
[二.Redis 性能管理](#二.Redis 性能管理)
[三.Redis 的三大缓存问题](#三.Redis 的三大缓存问题)
[(1) 缓存穿透解决方案](#(1) 缓存穿透解决方案)
一.Redis 持久化
1.持久化概述
Redis 是运行在内存中,内存中的数据断电丢失,为了能够重用Redis数据,或者防止系统故障、需要定期将Redis中的数据以某种形式(数据或命令)从内存保存到硬盘,当下次Redis重启时,利用持久化文件实现数据恢复,即持久化。
2.持久化分类
- RDB 方式:创建快照的方式获取某一时刻Redis中所有数据的副本定时保存在磁盘上
- AOF方式:将执行的写命令写到文件的末尾,以日志的方式记录数据的变化
3.RDB和AOF持久化
1.RDB持久化
RDB持久化是指在指定的时间间隔内将内存中当前进程中的数据生成快照保存到硬盘(因此也称作快照持久化),用二进制压缩存储 ,保存的文件后缀是rdb;当Redis重新启动时,可以读取快照文件恢复数据。
2.RDB触发条件
(1)手动触发
save命令和bgsave命令都可以生成RDB文件。
save命令会阻塞Redis服务器进程,直到RDB文件创建完毕为止,在Redis服务器阻塞期间,服务 器不能处理任何命令请求。
bgsave命令会创建一个子进程,由子进程来负责创建RDB文件,父进程(即Redis主进程)则继续处理请求。
bgsave命令执行过程中,只有fork子进程时会阻塞服务器,而对于save命令,整个过程都会阻塞服务器,因此save已基本被废弃,线上环境要杜绝save的使用。
(2)自动触发
在自动触发RDB持久化时,Redis也会选择bgsave而不是save来进行持久化。
save m n
自动触发最常见的情况是在配置文件中通过save m n,指定当m秒内发生n次变化时,会触发bgsave进行快照。
cpp
vim /usr/local/redis/conf/redis.conf
--433行--RDB默认保存策略
# save 3600 1 300 100 60 10000
#表示以下三个save条件满足任意一个时,都会引起bgsave的调用
save 3600 1 :当时间到3600秒时,如果redis数据发生了至少1次变化,则执行bgsave
save 300 10 :当时间到300秒时,如果redis数据发生了至少10次变化,则执行bgsave
save 60 10000 :当时间到60秒时,如果redis数据发生了至少10000次变化,则执行bgsave
--454行--是否开启RDB文件压缩
rdbcompression yes
--481行--指定RDB文件名
dbfilename dump.rdb
--504行--指定RDB文件和AOF文件所在目录
dir /usr/local/redis/data
注意其他自动触发机制 :
- 在主从复制场景下,如果从节点执行全量复制操作,则主节点会执行bgsave命令,并将rdb文件发送给从节点。
- 执行shutdown命令时,自动执行rdb持久化。
(3) 执行流程
- Redis父进程首先判断:当前是否在执行save,或bgsave/bgrewriteaof的子进程,如果在执行则bgsave命令直接返回。 bgsave/bgrewriteaof的子进程不能同时执行,主要是基于性能方面的考虑:两个并发的子进程同时执行大量的磁盘写操作,可能引起严重的性能问题。
- 父进程执行fork操作创建子进程,这个过程中父进程是阻塞的,Redis不能执行来自客户端的任何命令
- 父进程fork后,bgsave命令返回"Background saving started"信息并不再阻塞父进程,并可以响应其他命令
- 子进程创建RDB文件,根据父进程内存快照生成临时快照文件,完成后对原有文件进行原子替换
- 子进程发送信号给父进程表示完成,父进程更新统计信息
(4)启动时加载
RDB文件的载入工作是在服务器启动时自动执行的,并没有专门的命令。但是由于AOF的优先级更高,因此当AOF开启时,Redis会优先载入 AOF文件来恢复数据;只有当AOF关闭时,才会在Redis服务器启动时检测RDB文件,并自动载入。服务器载入RDB文件期间处于阻塞状态,直到载入完成为止。
Redis载入RDB文件时,会对RDB文件进行校验,如果文件损坏,则日志中会打印错误,Redis启动失败。
3.AOF持久化
RDB持久化是将进程数据写入文件,而AOF持久化,则是将Redis执行的每次写、删除命令记录到单独的日志文件中,查询操作不会记录; 当Redis重启时再次执行AOF文件中的命令来恢复数据。
与RDB相比,AOF的实时性更好,因此已成为主流的持久化方案。
(1)开启AOF
cpp
Redis服务器默认开启RDB,关闭AOF;要开启AOF,需要在配置文件中配置:
vim /usr/local/redis/conf/redis.conf
--1380行--修改,开启AOF
appendonly yes
--1407行--指定AOF文件名称
appendfilename "appendonly.aof"
--1505行--是否忽略最后一条可能存在问题的指令
aof-load-truncated yes
systemctl restart redis-server.service
(2)执行流程
由于需要记录Redis的每条写命令,因此AOF不需要触发,下面介绍AOF的执行流程。
AOF的执行流程包括:
●命令追加(append):将Redis的写命令追加到缓冲区aof_buf;
●文件写入(write)和文件同步(sync):根据不同的同步策略默认为everysec(每秒进行一次同步)将 aof_buf中的内容同步到硬盘;
bash
vim /usr/local/redis/conf/redis.conf
--1439--
●appendfsync always: 命令写入aof_buf后立即调用系统fsync操作同步到AOF文件,
fsync完成后线程返回。这种情况下,每次有写命令都要同步到AOF文件,硬盘IO成为性能瓶颈,
Redis只能支持大约几百TPS写入,严重降低了Redis的性能;即便是使用固态硬盘(SSD),
每秒大约也只能处理几万个命令,而且会大大降低SSD的寿命。
●appendfsync no: 命令写入aof_buf后调用系统write操作,不对AOF文件做fsync同步;
同步由操作系统负责,通常同步周期为30秒。这种情况下,文件同步的时间不可控,
且缓冲区中堆积的数据会很多,数据安全性无法保证。
●appendfsync everysec: 命令写入aof_buf后调用系统write操作,write完成后线程返回;
fsync同步文件操作由专门的线程每秒调用一次。everysec是前述两种策略的折中,
是性能和数据安全性的平衡,因此是Redis的默认配置,也是我们推荐的配置。
●文件重写(rewrite):减少aof文件的占用空间和加快恢复速度,可以配合crontab定时执行bgrewriteaof命令触发。
注意:文件重写能够压缩AOF文件原因:过期的数据不再写入文件、无效的命令不再写入文件、多条命令可以合并为一个,通过上述内容可以看出,由于重写后AOF执行的命令减少了,文件重写既可以减少文件占用的空间,也可以加快恢复速度
(3)启动时加载
当AOF开启时,Redis启动时会优先载入AOF文件来恢复数据;只有当AOF关闭时,才会载入RDB文件恢复数据。
当AOF开启,但AOF文件不存在时,即使RDB文件存在也不会加载。
Redis载入AOF文件时,会对AOF文件进行校验,如果文件损坏,则日志中会打印错误,Redis启动失败。但如果是AOF文件结尾不完整(机器突然宕机等容易导致文件尾部不完整),且aof-load-truncated参数开启,则日志中会输出警告,Redis忽略掉AOF文件的尾部,启动成功。aof-load-truncated参数默认是开启的。
4.RDB和AOF的优缺点和区别(重点!)
1.RDB
优点:RDB持久化保存的文件占用空间较小,网络传输较快,恢复速度比AOF更快,性能影响比AOF更小
缺点:实时性不如AOF,兼容性较差,持久化期间在fork子进程时会阻塞redis主进程,影响客户端命令的响应
2.AOF
优点:实时性比RDB更好,支持秒级持久化,兼容性较好
缺点:持久化文件占用空间较大,恢复速度较慢,性能影响更大,AOF文件重写期间在fork子进程时会阻塞redis主进程,且磁盘IO压力更大
3.区别
从 工作方式、实时性、占用空间、恢复速度、兼容性、磁盘IO性能影响描述:
bash
答案:
RDB是定时的将redis在内存的数据进行快照并压缩保存到硬盘中,
而AOF是实时的以追加的方式将redis写操作命令记录到aof文件中,
AOF的实时性比RDB更好,所以AOF保存数据更安全、兼容性也比RDB更好;
因为RDB是快照压缩保存在硬盘中的,所以占用空间会比AOF小,
网络传输速度和恢复速度都比AOF更快,因为RDB持久化期间在fork子进程时会阻塞redis主进程,
而AOF在重写期间在fork子进程会阻塞主进程,所以RDB的IO性能比AOF更小一些。
二.Redis 性能管理
1.查看Redis内存使用
cs
----- 内存碎片率 -----
mem_fragmentation_ratio:内存碎片率。
mem_fragmentation_ratio = used_memory_rss / used_memory
used_memory_rss:是Redis向操作系统申请的内存。
used_memory:是Redis中的数据占用的内存。
used_memory_peak:redis内存使用的峰值。
2.跟踪内存碎片率
cs
●内存碎片率在1到1.5之间是正常的,这个值表示内存碎片率比较低,也说明 Redis 没有发生内存交换。
●内存碎片率超过1.5,说明Redis消耗了实际需要物理内存的150%,其中50%是内存碎片率。
●内存碎片率低于1的,说明Redis内存分配超出了物理内存,操作系统正在进行内存交换。
需要增加可用物理内存或减少 Redis内存占用。
3.解决碎片率大的问题
cs
#解决碎片率大的问题:
如果你的Redis版本是4.0以下的,需要在 redis-cli 工具上输入 shutdown save 命令,
让 Redis 数据库执行保存操作并关闭 Redis 服务,再重启服务器。
Redis服务器重启后,Redis会将没用的内存归还给操作系统,碎片率会降下来。
Redis4.0版本开始,可以在不重启的情况下,线上整理内存碎片。
config set activedefrag yes #自动碎片清理,内存就会自动清理了。
memory purge #手动碎片清理
4.内存使用率
redis实例的内存使用率超过可用最大内存,操作系统将开始进行内存与swap空间交换。
bash
查看Redis内存是否发生Swap
redis-cli info | grep process_id #查看redis进程号
process_id: 5332 #ps aux | grep redis
#ss -lntp |grep redis
cat /proc/5332/smaps | egrep '^(Swap|Size)'
#发生内存 swap 的解决方法:
●设置key的过期时间
●增加 Redis 集群的实例个数,来分摊每个实例服务的数据量,进而减少每个实例所需的内存量
5.内回收key
cpp
----- 内回收key -----
内存数据淘汰策略,保证合理分配redis有限的内存资源。
当达到设置的最大阀值时,需选择一种key的回收策略,默认情况下回收策略是禁止删除。
配置文件中修改 maxmemory-policy 属性值:
vim /usr/local/redis/conf/redis.conf
--1149--
maxmemory-policy noenviction
●allkeys-lru:不管 key 是否设置了过期,淘汰最近最少访问的 key
●volatile-lru:只淘汰最近最少访问、并设置了过期时间的 key
●allkeys-random:不管 key 是否设置了过期,随机淘汰 key
●volatile-random:只随机淘汰设置了过期时间的 key
●volatile-ttl:不管 key 是否设置了过期,淘汰即将过期的 key
●no-eviction:不淘汰任何 key,实例内存达到 maxmeory 后,再写入新数据直接返回错误
●allkeys-lfu:不管 key 是否设置了过期,淘汰访问频率最低的 key(4.0+版本支持)
●volatile-lfu:只淘汰访问频率最低、并设置了过期时间 key(4.0+版本支持)
6.Redis性能优化(重点!!)
1)设置内存上限(maxmemory),并设置内存数据淘汰策略(maxmemory-policy),采用lru算 法淘汰最近最少使用的key
2)给key设置合理的过期时间,尽量避免大量key集中过期
3)合理规划键值的长度,避免存储大键bigkey导致操作延迟(string类型的建议大小在20KB以内。hash、list、set和zset建议控制好阈值,推荐控制元素数量在5000以内)
4)开启自动内存碎片清理(activedefrag yes)
5)开启AOF持久化保证数据安全(实时性好),设置AOF文件同步策略appendfsync everysec(秒级同步);开启混合持久化 aof-use-rdb-preamble yes
6)开启lazy free机制(lazyfree-lazy-eviction、lazyfree-lazy-expire、lazyfree-lazy-server-del yes),将删除过期key的操作放到后台线程执行,以减少删除对redis主进程的阻塞
7)尽量使用物理机而非虚拟机部署redis服务,使用高速固态硬盘作为AOF日志的写入盘
8)使用分布式架构(主从复制、哨兵、集群)实现读写分离、分散bigkey来提升读写速度,并实现高可用
9)禁用内存大页 echo never > /sys/kernel/mm/transparent_hugepage/enabled ,因为开启内存大页会导致fork子进程变慢,大幅增加重写期间父进程内存消耗,还会拖慢写操作的执行时间
三.Redis 的三大缓存问题
前言:正常情况下,大部分的访问请求应该是先被redis缓存命中响应,在redis那里得不到响应的小部分访问才会去请求MySQL数据库获取数据,这样MySQL数据库的负载压力会非常小,能够正常稳定工作。
注意:缓存雪崩/穿透/击穿问题的根本原因是在于redis缓存命中率下降,大量请求会直接发送给MySQL数据库,导致数据库负载压力过大而可能崩溃。
1.缓存雪崩
定义:缓存雪崩是指在短时间内,有大量缓存同时过期,导致大量的请求直接查询数据库,从而对数据库造成了巨大的压力,严重情况下可能会导致数据库宕机的情况叫做缓存雪崩。
(1)缓存雪崩解决方案
- 使用随机数设置缓存key的过期时间,避免缓存集中过期
- 设置二级缓存,除了redis本身的缓存,再设置一层缓存,当redis失效之后,先去查询二级缓存
- 在应用代码中使用互斥锁实现加锁排队,缓冲大量请求,防止大量的请求同时操作数据库(思路:当缓存未查询到时,对要请求的key进行加锁,只允许一个线程去数据库中查,其他线程等候排队)
2.缓存穿透
**定义:大量请求访问redis和数据库都不存在数据,因为数据库查询无数据,出于容错考虑,不会将结果保存到缓存中,因此每次请求都会去查询数据库,**导致最后大量的请求还是直接查询数据库,这种情况就叫做缓存穿透。
(1) 缓存穿透解决方案
- 使用布隆过滤器过滤掉一定不存在的无效请求,从而避免了无效请求给数据库带来的查询压力
- 对空值数据也进行缓存
3.缓存击穿
定义: redis中的热点缓存过期,导致缓存失效,此时又有大量请求访问这个热点数据,导致大量的请求直接查询数据库,此时这些请求会给数据库造成巨大的压力,这种情况就叫做缓存击穿。
(1)缓存击穿解决方案
- 对热点缓存不设置过期时间,保证缓存的稳定性
- 预先对热点数据进行缓存预热
- 在应用代码中使用互斥锁实现加锁排队,缓冲大量请求,防止大量的请求同时操作数据库