目录
redis的持久化机制
1.redis为什么需要持久化
redis本身运行时数据保存在内存中,那么在关闭redis的进程或者关闭计算机后数据肯定被会操作系统从内存中清掉。
redis持久化方式有两种:
RDB
AOF
redis默认采用了一种持久化方式,即RDB (Redis DataBase)
2. redis的RDB持久化方式
1. 什么是RDB
在指定的时间间隔内将内存中的数据集快照写入磁盘(持久化数据到磁盘),也就是行话讲的Snapshot快照,它恢复时是将快照文件直接读到内存里
Redis会单独创建(fork)一个子进程来进行持久化,会先将数据写入到一个临时文件中,待持久化过程都结束了,再用这个临时文件替换上次持久化好的文件。整个过程中,主进程是不进行任何IO操作的,这就确保了极高的性能如果需要进行大规模数据的恢复,且对于数据恢复的完整性不是非常敏感,那RDB方式要比AOF方式更加的高效。RDB的缺点是最后一次持久化后的数据可能丢失。
2. rdb配置文件说明
低版本
save 900 1 #如果至少执行了1个更改,则在900秒(15分钟)后同步
save 300 10 #如果至少执行了10个更改,则在300秒(5分钟)后同步
save 60 10000 #如果至少执行了10000个更改,则将在60秒后同步
高版本
save 3600 1 300 100 60 10000
或者
save 3600 1 #如果至少进行了1个更改,则在3600秒(1小时)之后同步
save 300 100 #如果至少执行了100个更改,则在300秒(5分钟)之后同步
save 60 10000 #如果至少执行了10000个更改,则在60秒后同步
dbfilename dump.rdb #快照的文件名
dir ./ #快照文件的存放路径
如果注释掉了save,并且没有开启aof持久化,使用的是redis的RDB默认持久化方案。
如果有重要的数据写进来 需要立即生成快照 使用 save / bgsave命令
Save:save时只管保存,其它不管,全部阻塞
BGSAVE:Redis会在后台异步进行快照操作,快照同时还可以响应客户端请求。
可以通过lastsave命令获取最后一次成功执行快照的时间
执行flushall命令,会把redis缓存的数据清空,也会产生dump.rdb文件,但里面是没有存储任何数据,无意义。
当我们执行 shutdown命令时直接生成一个dump.rdb文件,将数据持久化
但是如果是非正常关闭redis服务,是不会持久化数据的,会导致最后一次快照 和 意外down掉之间的写的数据都会丢失。
恢复数据只需要往dump.rdb文件移动到redis安装启动目录即可,可以通过config get dir命令去获取安装启动目录
3. RDB的操作
修改配置文件中的参数 需要重启redis服务,才能起作用
AOF的持久化
1.1.1. 什么是AOF持久化
以日志的形式来记录每个写操作,将Redis执行过的所有写指令记录下来(读操作不记录)
只许追加文件但不可以改写文件,redis启动之初会读取该文件重新构建数据。
redis重启的话就根据日志文件的内容将指令从前到后执行一次以完成数据的恢复工作
1.1.2. AOF配置文件的说明
启动AOF配置就需要将RDB配置禁掉。
appendonly yes #启用aof
appendfilename "appendonly.aof" #aof的文件名称
# appendfsync always
appendfsync everysec #aof的默认策略
# appendfsync no
先把rdb策略禁掉
appendonly no 改为 appendonly yes 启用aof的持久化
appendfilename "appendonly.aof" 记录写操作的文件
如果appendonly.aof文件出问题了,修复 appendonly.aof 使用 ./redis-check-aof --fix appendonly.aof
1.1.3. AOF的操作
1.2. RDB和AOF的总结( 面试题 重要)
1.2.1. RDB的优点
RDB文件是紧凑的二进制文件,比较适合做冷备(冷备是指两个服务器,一台运行,一台不运行做为备份,这样一旦运行的服务器宕机,就把备份的服务器运行起来。),全量复制的场景。
相对于AOF持久化机制来说,直接基于RDB数据文件来重启和恢复Redis进程,更加快速;
RDB每次写,都是直接写Redis内存,只是在一定的时候,才会将数据写入磁盘中;
RDB使用单独子进程来进行持久化,主进程不会进行任何IO操作,保证了Redis的高性能
1.2.2. RDB的缺点
如果想要在Redis故障时,尽可能少的丢失数据,那么RDB没有AOF好
RDB每次在fork(创建)子进程来执行RDB快照数据文件生成的时候,如果数据文件特别大,可能会导致对客户端提供的服务暂停数毫秒,或者甚至数秒;
RDB无法实现实时或者秒级持久化
1.2.3. AOF的优点
AOF可以更好的保护数据不丢失
AOF日志文件以append-only模式写入,写入性能比较高
AOF日志文件即使过大的时候,出现后台重写操作,也不会影响客户端的读写。
适合做灾难性的误删除紧急恢复
1.2.4. AOF的缺点
对于同一份数据来说,AOF日志文件通常比RDB数据快照文件更大,恢复速度慢
AOF开启后,支持的写QPS(每秒查询率)会比RDB支持的写QPS低
1.2.5. 应用场景
对数据非常敏感,建议使用默认的AOF持久化方案,比如购物车数据
数据呈现阶段有效性,能接受一段时间的缓存丢失,建议使用RDB持久化方案,比如新闻列表页面最新的新闻列表、大数据量的备份恢复(冷备)
1.2.6. redis使用准则
redis不可以一次性存储海量数据,只适用于较少数据的场景。
redis存储数据时一般都要设置有效期。
2.redis事务机制
支持事务,但是不支持强事务。
通过三个命令
multi 标记事务开始
命令入队
exec 执行事务 discard 取消作废事务
mysql事务:将一个事务的很多操作,作为一个系列操作,这个系列的操作要么都成功,要么都失败。
ACID : 原子性、一致性、隔离性、持久性。
2.1. 什么是redis的事务
Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证
批量操作在发送 EXEC 命令前被放入队列缓存。
收到 EXEC 命令后进入事务执行,如果事务中的任意命令语法有错,其余的命令会执行失败。
收到 EXEC 命令后进入事务执行,如果事务中的任意指令没有语法错误,但是事务中任意命令执行失败,其余的命令依然会执行成功。
在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
一个事务从开始到执行会经历以下三个阶段
开始事务。
命令入队。
执行事务
2.2. redis事务中的常用命令
序号 | 命令及描述 |
---|---|
1 | DISCARD 取消事务,放弃执行事务块内的所有命令。 |
2 | EXEC 执行所有事务块内的命令。 |
3 | MULTI 标记一个事务块的开始。 |
4 | UNWATCH 取消 WATCH 命令对所有 key 的监视。 |
5 | [WATCH key key ...] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。 |
2.3. redis事务的操作
Redis 没有在事务上增加任何维持原子性的机制,所以 Redis 事务的执行并不是原子性的
Redis事务可以理解为一个打包的批量执行脚本,但批量指令并非原子化的操作
3. redis的乐观锁和悲观锁
3.1. 什么是悲观锁
悲观锁(Pessimistic Lock),顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,当其他线程想要访问数据时,都需要阻塞挂起。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁、表锁,读锁,写锁等,都是在操作之前先上锁。
select * from user where id = 1 for update;
在Java中,synchronized的思想也是悲观锁。
3.2. 什么是乐观锁
mysql 乐观锁 列version版本号机制(1,2,3,4,5)
乐观锁(Optimistic Lock),顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量。
乐观锁策略:提交版本作为条件必须大于等于记录当前版本才能执行更新
一般会使用版本号机制或CAS操作实现
3.3. redis中乐观锁 watch监控
先监控一个key,再开启一个事务,在当前事务中对key进行操作
再启动一个redis客户端 来修改这个key
再提交之前的事务
3.4. redis中的悲观锁
使用Redis实现悲观锁命令SETNX命令
当且仅当 key 不存在,缓存数据,并返回1;若给定的 key 已经存在,则 SETNX 不做任何动作,并返回0。
使用场景:
在Java中实现分布式锁通常是为了解决多个进程或多个服务器实例之间共享资源访问的同步问题。由于分布式系统中的锁需要跨多个节点工作,因此不能简单地使用Java中的synchronized
关键字或ReentrantLock
类,这些只能在单个JVM内部工作。
4. redis发布订阅的功能
4.1. 什么是发布订阅
进程间的一种消息通信模式:发送者(pub)发送消息,订阅者(sub)(消费者)接收消息。
Redis 发送者、客户端(消费方)可以订阅任意数量的频道
下图展示了频道 channel1(通道) , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:
当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:
4.2. redis的发布订阅的应用
发送邮件、短信
聊天室功能
公告牌显示功能
服务之间利用消息解耦
4.3. redis发布订阅的相关命令
序号 | 命令及描述 |
---|---|
1 | [PSUBSCRIBE pattern pattern ...] 订阅一个或多个符合给定模式的频道。发送者使用 |
2 | PUBSUB subcommand [argument [argument ...]] 查看订阅与发布系统状态。 |
3 | PUBLISH channel message 将信息发送到指定的频道。发送者使用 |
4 | PUNSUBSCRIBE [pattern [pattern ...]] 退订所有给定模式的频道,发送者使用 |
5 | [SUBSCRIBE channel channel ...] 订阅给定的一个或多个频道的信息。消费者使用 |
6 | UNSUBSCRIBE [channel [channel ...]] 指退订给定的频道。消费者使用 |
消费者订阅监听对应的频道
生产者向对应的频道发送消息(将消息和频道进行绑定)
5. redis数据删除和淘汰策略
5.1. 过期数据
redis中的数据特征
Redis是一种内存级数据库,所有数据均存放在内存中,内存中的数据可以通过TTL指令获取其状态
TTL返回的值有三种情况:正数,-1,-2
正数:代表该数据在内存中还能存活的时间,过期时间
-1:永久有效的数据
-2 :已经过期的数据 或 被删除的数据 或 未定义的数据
删除策略就是针对已过期数据的处理策略,已过期的数据是真的就立即删除了吗?其实也不是,我们会有多种删除策略,是分情况的,在不同的场景下使用不同的删除方式会有不同效果,这也正是我们要将的数据的删除策略的问题
5.2. 数据过期删除策略
删除策略指的是数据删除的时机(什么时候删除数据)
在内存占用与CPU占用之间寻找一种平衡,顾此失彼都会造成整体redis性能的下降,甚至引发服务器宕机或 内存泄露
针对过期数据要进行删除的时候都有哪些删除策略呢?
1.定时删除(默认)
创建一个定时器,当key设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作
2.惰性删除
数据到达过期时间,不做处理。等下次访问该数据时,我们需要判断
3.定期删除
Redis启动服务器初始化时,读取配置hz的值,默认为10
每秒钟执行hz次activeExpireCycle(),对每个Redis库逐一进行检测,每次执行耗时:250ms/10 100ms/16
对某个Redis库检测时,随机挑选w个key检测
如果key超时,删除key
如果一轮中删除的key的数量>检测key总数的25%,循环该过程
如果一轮中删除的key的数量≤检测key总数的25%,检查下一个Redis库,0-15循环
w取值=ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP属性值
5.3. 数据淘汰策略
什么叫数据淘汰策略?什么样的应用场景需要用到数据淘汰策略?
当新数据进入redis时,如果内存不足怎么办?在执行每一个命令前,会调用freeMemoryIfNeeded()检测内存是否充足。如果内存不满足新加入数据的最低存储要求,redis要临时删除一些数据为当前指令清理存储空间。清理数据的策略称为数据淘汰策略。
LRU关键是看最后一次被使用到发生替换的时间长短,时间越长,就会被淘汰;而LFU关键是看一定时间段内被使用的频率(次数),使用频率越低,就会被淘汰
第一类:检测易失数据
volatile-lru:挑选最近最少使用的数据淘汰(最后一次使用时间)
volatile-lfu:挑选最近使用次数最少的数据淘汰(最近一段时间的使用次数)
volatile-ttl:挑选将要过期的数据淘汰
volatile-random:任意选择数据淘汰
第二类:检测全库数据
allkeys-lru:挑选最近最少使用的数据淘汰(最后一次使用时间)
allkes-lfu::挑选最近使用次数最少的数据淘汰(最近一段时间的使用次数)
allkeys-random:任意选择数据淘汰,相当于随机
第三类:放弃数据驱逐
no-enviction(驱逐):禁止驱逐数据(redis4.0中默认策略),会引发OOM(Out Of Memory)
5.4. 数据淘汰策略设置
redis.conf文件中配置
maxmemory-policy noeviction
面试题
1、redis持久化机制(重要)
Redis 是一种 内存数据库,数据默认存储在 RAM 中,这意味着一旦服务重启或崩溃,所有数据都会丢失。为了解决这个问题,Redis 提供了 持久化机制,将内存中的数据保存到磁盘,确保数据在故障后能够恢复。
RDB 是 Redis 默认的持久化方式,它会在 指定的时间间隔 内生成内存数据的 二进制快照(dump.rdb
文件),比较适合做冷备 ,保存到磁盘。RDB的缺点是最后一次持久化后的数据可能丢失
AOF 记录 所有写操作命令(如 SET
、DEL
),并以 追加写入(append-only) 的方式保存到 appendonly.aof
文件,写入性能比较高 。Redis 重启时会 重放 AOF 文件中的命令 恢复数据。
如果注释掉了save,并且没有开启aof持久化,使用的是redis的RDB默认持久化方案。
RDB默认持久化方案
save 900 1 # 15 分钟内至少 1 个 key 被修改
save 300 10 # 5 分钟内至少 10 个 key 被修改
save 60 10000 # 1 分钟内至少 10000 个 key 被修改
策略 | 行为 |
---|---|
always |
每个命令都同步到磁盘 |
everysec (默认) |
每秒同步一次 |
no |
由操作系统决定(通常 30 秒) |
appendonly yes #启用aof
appendfilename "appendonly.aof" #aof的文件名称
# appendfsync always
appendfsync everysec #aof的默认策略
# appendfsync no
2、redis的数据删除策略(重要)
删除策略就是针对已过期数据的处理策略,
TTL返回的值有三种情况:正数,-1,-2
正数:代表该数据在内存中还能存活的时间,过期时间
-1:永久有效的数据
-2 :已经过期的数据 或 被删除的数据 或 未定义的数据
————————————————————————————————
1.定时删除(默认)
创建一个定时器,当key设置有过期时间,且过期时间到达时,由定时器任务立即执行对键的删除操作
2.惰性删除
数据到达过期时间,不做处理。等下次访问该数据时,我们需要判断
3.定期删除
Redis启动服务器初始化时,读取配置hz的值,默认为10
每秒钟执行hz次activeExpireCycle(),对每个Redis库逐一进行检测,每次执行耗时:250ms/10 100ms/16
对某个Redis库检测时,随机挑选w个key检测
如果key超时,删除key
如果一轮中删除的key的数量>检测key总数的25%,循环该过程
如果一轮中删除的key的数量≤检测key总数的25%,检查下一个Redis库,0-15循环w取值=ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP属性值
3、redis的淘汰策略(重要)
当新数据进入redis时,如果内存不足怎么办?在执行每一个命令前,会调用freeMemoryIfNeeded()检测内存是否充足。如果内存不满足新加入数据的最低存储要求,redis要临时删除一些数据为当前指令清理存储空间。清理数据的策略称为数据淘汰策略
第一类:检测易失数据
volatile-lru:挑选最近最少使用的数据淘汰(最后一次使用时间)
volatile-lfu:挑选最近使用次数最少的数据淘汰(最近一段时间的使用次数)
volatile-ttl:挑选将要过期的数据淘汰
volatile-random:任意选择数据淘汰
第二类:检测全库数据
allkeys-lru:挑选最近最少使用的数据淘汰(最后一次使用时间)
allkes-lfu::挑选最近使用次数最少的数据淘汰(最近一段时间的使用次数)
allkeys-random:任意选择数据淘汰,相当于随机
第三类:放弃数据驱逐
no-enviction(驱逐):禁止驱逐数据(redis4.0中默认策略),会引发OOM(Out Of Memory)
4、redis事务相关命令和事务特点
序号 | 命令及描述 |
---|---|
1 | DISCARD 取消事务,放弃执行事务块内的所有命令。 |
2 | EXEC 执行所有事务块内的命令。 |
3 | MULTI 标记一个事务块的开始。 |
4 | UNWATCH 取消 WATCH 命令对所有 key 的监视。 |
5 | [WATCH key key ...] 监视一个(或多个) key ,如果在事务执行之前这个(或这些) key 被其他命令所改动,那么事务将被打断。 |
一个事务从开始到执行会经历以下三个阶段
开始事务。
命令入队。
执行事务
Redis 事务可以一次执行多个命令, 并且带有以下三个重要的保证
批量操作在发送 EXEC 命令前被放入队列缓存。
收到 EXEC 命令后进入事务执行,如果事务中的任意命令语法有错,其余的命令会执行失败。
收到 EXEC 命令后进入事务执行,如果事务中的任意指令没有语法错误,但是事务中任意命令执行失败,其余的命令依然会执行成功。
在事务执行过程,其他客户端提交的命令请求不会插入到事务执行命令序列中。
5、redis的乐观锁和悲观锁
见上
6、redis的发布订阅机制
Redis 的 发布订阅(Pub/Sub) 是一种消息通信模式,允许发送者(发布者)向频道(Channel)发送消息,而订阅者(Subscriber)可以接收来自一个或多个频道的消息。它是一种 实时、轻量级 的消息队列机制,适用于即时通知、事件驱动等场景。
(1) 命令
序号 | 命令及描述 |
---|---|
1 | [PSUBSCRIBE pattern pattern ...] 订阅一个或多个符合给定模式的频道。发送者使用 |
2 | PUBSUB subcommand [argument [argument ...]] 查看订阅与发布系统状态。 |
3 | PUBLISH channel message 将信息发送到指定的频道。发送者使用 |
4 | PUNSUBSCRIBE [pattern [pattern ...]] 退订所有给定模式的频道,发送者使用 |
5 | [SUBSCRIBE channel channel ...] 订阅给定的一个或多个频道的信息。消费者使用 |
6 | UNSUBSCRIBE [channel [channel ...]] 指退订给定的频道。消费者使用 |
(2) 特点
无持久化:离线客户端重新连接后无法收到历史消息。
实时性:消息即时推送,但无确认机制(可能丢失)。
替代方案:需持久化时使用
Stream
类型(Redis 5.0+)。