Redis持久化
Redis包含3中持久化方案: RDB, AOF, RDB与AOF混合使用
RDB
RDB: 将内存中数据生成快照形式, 将其保存到.rdb文件中, 关注点是数据
- 使用命令执行RDB过程
在保存.rdb文件之前还需要修改redis.conf配置文件, 修改项如下:
dbfilename dump.rdb //配置.rdb文件名, 通常设置为dump.端口号.rdb
dir /www/server/redis/data //设置rdb文件的存储位置
rdbcompression yes //设置存储到本地时, 是否压缩(默认压缩), 使用LZF压缩
rdbchecksum yes //设置是否在读写.rdb文件时, 进行格式校验, 默认开启, 关闭可节约10%时间, 但存在风险
- 保存指令
save //手动执行保存操作
bgsave //在后台执行保存操作
二者执行过程如下:
- save执行过程
注: redis是单线程, 执行save操作的时候会造成阻塞问题, 会严重影响其性能, 通常不使用.
解决删除上述问题, 使用bgsave指令. - bgsave指令工作原理如下:
bgsave与save的区别:
redis主线程操作save指令; redis生成子进程操作bgsave指令
- 自动执行RDB
在redis.conf配置文件中编写如下配置:
save second changes //在second时间内, 若key发生changes次变化, 那么就进行持久化操作.
配置redis.conf进行rdb操作, 内部使用的还是bgsave指令, 执行流程如下
注: save需要根据实际业务情况进行配置, save执行频率过高或者过低都会造成性能问题. save配置中的second与changes通常设置为互补的关系, 使用debug reload, shutdown指令会自动执行bgsave(如果没有开启AOF持久化功能)
- RDB优缺点
优: 存储效率高, 数据恢复速度快
缺: 服务器宕机时会出现部分数据丢失, 使用save或者bgsave都将会导致性能损失, redis的rdb文件版本不统一.
AOF
AOF: 使用日志的方式记录redis执行的命令, 当出现宕机时, 能尽量避免数据丢失的情况, 很好解决了RDB的问题, 但是AOF存在数据恢复较慢的问题.
- AOF执行步骤
- AOF写数据的3中策略(AOF写数据也是使用fork处理, 与bgsave一样)
always: 每次的写操作命令都同步到AOF文件中, 数据误差零, 但性能低下.
everysec: 每秒将命令缓存区中内容同步到.aof文件中, 会丢失1s的数据, 准确性较高, 性能较好, 默认配置.
no: 使用系统控制, 整体过程不可控.
- AOF相关配置
appendonly yes|no //yes代表aof功能开启, 默认不开启
appendfsync always|everysec|no //设置AOF写数据策略
appendfilename fileName //设置.aof文件名
dir path //.aof文件保存位置
- AOF重写
AOF重写: 对同一个数据操作的指令压缩为一条指令, 解决.aof文件体积过大, 增加磁盘利用率, 提高IO性能.
例: set name A; set name B; set name C; ===> set name C; - AOF重写方式
//输入命令进行手动重写
bgrewriteaof//配置redis.conf进行自动重写
auto-aof-rewrite-min-size size //当前缓存区中指令数大于最小值, 就重写
auto-aof-rewrite-percentage percent //当尺寸的比例, 大于指令值就重写
注: AOF手动重写过程调用fork函数, 生成子进程, 子进程进行.aof重写, 流程与bgsave一样.
- AOF工作流程:
- AOF重写流程:
注:
AOF缓冲区同步策略由appendfsync控制.
系统调用write和fsync说明:
write操作会触发延迟写机制, Linux在内核提供页缓冲区用来提高IO性能, write操作在写入系统缓冲区后直接返回, 当出现宕机的情况会导致缓冲区内容丢失.
fsync对单个文件操作, 使用强制磁盘同步.保证数据持久化
RDB与AOF区别
持久化方式 | RDB | AOF |
---|---|---|
占用存储空间 | 小(数据压缩) | 大(数据重写) |
存储速度 | 慢 | 快 |
恢复速度 | 快 | 慢 |
数据安全性 | 存在数据丢失 | 依赖于写策略 |
资源消耗 | 高/重量级 | 低/轻量级 |
启动优先级 | 低 | 高 |
RDB与AOF的选择
数据敏感: 使用AOF(.aof文件较大, 数据恢复速度慢)
数据呈阶段性有效: 使用RDB(对数据丢失不敏感, 数据恢复速度快)
综合比对:
- RDB与AOF的选择实际上是在做一种权衡,每种都有利有弊
- 如不能承受数分钟以内的数据丢失,对业务数据非常敏感,选用AOF
- 如能承受数分钟以内的数据丢失,且追求大数据集的恢复速度,选用RDB
- 灾难恢复选用RDB
- 双保险策略,同时开启 RDB 和 AOF,重启后,Redis优先使用 AOF 来恢复数据,降低丢失数据的量
持久化应用场景
Tips 1:redis 应用于抢购,限购类、限量发放优惠卷、激活码等业务的数据存储设计
Tips 2:redis 应用于具有操作先后顺序的数据控制
Tips 3:redis 应用于最新消息展示
Tips 4:redis 应用于基于黑名单与白名单设定的服务控制
Tips 5:redis 应用于计数器组合排序功能对应的排名
Tips 6:redis 应用于即时任务/消息队列执行管理
Redis事务
多个redis-cli操作一个redis-server就会存在事务问题.
- 事务基本操作
multi //开启事务
exec //执行事务
discard //取消事务, 终止当前事务, 在multi与exec中间发生
注: multi必须与exec成对出现, 当开启事务时, 加入事务的命令都进入任务队列中, 当执行exec指令时, 才将任务队列中的指令取出进行执行.
注: 若命令队列中的命令书写错误, 则队列中所有的指令都不会执行, 若队列中的命令书写正确但是运行错误(例如: set name A; name不存在), 正确的命令都会执行, 错误的命令不会执行, 已经执行完的命令不会自动回滚, 需要在代码中手动实现.
- 手动回滚事务
记录操作之前数据状态, 设置修改的值可以进行恢复.
- 锁
在执行事务时为共享资源加锁, 防止其他redis-cli对共享资源修改.
watch key1 [key2...] //对key加锁, 在一个事务内, 其他redis-cli对key做修改, 则当前事务无效
unwatch //取消对所有key的监视
事务执行成功:
事务执行失败, 在该事务下, 使用另外一个redis-cli修改list中的值, 当该事务exec, 出现失败.
- 监控具体的数据是否被修改(watch只能监控key对应value的变化, 不能监控具体的数据)
setnx lock lockValue//设置lock(可以将这里的lock理解为就是string), 如果lock已经存在, 则返回0
del lock //删除lock
当1号客户端设置lock时, 2号客户端对尝试修改lock, 但是修改失败, 说明该lock已经存在, 此时2号客户端停止操作.
注: 利用setnx的返回值, 对于返回设置成功的, 拥有控制权, 进行下一步具体业务操作, 对于返回失败的, 不具有控制权, 进行排队或等待.当拥有控制的客户端执行完所有操作后, 使用del删除lock, 此时另外的客户端就拥有了资源的控制权.
- 使用expire为lock添加时间期限, 防止setnx后, 服务器宕机, lock一直存在, 其他客户端无法获取lock进行操作
expire lock seconds //为setnx的锁添加seconds时间限定, 超出该时间后, 自动放弃锁
pexpire lock milliseconds //同上, 添加milliseconds时间
注: 通常操作都是毫秒级别, 不应将锁定时间设置过大, 锁定时间设置=>最大耗时120%+平均网络延迟110%
Redis删除策略
- redis 所有数据放置在内存中, 可使用TTL命令获取数据状态
XX: 具有有效性的数据
-1: 永久有效数据
-2: 已经过期, 删除, 未定义的数据
- redis expires数据分布结构:
- 数据删除策略如下:
定时删除:数据到达过期时间时, 定时器启动删除.优: 节省内存; 缺: CPU压力较大, 影响吞吐量拿性能换空间惰性删除:数据到达过期时间, 不进行处理, 等再次访问该数据时再删除优: 节约CPU; 缺: 内存压力大拿空间换性能.定期删除(定时删除与惰性删除的折中方案)周期轮询redis库中时效性数据, 采用随机抽取, 利用过期数据占比控制删除频率.优: 修改配置文件数据, 可做到性能与内存的最优.周期性抽查内存空间
- 定期删除
- activeExpireCycle()函数对每个db的expires空间进行检查, 每次执行250ms/server.hz
- 检查一个expires时, 随机挑选w个key(w = ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP属性)
- 若key过期, 则删除key
- 若一轮中删除的key数量>=w*25%, 则循环该过程
- 若一轮中删除的key数量<=w*25%, 则进入下一个expires空间
- 使用current_db记录当前activeExpireCycle()进入哪一个expires空间
- 当activeExpireCycle()执行时间到了, 下一次的activeExpireCycle执行就从current_db开始执行
逐出算法
逐出算法: redis执行每一个命令之前, 都调用freeMemoryIfNeeded()进行内存检测, 判断是否充足, 内存不足时, 将临时删除一些数据, 然后保存新生成的数据.
逐出算法不能100%成功, 不成功则反复执行, 对所有数据进行尝试后, 若不能达到清除的要求, 则输出OOM错误.
- 逐出算法相关配置
maxmemory //最大可使用内存, 默认为0, 不限制, 通常设置该值为50%以上
maxmemory-samples //选取待删除数据的个数, 使用随机抽取, 而不是全库扫描, 提高性能
maxmemory-policy //达到最大内存后, 对挑选出来的数据进行删除检测易失数据:volatile-lru: 挑选最近最少使用的数据淘汰volatile-lfu: 挑选最近使用次数最少的数据淘汰volatile-ttl: 挑选将要过期的数据淘汰检测全库数据:allkeys-lru: 挑选最近最少使用的数据淘汰allkeys-lfu: 挑选最近使用次数最少的数据淘汰all-random: 任意数据淘汰放弃数据驱逐no-enviction: 放弃删除, 会导致OOM
使用info命令, 查看控制信息, 然后配置逐出策略