发布/订阅
Redis提供了发布订阅功能,可以用于消息的传输
Redis发布订阅机制包含
client
publisher、subscriber
命令
subscribe
订阅
subscribe channel1 channel2
publish
发布消息
publish channel message
psubscribe
模式匹配订阅
psubscribe pattern
punsubscribe
退订模式
punsubscribe pattern
机制
订阅某个频道/模式
客户端
pubsub_channels,表明该客户端订阅的所有频道
pubsub_patterns,表明该客户端订阅的所有模式
服务器端
pubsub_channels,该服务端中的所有频道以及订阅了这个频道的客户端
pubsub_patterns,该服务端中的所有模式和订阅了这些模式的客户端
客户端向某个频道发送消息时,Redis首先在redisServer中的pubsub_channels中找出键为该频道的节点,遍历该节点的值,即遍历订阅了该频道的客户端,将消息发送给这些客户端
然后,遍历结构体redisServer中的pubsub_patterns,找出包含该频道的模式的节点,将消息发送给订阅了该模式的客户端
事务
Redis事务
Redis事务是通过multi、exec、discard和watch这四个命令来完成的
Redis的单个命令都是原子性的,所以这里需要确保事务行的对象是命令集合
Redis将命令集合序列化并确保处于同一事务的命令集合连续且不被打断的执行
Redis不支持回滚操作
Redis ACID
A:Redis一个队列中的命令,执行或不执行
C:集群中不能保证实时一致性,只能是最终一致性
I:Redis命令是顺序执行的,在一个事务中,有可能被执行其他客户端的命令
D:有持久化,但不保证数据完整性
事务命令
multi
用于标记事务块的开始,Redis会将后续的命令逐个放入队列中,然后使用exec原子化地执行这个命令队列
机制
事务的执行
1. 事务开始
在RedisClient中,有属性flags,用来表示是否在事务中
flag = 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命令,服务端判断Client的flags,如果为REDIS_DIRTY_CAS,则清空事务队列
Redis的弱事务性
Redis不支持回滚
1. 大多数事务失败是因为语法错误或类型错误,这都是可以预见的
2. Redis为了性能方面忽略了事务回滚
Lua整合
简介
Lua是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开发,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能
利用Redis整合Lua,主要是为了性能及事务原子性,因为Redis提供的事务功能太差
环境协作组件
Redis2.6.0开始,通过内置的Lua编译/解释器,可以使用EVAL命令对Lua脚本进行求值
脚本的命令是原子的,RedisServer在执行脚本命令中,不允许插入新的命令
脚本命令可以复制,RedisServer在获得脚本后不执行,生成标识返回,Client根据标识可以随时执行
EVAL
通过eval命令,可以运行一段lua脚本
EVAL script numkeys key [key....] arg [arg....]
script
一段lua脚本程序,被运行在RedisServer上下文中
这段脚本不必(也不应该)定义为一个Lua函数
key [key...]
EVAL第三个参数算起,使用了numkeys个键
表示在脚本中所用到的那些Redis键,这些键名参数可以在Lua中通过全局变量KEYS数组访问
arg [arg....]
可以在Lua中通过全局变量ARGV数组访问
Lua中调用Redis命令
redis.call()
返回值就是redis命令执行的返回值
如果出错,则返回错误信息,不继续执行
redis.pcall()
返回值为reidis命令执行的返回值
如果出错,记录错误信息,然后继续执行
注意
在脚本中,使用return语句将返回值返回给客户端,如果没有return,则返回nil
EVALSHA
EVAL要求在每次执行脚本的时候都发送一次脚本主体
Redis有一个内部的缓存机制,因此不会每次都重新编译脚本,为了减少带宽消耗,Redis实现了EVALSHA命令
它作用和EVAL一样,都用于对脚本求职,但它接受的第一个参数不是脚本,而是脚本的SHA1校验和
SCIRPT
Script Exists
根据给定的脚本校验和,检查指定脚本是否存在脚本缓存
Script Load
将一个脚本装入脚本缓存,返回SHA1摘要,但并不运行它
脚本管理命令
可以使用redis-cli直接执行lua脚本
脚本复制
Redis传播Lua脚本,在使用主从模式和开启AOF持久化前提下
当执行Lua脚本时,Redis服务器有脚本传播模式和命令传播模式
脚本传播模式
Redis复制脚本的默认使用模式,会将执行的脚本及其参数复制到AOF文件及从服务器里面
这一模式下执行的脚本不能有时间、内部状态、随机函数等,执行相同的脚本及参数必须产生相同的结果
命令传播模式
处于本模式的主服务器会将执行脚本产生的所有写命令用事务包裹起来,然后将事务复制到AOF文件及从服务器里面
因为复制的是写命令而不是脚本本身,所以即使脚本本身包含时间、内部状态、随机函数等,主服务器给所有从服务器复制的写命令仍然是相同的
开启命令传播模式,用户在使用脚本执行任何写操作之前,需要现在脚本里面调用
redis.replicate_commands()
只对调用该函数的脚本有效
使用命令模式执行完当前脚本后,服务器将自动切换回默认的脚本传播模式
管道/事务/脚本的区别
三者都可以批量执行命令
管道无原子性,命令都是独立的,属于无状态的操作
事务和脚本是有原子性的,区别在于脚本可借助Lua语言在服务端存储的遍历性定制和简化操作
脚本的原子性要强于事务,脚本执行期间,另外的客户端其它脚本/命令都无法执行,脚本的执行时间应该尽量短,不能太耗时
慢查询日志
Redis有慢查询日志,可用于监视和优化查询
Redis使用列表存储慢查询日志,采用队列方式(FIFO)
设置
redis.conf
slowlog-log-slower-than 1000
执行时间超过多少ms的命令会被记录到日志上
slowlog-max-len
存储慢查询日志条数
config set
可以临时设置,重启后无效
config set slowlog-max-len
保存
在redisServer中保存和慢查询日志相关的信息
属性lowlog链表保存了服务器中所有慢查询日志,链表中每个节点都保存了一个slowlogEntry结构(代表一条慢查询日志)
添加
每次hi行命令前后,程序都会记录微妙格式的当前unix时间戳,其差值就是服务器执行命令消耗的时长,服务器会将这个时长作为参数之一传给slowlogPushEntryIfNeeded(),它负责检查是否需要为这次执行的命令创建慢查询日志
slowlogPushEntryIfNeeded
1.检查命令执行时长是否超过slowlog-log-slower-than,是则创建一个新的日志,并添加到slowlog链表表头
2.检查慢查询日志长度是否超过slowlog-max-len,是则将多出来的日志从slowlog链表中删除
定位/处理
1. 尽量使用短的key,对于value有些也可精简,能使用int就int
2. 避免使用keys *,hgetall等全量操作
3. 减少大key的存取,打散为小key
4. 将rdb改为aof模式
5. 想要一次添加多条数据时使用管道
6. 尽可能使用hash存储
7. 尽量限制redis使用内存大小,这样可以避免redis使用swap分区或出现OOM错误
Redis监听器
Redis客户端通过执行Monitor命令可以将自己变为一个监视器,实时接收并打印出服务器当i请安处理的命令请求的相关信息
其他客户端向服务器发送一条命令请求时,服务器除了处理请求外,还会将这条命令请求的信息发送给所有监视器
redisServer维护一个monitors的链表,记录自己的监视器,每次收到Monitor命令后,将客户端追加到链表尾
监控平台
grafana
一个开箱即用的可视化工具,具有功能齐全的度量仪表盘和图形编辑器,有灵活丰富的图形化选项,可以混合多种风格,支持多个数据源特点
prometheus
一个开源的服务监控系统,它通过HTTP协议从远程的及其机器数据并存储在本地的时序数据库上
redis_exporter
为Prometheus提供了redis指标的导出