慢查询分析
类似关系性数据库,redis也有机制来保存和查看慢查询
- 预设值 slowlog-log-slower-than
如果比这个值大,则会记录日志,默认值时10000,可以通过config get slowlog-log-slower-than
命令获取 - 日志存储 slowlog-max-len
同普通数据一样,慢查询日志也是存在内存当中,是一个先进先出的队列,而slowlog-max-len这个值,代表的就是该日志队列的长度的长度。同样可以通过CONFIG get slowlog-max-len
,默认是128 - 查询慢查询日志 slowlog subcommand [arg]
可以获取该条慢查询的id、时间戳、命令耗时和命令内容
pipeline
Pipeline机制能让一组Redis的命令,通过一次RTT传输到服务器,并将结果按顺序返回给客户端,在网络延时较大时,提升性能非常明显。因为很多情况下,redis的性能瓶颈都在网络上。
与原生批量命令如mset、hmset、hmget有以下不同:
- 原生批量命令是原子的,而pipepline不是
- 原生批量命令一般都是多个不同的key,而pipeline不是
- 原生批量命令是服务端支持的,而pipeline是客户端和服务端之间的
事务
同关系型数据类似,redis也支持事务,事务的写法如下:
1 | multi |
错误处理:
命令错误,例如set写成了sett,那么整个事务无法提交
运行时错误,例如sadd,写成了zadd,语法没有错,但运行时报错。这种场景会提交正确的命令,不支持回滚
对于事务过程中,其他客户端导致的修改,可以使用watch机制来抛弃这次事务,有点类似java中的CAS:
1
2
3
4
5
6
7
8
9
10set key java
#客户端1
watch key
multi
append key python
#客户端2
append key jedis
#客户端1
exec
(nil) # 也就是提交失败缺点不支持回滚,不过不会滚会带来以下好处
- 仅当使用错误的语法(并且在命令队列期间无法检测到该问题)或针对包含错误数据类型的键调用Redis命令时,该命令才能失败:这实际上意味着失败的命令是编程错误的结果, 以及一种很可能在开发过程中而不是生产过程中发现的错误。也就是说这种错误需要我们从程序中改
- Redis在内部得到了简化和更快,因为它不需要回滚的能力
Lua脚本
redis中使用Lua
在客户端中使用eval命令
1
2# eval [脚本内容] [key的个数] [key列表] [参数列表]
eval 'return "hello" .. KEYS[1]..ARGV[1]' 1 redis worldredis-cli --eval
直接执行文件,该方法本质和eval是一样的,过程如下
evalsha 除了以上方法,也可以使用evalsha将脚本加载在内存当中,以键值对的方式存储,其中key为脚本的SHA1校验值,value就是脚本。
1
2
3
4redis-cli script load "$ cat lua_get.lua"
"a4eb97a51a5fd626ad3966923fa44190a2283cbb" # 返回sha1值
# evalsha [脚本sha1值] [key的个数] [key列表] [参数列表]
evalsha a4eb97a51a5fd626ad3966923fa44190a2283cbb 1 redis worldLua在调用的时候可以反过来通过redis.call() ,实现对redis访问
Lua脚本在redsi中是原子执行的,我们可以利用Lua脚本来定制功能,并常驻在redis内存中。
redis管理Lua脚本
- script load 加载脚本
- script exists 判断是否存在sha1
- script flush 用于清除redis已加载的Lua脚本
- script kill 停止正在执行的Lua脚本,在耗时较长或脚本存在问题时很有用,但如果脚本正在写,则不会kill
HyperLogLog
HyperLogLog不是一种新的数据结构(实际结构是字符串),而是一种基数算法,它可以实现用极小的内存空间完成独立总数的统计。
什么叫独立总数统计
类似数据库中的distinct 关键字,也就是去重后的总数,这种情形使用计数器是无法做到的,但如果用set这种数据结构,则会占用大量内存。用途:只为了计算独立总数,不需要获取单条数据源
缺点:会有一定的误差(官方统计是0.81%),所以需要容忍一定的误差率。
三个命令:
- PFADD key element[element],添加一个数据 e.g pfadd user user1 user2 user3
- pfcount 统计独立总数
- pfmerge destkey sourcekey[…],将多个HyperLogLog合并
发布订阅
redis的消息发布,就是传统的发布/订阅模型,生产者产生消息到服务端,然后服务端会通知订阅了这个channel的订阅者。
发布 publish channel message
1
2127.0.0.1:6379> publish channel:test "i am test message"
(integer) 0 #返回0 代表没有观察者消息订阅 subscribe channel […] 订阅一个或多个channel
1
2
3
4
5
6
7
8
9
10
11
12
13#1. 客户端1 订阅该channel
SUBSCRIBE channel:test
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel:test"
3) (integer) 1
#2. 客户端2 发布消息到该channel
127.0.0.1:6379> publish channel:test "i am test message2"
(integer) 1
#3. 客户端1 收到通知
1) "message"
2) "channel:test"
3) "i am test message2"取消订阅 unsubscribe [channel]
取消之后就不再收到该channel发送的消息,另外再redis-cli客户端中我们无法执行这个命令,ctrl-c会退出该客户端,因此可以采用java客户端jedis来测试按照channel匹配模式来订阅或者取消 psubscribe pattern […]
查询订阅
- 查看活跃的channel,也就是有订阅客户端的channel: pubsub channels [pattern]
- 查看channel的订阅客户端数: pubsub numsub [channel …]
缺陷:
redis的发布订阅模型相对专业的消息队列如kafka、RocketMQ比较简单,不支持消息的持久化,也就是无法重复消费,或者回溯消息以及其他高级功能。
GEO
地理位置,支持存储地理位置信息来实现一些功能。
- 增加地理位置信息geoadd,结果返回成功的个数,如果已存在或者只是修改,返回0
- 获取地理位置信息geopos
- 获取两个地理位置之间的距离geodist
- 返回指定范围内的地理位置集合georadius和georadiusbymemeber,一个使用的地理位置值,一个是用成员
- 获取地理位置信息的hash值,hash值越长,代表越精确
1 | #geoadd key longitude latitude memeber [...other] |
geo利用的是有序列表(sorted set)这种结构,并结合geohash的一些特性来实现。我们可以尝试用zXXX命令来查看
1 | 127.0.0.1:6379> ZRANGE cities:location 0 -1 |
redis 自带的shell脚本
redis提供了redis-cli、redis-server等工具脚本。有时候很有用
redis-cli
使用redis-cli –help 获取使用指南- -r 重复执行命令多次 ./redis-cli -r 3 incr count
- -i 每隔几秒执行一次,和-r参数一起使用
- -x 读取数据作为最后一个参数你。echo “world” | redis-cli -x set hello
- -a (auth),如果配置了密码,需要鉴权
- –slave,将客户端模拟redis的从节点
- –bigkeys 查看大对象
redis-server,有个–test-memory 的参数,检测是否有足够内存
redis-benchmark,提供了很多性能测试
- -c 表示客户端的并发数量(client,默认是50)
- -n nums代表客户端请求总量
- -q 仅显示每秒请求的数量,也就是简要信息
- -r 随机向redis插入更多的键值对