Redis事务与扩展
前言
本节主要介绍一下redis的事务和一些扩展功能
Redis事务
所谓事务(Transaction) ,是指作为单个逻辑工作单元执行的一系列操作
事务的四个特性ACID
- Atomicity(原子性):构成事务的的所有操作必须是一个逻辑单元,要么全部执行,要么全部不执行。
- Consistency(一致性):数据库在事务执行前后状态都必须是稳定的或者是一致的。
- Isolation(隔离性):事务之间不会相互影响。
- Durability(持久性):事务执行成功后必须全部写入磁盘
redis的事务:
- Redis的事务是通过multi、exec、discard和watch这四个命令来完成的。
- Redis的单个命令都是原子性的,所以这里需要确保事务性的对象是命令集合。
- Redis将命令集合序列化并确保处于同一事务的命令集合连续且不被打断的执行
- Redis不支持回滚操作
事务命令
- multi:用于标记事务块的开始,Redis会将后续的命令逐个放入队列中,然后使用exec原子化地执行这个命令队列
- exec:执行命令队列
- discard:清除命令队列
- watch:监视key
- unwatch:清除监视key
事务机制
事务的执行
- 事务开始
在RedisClient中,有属性flags,用来表示是否在事务中
flags=REDIS_MULTI - 命令入队
RedisClient将命令存放在事务队列中(EXEC,DISCARD,WATCH,MULTI除外) - 事务队列
multi Cmd *commands 用于存放命令 - 执行事务
RedisClient向服务器端发送exec命令,RedisServer会遍历事务队列,执行队列中的命令,最后将执行的结果一次性返回给客户端。
如果某条命令在入队过程中发生错误,redisClient将flags置为REDIS_DIRTY_EXEC,EXEC命令将会失败
返回。
Watch的执行
使用WATCH命令监视数据库键
redisDb有一个watched_keys字典,key是某个被监视的数据的key,值是一个链表.记录了所有监视这个数据的客户端。
监视机制的触发
当修改数据后,监视这个数据的客户端的flags置为REDIS_DIRTY_CAS
事务执行
RedisClient向服务器端发送exec命令,服务器判断RedisClient的flags,如果为REDIS_DIRTY_CAS,则清空事务队列。
Redis的弱事务性
Redis语法错误
整个事务的命令在队列里都清除
127.0.0.1:6379> multi
OK
127.0.0.1:6379> sets m1 44
(error) ERR unknown command `sets`, with args beginning with: `m1`, `44`,
127.0.0.1:6379> set m2 55
QUEUED
127.0.0.1:6379> exec
(error) EXECABORT Transaction discarded because of previous errors.
127.0.0.1:6379> get m1
"22"
flags=multi_dirty
Redis运行错误
在队列里正确的命令可以执行 (弱事务性)
弱事务性 :
1、在队列里正确的命令可以执行 (非原子操作)
2、不支持回滚
27.0.0.1:6379> multi
OK
127.0.0.1:6379> set m1 55
QUEUED
127.0.0.1:6379> lpush m1 1 2 3 #不能是语法错误
QUEUED
127.0.0.1:6379> exec
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
127.0.0.1:6379> get m1
"55"
Redis不支持事务回滚(为什么呢)
1、大多数事务失败是因为语法错误或者类型错误,这两种错误,在开发阶段都是可以预见的
2、Redis为了性能方面就忽略了事务回滚。 (回滚记录历史版本)
Redis其它扩展功能
发布与订阅
Redis提供了发布订阅功能,可以用于消息的传输
Redis的发布订阅机制包括三个部分,publisher,subscriber和Channel
发布者和订阅者都是Redis客户端,Channel则为Redis服务器端。
发布者将消息发送到某个的频道,订阅了这个频道的订阅者就能接收到这条消息
发布订阅的机制
订阅某个频道或模式:
客户端(client):
- 属性为pubsub_channels,该属性表明了该客户端订阅的所有频道
- 属性为pubsub_patterns,该属性表示该客户端订阅的所有模式
服务器端(RedisServer):
- 属性为pubsub_channels,该服务器端中的所有频道以及订阅了这个频道的客户端
- 属性为pubsub_patterns,该服务器端中的所有模式和订阅了这些模式的客户端
当客户端向某个频道发送消息时,Redis首先在redisServer中的pubsub_channels中找出键为该频道的结点,遍历该结点的值,即遍历订阅了该频道的所有客户端,将消息发送给这些客户端。
然后,遍历结构体redisServer中的pubsub_patterns,找出包含该频道的模式的结点,将消息发送给订阅了该模式的客户端。
使用场景
哨兵模式,Redisson框架使用
在Redis哨兵模式中,哨兵通过发布与订阅的方式与Redis主服务器和Redis从服务器进行通信。
Redisson是一个分布式锁框架,在Redisson分布式锁释放的时候,是使用发布与订阅的方式通知的
Lua脚本
lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
Lua应用场景:游戏开发、独立应用脚本、Web应用脚本、扩展和数据库插件。
从Redis2.6.0版本开始,通过内置的lua编译/解释器,可以使用EVAL命令对lua脚本进行求值。
简单的说,redis支持Lua脚本进行操作的
管道(pipeline),事务和脚本(lua)三者的区别
三者都可以批量执行命令
- 管道无原子性,命令都是独立的,属于无状态的操作
- 事务和脚本是有原子性的,其区别在于脚本可借助Lua语言可在服务器端存储的便利性定制和简化操作
脚本的原子性要强于事务,脚本执行期间,另外的客户端 其它任何脚本或者命令都无法执行,脚本的执
行时间应该尽量短,不能太耗时的脚本
慢查询日志
我们都知道MySQL有慢查询日志
Redis也有慢查询日志,可用于监视和优化查询
慢查询设置
在redis.conf中可以配置和慢查询日志相关的选项:
#执行时间超过多少微秒的命令请求会被记录到日志上 0 :全记录 <0 不记录
slowlog-log-slower-than 10000
#slowlog-max-len 存储慢查询日志条数
slowlog-max-len 128
Redis使用列表存储慢查询日志,采用队列方式(FIFO)
config set的方式可以临时设置,redis重启后就无效
config set slowlog-log-slower-than 微秒
config set slowlog-max-len 条数
查看日志:slowlog get [n]
127.0.0.1:6379> config set slowlog-log-slower-than 0
OK
127.0.0.1:6379> config set slowlog-max-len 2
OK
127.0.0.1:6379> set name:001 zhaoyun
OK
127.0.0.1:6379> set name:002 zhangfei
OK
127.0.0.1:6379> get name:002
"zhangfei"
127.0.0.1:6379> slowlog get
1) 1) (integer) 7 #日志的唯一标识符(uid)
2) (integer) 1589774302 #命令执行时的UNIX时间戳
3) (integer) 65 #命令执行的时长(微秒)
4) 1) "get" #执行命令及参数
2) "name:002"
5) "127.0.0.1:37277"
6) ""
2) 1) (integer) 6
2) (integer) 1589774281
3) (integer) 7
4) 1) "set"
2) "name:002"
3) "zhangfei"
5) "127.0.0.1:37277"
6) ""
# set和get都记录,第一条被移除了。
慢查询定位&处理
使用slowlog get 可以获得执行较慢的redis命令,针对该命令可以进行优化:
1、尽量使用短的key,对于value有些也可精简,能使用int就int。
2、避免使用keys *、hgetall等全量操作。
3、减少大key的存取,打散为小key
4、将rdb改为aof模式
rdb fork 子进程 主进程阻塞 redis大幅下降
关闭持久化 , (适合于数据量较小)
改aof 命令式
5、想要一次添加多条数据的时候可以使用管道
6、尽可能地使用哈希存储
7、尽量限制下redis使用的内存大小,这样可以避免redis使用swap分区或者出现OOM错误 内存与硬盘的swap
监视器
Redis客户端通过执行MONITOR命令可以将自己变为一个监视器,实时地接受并打印出服务器当前处理的命令请求的相关信息。
此时,当其他客户端向服务器发送一条命令请求时,服务器除了会处理这条命令请求之外,还会将这条命令请求的信息发送给所有监视器。
Redis客户端1
127.0.0.1:6379> monitor
OK
1589706136.030138 [0 127.0.0.1:42907] "COMMAND"
1589706145.763523 [0 127.0.0.1:42907] "set" "name:10" "zhaoyun"
1589706163.756312 [0 127.0.0.1:42907] "get" "name:10"
Redis客户端2
127.0.0.1:6379>
127.0.0.1:6379> set name:10 zhaoyun
OK
127.0.0.1:6379> get name:10
"zhaoyun"
Redis监控平台
依靠自身的监控命令采集既没有图形界面化的便捷,可读性也差,所以大多都会使用其它监控组件进行监控,如grafana、prometheus以及redis_exporter。
Grafana 是一个开箱即用的可视化工具,具有功能齐全的度量仪表盘和图形编辑器,有灵活丰富的图形化选项,可以混合多种风格,支持多个数据源特点。
Prometheus是一个开源的服务监控系统,它通过HTTP协议从远程的机器收集数据并存储在本地的时序数据库上。
redis_exporter为Prometheus提供了redis指标的导出,配合Prometheus以及grafana进行可视化及监控。
总结
本文主要介绍了redis的事务。了解redis为何是弱事务性