Redis 事务通过 MULTI
、EXEC
、DISCARD
、WATCH
几个命令来实现
Redis 事务可以一次执行多条命令,Redis 事务有如下特点:
事务操作如下:
Reids 事务发生错误分为两种情况:
①:事务提交前发生错误
这里故意将 incr 命令
写错,从结果我们可以看到:这条 incr 命令
没有入队,并且事务执行失败,num1 和 num2 都没有值。
②:事务提交后发生错误
上面的事务命令中,我给 num1
设置了一个 a,然后执行自增命令,最后获取 num1 的值。
我们发现第二条命令执行发生了错误,但是整个事务依然提交成功了。
从上面现象中可以得出,Redis 事务不支持回滚操作。如果支持的话,整个事务的命令都不应该被执行。
以下是这种做法的优点:
有种观点认为 Redis 处理事务的做法会产生 bug , 然而需要注意的是, 在通常情况下, 回滚并不能解决编程错误带来的问题。 举个例子, 如果你本来想通过 incr 命令将键的值加上 1 , 却不小心加上了 2 , 又或者对错误类型的键执行了 incr , 回滚是没有办法处理这些情况的
当执行 discard
命令时, 事务会被放弃, 事务队列会被清空, 并且客户端会从事务状态中退出
WATCH 机制:使用 WATCH 监视一个或多个 key , 跟踪 key 的 value 修改情况,如果有key 的 value 值在事务 EXEC 执行之前被修改了,整个事务被取消。EXEC 返回提示信息,表示事务已经失败
WATCH 监视了一个带过期时间的键,那么即使这个键过期了,事务仍然可以正常执行
从定义上来说, Redis 中的脚本本身就是一种事务, 所以任何在事务里可以完成的事, 在脚本里面也能完成。 并且一般来说, 使用脚本要来得更简单,并且速度更快
我们知道 Redis 是内存数据库,主打高性能,速度快。相比 Redis 而言,MySQL 的数据则是保存再硬盘中速度慢,但是稳定性好。
你想想 Redis 数据保存在内存中,一旦服务器宕机了,数据岂不是全部都没了,这将会出现很大问题。所以 Redis 为了弥补这一缺陷,提供数据持久化机制,即使服务器宕机,依然可以保证数据不丢失
Redis 提供了两种持久化机制 RDB 和 AOF,适用于不同场景:
RDB 持久化是通过在指定时间间隔对数据进行快照。比如:在 8 点钟对数据进行持久化,那么 Redis 会 fork 一个子进程将 8 点那一刻内存中的数据持久化到磁盘上。
触发 RDB 持久化有以下几种方法:
save 900 1:900s 内有 1 个 key 发生变化,则触发 RDB 快照
save 300 10:300s 内有 10 个 key 发生变化,则触发 RDB 快照
save 60 10000:60s 内有 10000 个 key 发生变化(新增、修改、删除),则触发 RDB 快照
save “”:该配置表示关闭 RDB 持久化
Redis 进行 RDB 时,会 fork 一个子进程来进行数据持久化,这样不妨碍 Redis 继续对外提供服务,提高效率
优点:
缺点:
AOF 持久化是通过日志的方式,记录每次 Redis 的写操作。当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF 命令以 Redis 协议追加保存每次写的操作到文件末尾。
Redis 还能对 AOF 文件进行后台重写,使得 AOF 文件的体积不至于过大.
AOF 持久化配置:
# 是否开启 aof no:关闭;yes: 开启
appendonly no
# aof 文件名
appendfilename "appendonly.aof"
# aof 同步策略
# appendfsync always # 每个命令都写入磁盘,性能较差
appendfsync everysec # 每秒写一次磁盘,Redis 默认配置
# appendfsync no # 由操作系统执行,默认Linux配置最多丢失30秒
# aof 重写期间是否同步
no-appendfsync-on-rewrite no
# 重写触发策略
auto-aof-rewrite-percentage 100 # 触发重写百分比 (指定百分比为0,将禁用aof自动重写功能)
auto-aof-rewrite-min-size 64mb # 触发自动重写的最低文件体积(小于64mb不自动重写)
# 加载aof时如果有错如何处理
# 如果该配置启用,在加载时发现aof尾部不正确是,会向客户端写入一个log,但是会继续执行,如果设置为 no ,发现错误就会停止,必须修复后才能重新加载。
aof-load-truncated yes
# aof 中是否使用 rdb
# 开启该选项,触发AOF重写将不再是根据当前内容生成写命令。而是先生成RDB文件写到开头,再将RDB生成期间的发生的增量写命令附加到文件末尾。
aof-use-rdb-preamble yes
aof 文件是命令追加的方式,先将命令写入缓冲区,时间到了再写如磁盘中:
appendfsync always # 每个命令都写入磁盘,性能较差
appendfsync everysec # 每秒写一次磁盘,Redis 默认配置
appendfsync no # 由操作系统执行,默认Linux配置最多丢失30秒
上面配置就是何时写入磁盘中
aof 文件虽然丢失的数据少,但是随着时间的增加,aof 文件体积越来越大,占用磁盘空间越来越大,恢复时间长。所以 redis 会对 aof 文件进行重写,以减少 aof 文件体积:
-- 重写前的 aof
set k1 20
set k2 40
set k1 35
set k3 34
set k2 19
-- 这里 k1 最终的值为 35,k2 最终值为 19,所以不需要写入两个命令
-- 重写后
set k1 35
set k3 34
set k2 19
从 Redis 4.0 版本开始,引入了混合持久化机制,纯AOF方式、RDB+AOF方式,这一策略由配置参数aof-use-rdb-preamble(使用RDB作为AOF文件的前半段)控制,默认关闭(no),设置为yes可开启
混合持久化优点如下:
AOF 数据恢复有两种:
优点
缺点
在生产环境中,我们使用 Redis 通常采用集群模式,因为单机版 Redis 稳定性可靠性较低,而且存储空间有限。
Redis 支持三种集群模式:
主从复制模式,有一个主,多个从,从而实现读写分离。主机负责写请求,从机负责读请求,减轻主机压力
主从复制过程如下:
优点:
缺点
哨兵的作用是起到监控作用。一旦 Redis 集群出现问题了,哨兵会立即做出相应动作,应对异常情况。
哨兵模式是基于主从复制模式上搭建的,因为主从复制模式情况下主服务器宕机,会导致整个集群不可用,需要人工干预。所以,哨兵模式在主从复制模式下引入了哨兵来监控整个集群,哨兵模式架构图如下:
哨兵功能:
监控(Monitoring):哨兵会不断地检查主节点和从节点是否运作正常。
自动故障转移(Automatic failover):当主节点不能正常工作时,哨兵会开始自动故障转移操作,它会将失效主节点的其中一个从节点升级为新的主节点,并让其他从节点改为复制新的主节点。
配置提供者(Configuration provider):客户端在初始化时,通过连接哨兵来获得当前Redis服务的主节点地址。
通知(Notification):哨兵可以将故障转移的结果发送给客户端。
Redis 下线分为主观下线和客观下线两种
哨兵集群中任意一台服务器判断主库不可用时,此时会发送命令给哨兵集群中的其他服务器确认,其他服务器收到命令后会确认主库的状态,如果不可用,返回 YES,可用则返回 NO,当有半数的服务器都返回 YES,说明主库真的不可用,此时需要重新选举:
当哨兵集群判定主库下线了,此时需要重新选举出一个新的主库对外提供服务。那么该由哪个哨兵来完成这个新库选举和切换的动作呢?
注意:这里不能让每个哨兵都去选举,可能会出现每个哨兵选举出的新主库都不同,这样就没法判定,所以需要派出一个代表 ------ 哨兵代表选择
哨兵的选举机制其实很简单,就是一个Raft选举算法: 选举的票数大于等于num(sentinels)/2+1时,将成为领导者,如果没有超过,继续选举
任何一个想成为 Leader 的哨兵,要满足两个条件:
以 3 个哨兵为例,假设此时的 quorum 设置为 2,那么,任何一个想成为 Leader 的哨兵只要拿到 2 张赞成票,就可以了。
上面已经选举出了哨兵代表,此时代表需要完成新主库的选择,新库的选择需要满足以下几个标准:
故障转移要实现新老主库之间的切换
故障转移流程如下:
优点
缺点
Redis 的哨兵模式实现了高可用了,但是每台 Redis 服务器上存储的都是相同的数据,浪费内存,而且很难实现容量上的扩展。所以在 redis3.0上加入了 Cluster 集群模式,实现了 Redis 的分布式存储,也就是说每台 Redis 节点上存储不同的内容
Redis 集群没有使用一致性hash, 而是引入了 哈希槽的概念.
Redis 集群有16384个哈希槽,每个key通过CRC16校验后对16384取模来决定放置哪个槽。集群的每个节点负责一部分hash槽。
举个例子:比如当前集群有3个节点,那么:
这种结构很容易添加或者删除节点.。比如如果我想新添加个节点 D,我需要从节点 A, B, C中得部分槽到 D 上。如果我想移除节点 A,需要将 A 中的槽移到 B 和 C 节点上,然后将没有任何槽的 A 节点从集群中移除即可。
由于从一个节点将哈希槽移动到另一个节点并不会停止服务,所以无论添加删除或者改变某个节点的哈希槽的数量都不会造成集群不可用的状态。