絮叨
逍遥天境 以天道为武力,一刀一剑有万物与之呼应。
这是进阶篇哦,如果要去看基础的话建议看我下面的链接:
第一篇基础的概率很多 估计大家看得想睡觉 第二篇的干货确实很多 值得深刻研究 这篇的干货也不少的
这篇主要写redis的淘汰算法 持久化
Redis的淘汰策略
如果你的 Redis 只能存8G数据,你写了11G,那么 Redis 会怎么淘汰那3G数据呢? redis.conf 中的过期淘汰配置 看下源码的配置
# MAXMEMORY POLICY: how Redis will select what to remove when maxmemory # is reached. You can select among five behaviors: #最大内存策略:当到达最大使用内存时,你可以在下面5种行为中选择,Redis如何选择淘汰数据库键 #当内存不足以容纳新写入数据时 # volatile-lru -> remove the key with an expire set using an LRU algorithm # volatile-lru :在设置了过期时间的键空间中,移除最近最少使用的key。这种情况一般是把 redis 既当缓存,又做持久化存储的时候才用。 # allkeys-lru -> remove any key according to the LRU algorithm # allkeys-lru : 移除最近最少使用的key (推荐) # volatile-random -> remove a random key with an expire set # volatile-random : 在设置了过期时间的键空间中,随机移除一个键,不推荐 # allkeys-random -> remove a random key, any key # allkeys-random : 直接在键空间中随机移除一个键,弄啥叻 # volatile-ttl -> remove the key with the nearest expire time (minor TTL) # volatile-ttl : 在设置了过期时间的键空间中,有更早过期时间的key优先移除 不推荐 # noeviction -> don't expire at all, just return an error on write operations # noeviction : 不做过键处理,只返回一个写操作错误。 不推荐 # Note: with any of the above policies, Redis will return an error on write # operations, when there are no suitable keys for eviction. # 上面所有的策略下,在没有合适的淘汰删除的键时,执行写操作时,Redis 会返回一个错误。下面是写入命令: # At the date of writing these commands are: set setnx setex append # incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd # sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby # zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby # getset mset msetnx exec sort # 过期策略默认是: # The default is: # maxmemory-policy noeviction 复制代码
- noeviction:当内存不足以容纳新写入数据时,新写入操作会报错。
- allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的key。
- allkeys-random:当内存不足以容纳新写入数据时,在键空间中,随机移除某个key。
- volatile-lru:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,移除最近最少使用的key。
- volatile-random:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,随机移除某个key。
- volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的key优先移除。
其实我觉得用volatile-lru就好了 毕竟报错是完全没有必要的 还有就是设置一个报警装置 如果不够了 就搞主从 哈哈
Redis的持久化方式
Redis为持久化提供了两种方式:
- RDB:在指定的时间间隔能对你的数据进行快照存储。
- AOF:记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据。
为了使用持久化的功能,我们需要先知道该如何开启持久化的功能。在redis.conf 中有下面的配置:
RDB的配置
# 时间策略 save 900 1 save 300 10 save 60 10000 # 文件名称 dbfilename dump.rdb # 文件保存路径 dir /home/work/app/redis/data/ # 如果持久化出错,主进程是否停止写入 stop-writes-on-bgsave-error yes # 是否压缩 rdbcompression yes # 导入时是否检查 rdbchecksum yes 复制代码
save 900 1 表示900s内如果有1条是写入命令,就触发产生一次快照,可以理解为就进行一次备份 save 300 10 表示300s内有10条写入,就产生快照
下面的类似,那么为什么需要配置这么多条规则呢?因为Redis每个时段的读写请求肯定不是均衡的,为了平衡性能与数据安全,我们可以自由定制什么情况下触发备份。所以这里就是根据自身Redis写入情况来进行合理配置。
stop-writes-on-bgsave-error yes
这个配置也是非常重要的一项配置,这是当备份进程出错时,主进程就停止接受新的写入操作,是为了保护持久化的数据一致性问题。如果自己的业务有完善的监控系统,可以禁止此项配置, 否则请开启。
关于压缩的配置 rdbcompression yes ,建议没有必要开启,毕竟Redis本身就属于CPU密集型服务器,再开启压缩会带来更多的CPU消耗,相比硬盘成本,CPU更值钱。
当然如果你想要禁用RDB配置,也是非常容易的,只需要在save的最后一行写上:save "", 默认是开启的。
RDB的原理
在Redis中RDB持久化的触发分为两种:自己手动触发与Redis定时触发。
针对RDB方式的持久化,手动触发可以使用:
- save:会阻塞当前Redis服务器,直到持久化完成,线上应该禁止使用。 - bgsave:该触发方式会fork一个子进程,由子进程负责持久化过程,因此阻塞只会发生在fork子进程的时候。 复制代码
而自动触发的场景主要是有以下几点:
- 根据我们的 save m n 配置规则自动触发; - 从节点全量复制时,主节点发送rdb文件给从节点完成复制操作,主节点会触发 bgsave; - 执行 shutdown时,如果没有开启aof,也会触发。 复制代码
由于 save 基本不会被使用到,我们重点看看 bgsave 这个命令是如何完成RDB的持久化的。
这里注意的是 fork 操作会阻塞,导致Redis读写性能下降。我们可以控制单个Redis实例的最大内存,来尽可能降低Redis在fork时的事件消耗。以及上面提到的自动触发的频率减少fork次数,或者使用手动触发,根据自己的机制来完成持久化。
保存已经会了,接下来我看看如何恢复数据-> 将备份文件复制到Redis的暗安装目录下,然后重新启动服务。
AOF的配置
# 是否开启aof appendonly yes # 文件名称 appendfilename "appendonly.aof" # 同步方式 appendfsync everysec # aof重写期间是否同步 no-appendfsync-on-rewrite no # 重写触发配置 auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb # 加载aof时如果有错如何处理 aof-load-truncated yes # 文件重写策略 aof-rewrite-incremental-fsync yes 复制代码
还是重点解释一些关键的配置:
appendfsync everysec 它其实有三种模式:
- always:把每个写命令都立即同步到aof,很慢,但是很安全
- everysec:每秒同步一次,是折中方案(默认也是这个)
- no:redis不处理交给OS来处理,非常快,但是也最不安全
AOF的原理
AOF的整个流程大体来看可以分为两步,一步是命令的实时写入(如果是 appendfsync everysec 配置,会有1s损耗),第二步是对aof文件的重写。
对于增量追加到文件这一步主要的流程是:命令写入=》追加到aof_buf =》同步到aof磁盘。那么这里为什么要先写入buf在同步到磁盘呢?如果实时写入磁盘会带来非常高的磁盘IO,影响整体性能。
aof重写是为了减少aof文件的大小,可以手动或者自动触发,关于自动触发的规则请看上面配置部分。fork的操作也是发生在重写这一步,也是这里会对主进程产生阻塞。
手动触发: bgrewriteaof,自动触发 就是根据配置规则来触发,当然自动触发的整体时间还跟Redis的定时任务频率有关系。
数据的备份、持久化做完了,我们如何从这些持久化文件中恢复数据呢?如果一台服务器上有既有RDB文件,又有AOF文件,该加载谁呢?
启动时会先检查AOF文件是否存在,如果不存在就尝试加载RDB。那么为什么会优先加载AOF呢?因为AOF保存的数据更完整,通过上面的分析我们知道AOF基本上最多损失1s的数据。
Redis持久化的性能优化
通过上面的分析,我们都知道RDB的快照、AOF的重写都需要fork,这是一个重量级操作,会对Redis造成阻塞。因此为了不影响Redis主进程响应,我们需要尽可能降低阻塞。
- 降低fork的频率,比如可以手动来触发RDB生成快照、与AOF重写; - 控制Redis最大使用内存,防止fork耗时过长; - 使用更牛逼的硬件; - 合理配置Linux的内存分配策略,避免因为物理内存不足导致fork失败。 复制代码
一些线上经验
- 如果Redis中的数据并不是特别敏感或者可以通过其它方式重写生成数据,可以关闭持久化,如果丢失数据可以通过其它途径补回; - 自己制定策略定期检查Redis的情况,然后可以手动触发备份、重写数据; - 可以加入主从机器,利用一台从机器进行备份处理,其它机器正常响应客户端的命令; 复制代码
结尾
redis 今天的内存淘汰策略和持久化就讲这么多,本来我想多写点,但是一篇文章怕大家看得累 就写短点吧
哈哈 后面还有 lua脚本 主从 哨兵 等我们下集再见
因为博主也是一个开发萌新 我也是一边学一边写 我有个目标就是一周 二到三篇 希望能坚持个一年吧 希望各位大佬多提意见,让我多学习,一起进步。