写在开头:
本章是Redis学习归纳第四部分,着重于归纳redis发布与订阅,事务,Lua脚本,慢查询日志和监视器的笔记。
文章内容输出来源:拉勾教育大数据高薪训练营。
Redis高级部分是整个redis中较为重要的部分,在学习时经常会遇到概念不清和配置错误的问题,这里感谢导师Bob的帮助。
发布与订阅
Redis提供了发布订阅功能,可以用于消息的传输,Redis的发布订阅机制包括三个部分,publisher,subscriber和Channel 。
发布者和订阅者都是Redis客户端,Channel则为Redis服务器端。
发布者将消息发送到某个的频道,订阅了这个频道的订阅者就能接收到这条消息。
频道/模式的订阅与退订
subscribe:订阅 subscribe channel1 channel2 ..
Redis客户端1订阅频道1和频道2
publish:发布消息 publish channel message
Redis客户端2将消息发布在频道1和频道2上
Redis客户端1接收到频道1和频道2的消息
unsubscribe:退订 channel
Redis客户端1退订频道1
psubscribe :模式匹配 psubscribe +模式
Redis客户端1订阅所有以ch开头的频道
Redis客户端2发布信息在频道5上
Redis客户端1收到频道5的信息
punsubscribe 退订模式
punsubscribe ch*
发布订阅的机制
订阅某个频道或模式:
客户端(client):
属性为pubsub_channels,该属性表明了该客户端订阅的所有频道
属性为pubsub_patterns,该属性表示该客户端订阅的所有模式
服务器端(RedisServer):
属性为pubsub_channels,该服务器端中的所有频道以及订阅了这个频道的客户端
属性为pubsub_patterns,该服务器端中的所有模式和订阅了这些模式的客户端
当客户端向某个频道发送消息时,Redis首先在redisServer中的pubsub_channels中找出键为该频道的结点,遍历该结点的值,即遍历订阅了该频道的所有客户端,将消息发送给这些客户端。
然后,遍历结构体redisServer中的pubsub_patterns,找出包含该频道的模式的结点,将消息发送给订阅了该模式的客户端。
使用场景:哨兵模式,Redisson框架使用
在Redis哨兵模式中,哨兵通过发布与订阅的方式与Redis主服务器和Redis从服务器进行通信。这个我们将在后面的章节中详细讲解。
Redisson是一个分布式锁框架,在Redisson分布式锁释放的时候,是使用发布与订阅的方式通知的,这个我们将在后面的章节中详细讲解。
事务
所谓事务(Transaction) ,是指作为单个逻辑工作单元执行的一系列操作。
ACID回顾
- Atomicity(原子性):构成事务的的所有操作必须是一个逻辑单元,要么全部执行,要么全部不执行。 Redis:一个队列中的命令 执行或不执行
- Consistency(一致性):数据库在事务执行前后状态都必须是稳定的或者是一致的。 Redis: 集群中不能保证时时的一致性,只能是最终一致性
- Isolation(隔离性):事务之间不会相互影响。 Redis: 命令是顺序执行的,在一个事务中,有可能被执行其他客户端的命令的
- Durability(持久性):事务执行成功后必须全部写入磁盘。 Redis有持久化但不保证 数据的完整性
Redis事务
- Redis的事务是通过multi、exec、discard和watch这四个命令来完成的。
- Redis的单个命令都是原子性的,所以这里需要确保事务性的对象是命令集合。
- Redis将命令集合序列化并确保处于同一事务的命令集合连续且不被打断的执行
- Redis不支持回滚操作
事务命令
multi:用于标记事务块的开始,Redis会将后续的命令逐个放入队列中,然后使用exec原子化地执行这个命令队列
exec:执行命令队列
discard:清除命令队列
watch:监视key
unwatch:清除监视key
命令书写:
1。开始一个事务,写两个命令,然后执行
2。使用discard命令清楚队列,这时调用exec就会报错。
3。使用watch命令监视一个key,在执行exec之前,用另一个客户端修改s1的值,然后就监视到了s1的变化,那么这里就会修改失败。
事务机制
事务的执行
1. 事务开始
在RedisClient中,有属性flags,用来表示是否在事务中
flags=REDIS_MULTI
2. 命令入队
RedisClient将命令存放在事务队列中 (EXEC,DISCARD,WATCH,MULTI除外)
3. 事务队列
multiCmd *commands 用于存放命令
4. 执行事务
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的弱事务性
1。Redis语法错误
整个事务的命令在队列里都清除
flags=REDIS_DIRTY_EXEC
2。运行错误
在队列里正确的命令可以执行 (弱事务性)
弱事务性 :
1、在队列里正确的命令可以执行 (非原子操作)
2、不支持回滚
3。Redis不支持事务回滚(为什么呢)
1、大多数事务失败是因为语法错误或者类型错误,这两种错误,在开发阶段都是可以预见的
2、Redis为了性能方面就忽略了事务回滚。 (回滚记录历史版本)
Lua脚本
lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
Lua应用场景:游戏开发、独立应用脚本、Web应用脚本、扩展和数据库插件
nginx上使用lua 实现高并发
Lua环境协作组件
从Redis2.6.0版本开始,通过内置的lua编译/解释器,可以使用EVAL命令对lua脚本进行求值。
脚本的命令是原子的,RedisServer在执行脚本命令中,不允许插入新的命令
脚本的命令可以复制,RedisServer在获得脚本后不执行,生成标识返回,Client根据标识就可以随时执行
EVAL/EVALSHA命令实现
EVAL命令
通过执行redis的eval命令,可以运行一段lua脚本。
命令说明:
- script参数:是一段Lua脚本程序,它会被运行在Redis服务器上下文中,这段脚本不必(也不应该)定义为一个Lua函数。
- numkeys参数:用于指定键名参数的个数
- key [key ...]参数: 从EVAL的第三个参数开始算起,使用了numkeys个键(key),表示在脚本中所用到的那些Redis键(key),这些键名参数可以在Lua中通过全局变量KEYS数组,用1为基址的形式访问( KEYS[1] , KEYS[2] ,以此类推)。
- arg [arg ...]参数:可以在Lua中通过全局变量ARGV数组访问,访问的形式和KEYS变量类似( ARGV[1] 、 ARGV[2] ,诸如此类)
eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 first second
lua脚本中调用Redis命令
redis.call():
- 返回值就是redis命令执行的返回值
- 如果出错,则返回错误信息,不继续执行
redis.pcall():
- 返回值就是redis命令执行的返回值
- 如果出错,则记录错误信息,继续执行
注意事项
在脚本中,使用return语句将返回值返回给客户端,如果没有return,则返回nil
eval "return redis.call('set',KEYS[1],ARGV[1])" 1 n1 zhaoyun
慢查询日志
我们都知道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都记录,第一条被移除了。
慢查询记录的保存
在redisServer中保存和慢查询日志相关的信息
慢查询日志的阅览&删除
获得慢查询日志记录
查看日志数量的
slowlog len
清除日志
slowlog reset
监视器
Redis客户端通过执行MONITOR命令可以将自己变为一个监视器,实时地接受并打印出服务器当前处理的命令请求的相关信息。
此时,当其他客户端向服务器发送一条命令请求时,服务器除了会处理这条命令请求之外,还会将这条命令请求的信息发送给所有监视器。
在一个客户端上:
在另一个客户端上:
Redis监控平台
grafana、prometheus以及redis_exporter。
Grafana 是一个开箱即用的可视化工具,具有功能齐全的度量仪表盘和图形编辑器,有灵活丰富的图形化选项,可以混合多种风格,支持多个数据源特点。
Prometheus是一个开源的服务监控系统,它通过HTTP协议从远程的机器收集数据并存储在本地的时序数据库上。
redis_exporter为Prometheus提供了redis指标的导出,配合Prometheus以及grafana进行可视化及监控。