慢查询分析
用于统计redis执行命令过程中耗时较长的命令
redis执行客户端命令的流程包括:客户端发送命令、命令排队、命令执行、服务端返回命令结果,而慢查询统计的仅仅是命令执行阶段的耗时,即网络问题、命令阻塞不会被统计到
启用慢查询需要配置的参数为:
slowlog-log-slower-than:查询耗时小于该配置的查询不会被认为是慢查询,单位是微秒,默认值10000,即10ms以上被认为是慢查询。当该值配置为0,所有命令都会被记录
slowlog-max-len:慢查询日志长度,当日志存满了,FIFO,即队满时,第一个记录的慢查询将被驱逐
在redis-client端获取慢日志的命令是slowlog命令:
# n代表条数
slowlog get [n]
每个展示的慢查询有四个维度:id、执行时间戳、查询总耗时、执行的命令和参数
redis-benchmark
用于为redis做基准性能测试
Pipline
redis的管道式命令
前面了解过,redis命令瓶颈实际上最大在网络IO中,如果把一批命令打包发送过来执行,执行完了打包发回去,就会大大节省网络IO,这就是pipeline
一般使用pipeline都是基于java客户端jedis编写
但是要注意的是,Pipeline只是打包,并不是将这些命令封装成原子操作!
lua脚本
类似mysql支持通过显式事务加锁并将一组操作封装成一个完整的原子操作
lua脚本是redis借助lua脚本语言开发的,用于将一组redis操作封装成原子操作的能力
redis中有两个命令用于执行lua:
eval:发送lua命令到服务端执行,
eval 脚本内容 key个数 key列表 参数列表
evalsha:将lua加载到服务端,每次执行直接执行服务端存储的对应命令,避免每次发送命令的网络IO开销
lua脚本中,使用redis.call()命令执行命令,call命令的参数1固定是redis命令,redis命令有几个参数,后面就跟几个额外参数,例如:
redis.call("set", "hello", "world")
redis.call("get", "hello")
还有一个写法:
eval 'return "hello " .. KEYS[1] .. ARGV[1]' 1 redis world
这里1的意思是,后面的参数,只有1个是KEYS列表中的,1个以后的都是ARGV列表的
另外redis是从1开始的,因此没有KEYS[0]这样的写法
一般使用这个能力也是通过java客户端jedis编写:
Bitmap
bitmap本质上是字符串类型,只不过通过一系列命令让字符串的值具有了位操作的能力
原本字符串可以存任意字符,而使用bitmap只能存0、1,这样就显得这个字符串看起来像是二进制数据,再加上对每个字符给定的偏移量(类似index),使得实际操作起来就像真的在操作二进制数据
bitmap包括如下命令:
# 将bitmap指定偏移量的位设置指定值
setbit key offset value
# 获取bitmap指定偏移量的值
getbit key offset
# 获取Bitmaps指定范围值为1的个数
bitcount [start] [end]
# bitmap间的交、并、非、异或操作
bitop operator destkey key1 key2 [key3...]
对于bitop操作,说明一下:
operator指的是操作符,可以执行的包括and、or、not、xor,分别为交集、并集、非、异或
destkey是计算结果存储到哪个key里面
举个例子,有两个bitmap,分别代表用户1和用户2在本周的登录数据,1为有登录,0位没有登录
# user1_login: 0 1 1 0 0 1 0
# user2_login: 0 1 0 1 0 0 1
# 求两个用户都登录的天,这时候就可以用交集
bitop and user1_user2_login user1_login user2_login
# user1_user2_login: 0 1 0 0 0 0 0
其他命令如下:
# 计算Bitmaps中第一个值为targetBit的偏移量
bitpos key targetBit [start] [end]
那么bitmap的作用是什么,就是用于存储大量用户登录信息的场景
如果用户id方便转换成index,例如都是数字形式,就可以统一减去一个差值算出其偏移量,存到一个key里面
在一个key里面存5000000位,也比存5000000个key占空间更小更快,而且随着时间推移,会越来越明显
HyperLogLog
HyperLogLog也是基于字符串衍生的,它本质上不是一个存储类型,而是一种算法,它的目的就是用于存储一个集合,能够占用非常小的空间,并随时获取集合中数据的总数量(无法获取具体信息了)
HyperLogLog的命令包括:
# 向集合中添加数据
pfadd key element [element...]
# 计算一个集合中独立数据个数
pfcount key1 [key2 ...]
# 合并,其中destkey是存储在哪个键
pfmerge destkey key1 key2 [key3 ...]
HyperLogLog占空间有多小呢,假设有1000000用户,要存储每天有哪些用户登录过
使用集合类直接存储用户id1天80M,1个月2.4G,1年28G
使用HyperLogLog存储1天15k,1个月450k,1年5M
但是占的空间那么小,必然有其问题:
命令中没有任何一个可以取出集合中的数据,可见数据一旦存入,就不可能拿出来读取了,只能计算其中数量
计算数量也存在0.81%的错误率(官方给出)
因此这个类型只适用于只存只算不取的场景,且是不太在意错误率的场景
发布订阅机制
Redis也提供了一套类似于kafka这样的发布/订阅消息机制,即publisher和subscriber不直接通信,而是通过一个channel进行通信(类似kafka中的topic)
订阅该channel的每个subscriber都可以收到消息
发布订阅命令包括:
# 发布
publish channel message
# 订阅频道
subscribe channel1 [channel2 ...]
# 取消订阅
unsubscribe [channel1 channel2 ...]
# 按照模式订阅和取消订阅
psubscribe pattern [pattern...]
punsubscribe [pattern [pattern ...]]
# 查看活跃频道
pubsub channels [pattern]
# 查看频道订阅数
pubsub numsub [channel ...]
查看订阅模式
pusub numpat
要注意的是,Redis的发布订阅机制相比于kafka,轻便但功能不足:无法进行消息持久化,新订阅的subscriber是无法接收到channel中历史发布的数据,而kafka通过cursor的逻辑实现这一点,因此Redis发布订阅无法处理消息堆积和回溯的问题
那么Redis的发布订阅适合什么场景呢?例如新闻咨询推送、广告推送
没有一个新用户注册新闻软件之后会希望收到这个软件历史发布的所有推送的
GEO地理信息定位
可以了解下这种用法,它的底层是zset有序集合,可以使用如下命令添加地理信息:
# 添加经纬度信息
geoadd key longitude1 latitude1 member1 [longitude2 latitude2 member2 ...]
其中logitude代表经度,latitude1代表维度,member是成员
geoadd cities 116.28 39.55 beijing
# 获取经纬度信息
geopos key member [member ...]
# 获取两个地理位置的距离
geodist key member1 member2 [unit]
获取距离这个功能就比较强大了,省去了开发自己的计算,其中支持的unit包括:
m:米km:公里
mi:英里
ft:尺
# 获取指定位置范围内的地理信息位置集合
georadius key longitude latitude radiusm|km|ft|mi [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key] [storedist key]
georadiusbymember key member radiusm|km|ft|mi [withcoord] [withdist] [withhash] [COUNT count] [asc|desc] [store key] [storedist key]
georadius和georadiusbymember两个命令的作用是一样的,都是以一个地 理位置为中心算出指定半径内的其他地理信息位置,不同的是georadius命令 的中心位置给出了具体的经纬度,georadiusbymember只需给出成员即可。其 中radiusm|km|ft|mi是必需参数,指定了半径(带单位)
# 获取geohash
geohash key member [member ...]
# 删除地理位置信息
zrem key member
因为底层是zset,可以直接用zrem删就行
评论区