内容:
redis audit工具是使用ruby语言编写的脚本,用来分析redis哪些key占内存比较多,及占用内存较多的key键的过期时间。在分析redis内存增长时非常有帮助。在分析时,为了防止对生产库的影响,一般在从库上执行。但是在使用redis audit分析redis cluster从库实例时发现报错不能使用。于是花了些时间分析原因并使之能够使用redis cluster从库实例。
背景:
有个redis集群由于接入了新业务系统,导致redis cluster内存8个实例增加了64g内存。另外还在以每天8g的速度增加。
使用工具ruby的redis audit查询哪些key占内存比较多,发现这个工具还不能支持redis cluster环境。
抽查了几个key的过期时间,发现都是千万年的过期时间。下面这个key是4万6千年。
127.0.0.1:6410> ttl "b7b9dd35-1149-41ab-94d5-14906a68ab50_1464403073675"
(integer) 1462986538098
联系该系统的外购厂商查看,解释说这些key是少数的配置信息。其他内存是正常增长,要求扩容。
实在没办法了,只好将aof文件拷贝到一个非集群空实例上进行回放,再使用redis audit进行分析。
---------------------------------------------------+--------------+-------------------+---------------------------------------------------
Key | Memory Usage | Expiry Proportion | Last Access Time
---------------------------------------------------+--------------+-------------------+---------------------------------------------------
e005a0c4-242a-4888-8418-ca3b46b7951a_3547790698057 | 3.38% | 100.0% | 0 seconds
cbd4d269-8081-498b:10100000003215023 | 2.55% | 100.0% | 3 seconds
cbd4d269-488b-4fba-8a71-7c5125cfe268_90e1865f11559 | 0.57% | 100.0% | 61 days, 13 hours, 3 minutes, 12 seconds
e82192ea-439b-4b18-ad79-1b03d440042b_0000000000000 | 0.36% | 100.0% | 0 seconds
fff01f23-881e-434d-8061-5229c2c67a0a_E12861F9-5418 | 0.28% | 100.0% | 61 days, 13 hours, 3 minutes, 13 seconds
2c69edf4-844d-4b6d-80ad-aa26046d5b34_015c7b47c43c1 | 0.17% | 100.0% | 0 seconds
==============================================================================
Found 8 keys containing strings, like:
[0;33m765b506e-9052-4002-b2e4-c2062e392da6_1464172442715, 74be1490-1501-4634-bfb1-9ea25b14ed9a_1464923898832, 27b4e195-0208-4434-9345-91a2f127c173_1464572544882, 32b18e20-0997-4c80-b335-f69ad857e86d_1464077619056, 2b5456e5-4948-4f7d-9a9f-2679b566a732_1464362686736, 3038b6e7-3929-4023-88ac-3eec1e198075_1462865085803, 71b10e08-8399-4572-a807-a2a59277a66f_1464250132607, 73b4706e-4016-4651-9755-06a44aa16a1a_1464751883853[0m
These keys use [0;1;4m0.08%[0m of the total sampled memory (16 bytes)
[0;1;4m100.0%[0m of these keys expire (8), with maximum ttl of 16938711 days, 9 hours, 33 minutes, 23 seconds
Average last accessed time: [0;1;4m0 seconds[0m - (Max: 0 seconds Min:0 seconds)
==============================================================================
Found 8 keys containing strings, like:
[0;33m47160e49-4421-44f1-a6aa-de40d00fa752_1464230697555, 752e6455-9511-4f71-9eff-5932d0b5c5d5_1461976447739, e0053117-4541-49f8-a998-da86ea0740f3_1464124239370, 5948206e-1219-4f76-a877-a535b4bf8a53_1464887459261, 4470937e-1641-4fd5-bba7-47ac21477536_1464716582874, 2595e912-8264-45f4-b109-ea06f843b4ef_1464825098739, 63821e85-9080-497f-9438-adae16a551cd_1463280482719, 774e7379-8333-412f-a7fa-b5b0b0d7e44f_1464358656511[0m
These keys use [0;1;4m0.08%[0m of the total sampled memory (16 bytes)
[0;1;4m100.0%[0m of these keys expire (8), with maximum ttl of 16938711 days, 9 hours, 33 minutes, 14 seconds
Average last accessed time: [0;1;4m0 seconds[0m - (Max: 0 seconds Min:0 seconds)
通过分析发现几乎所有的key都是万年以上的过期时间。要求厂商再次排查,原来是将过期时间单位看错了,多乘了几个0。
经过这次问题分析,发现分析过程比较麻烦,最好工具redis audit直接分析redis cluster。于是尝试查找下redis audit在redis cluster运行时出错的原因。
根据前面在redis cluster下运行出错的信息进行debug redis audit脚本,发现真实的报错如下:
redis 3.0的客户端工具redis-cli有个选项-c 是用来连接redis cluster集群的,但是ruby的redis客户端api没有提供类似功能。在网上查找了ruby访问redis cluster客户端redis-rb-cluster (https://github.com/antirez/redis-rb-cluster),
修改了redis-audit代码使用ruby类RedisCluster创建连接,运行后发现还是有同样的报错。
查看redis-rb-cluster提供的案例example.rb:调用的是get接口和set,并能从cluster中成功的设置和获取key
具体在redis-rb-cluster文件cluster.rb中封装的接口中,先是根据key获取slot,然后根据slot在获取可以所在的实例(也就是host和端口)。。
查看slot函数,发现redis cluster使用的是CCITT standards,XMODEM CRC 16 algorithm。百度了下CCITT:CCITT是国际电报电话咨询委员会的简称,它是国际电信联盟(ITU)的前身。Xmodem是在1978年由Ward Christensen创建的用于调制解调器纠错的协议。
了解了redis cluster连接方法,就很容易解决redis-audit工具的问题,将原来的连接方式修改为先获取randomkey,根据key找到对应的节点,并创建连接。
本来到这儿可以结束了,但是为了查找下这些key最终都指向哪个实例了,将选择的节点都打印出来,发现都是同一个节点:
192.168.0.5:6379
192.168.0.5:6379
192.168.0.5:6379
192.168.0.5:6379
192.168.0.5:6379
192.168.0.5:6379
而其slot都是在节点6379的slot区间:8192-10239。这个节点192.168.0.5是发起查找实例192.168.0.6的主库节点。这里解释下,为了防止redis audit分析工具对生产库的影响,规定redis audit只能对redis的从库执行,这样redis的主库是不受影响的。
那么为什么在redis cluster集群中在redis从实例上查询数据,连接会被定向到redis主实例上呢?查了redis cluster官方文档http://redis.io/commands/readonly,发现如果需要查询redis cluster的从实例数据,需要执行函数redis.readonly()。否则,redis cluster会自动将客户端的连接重新定向到从库对应的主库。
通过近两天的时间分析,了解了redis cluster的分片算法,学习了ruby连接redis和redis cluster的方法原理。并修改了redis audit工具,使之能够适用redis cluster的架构。
下面是经过和其他同事讨论规范后的redis audit修改代码:请见标黄部分:
[padba@localhost-redis-audit-master]$ more redis-audit.rb
#!/usr/bin/ruby
….省略其他代码
host = ARGV[0]
port = ARGV[1].to_i
db = ARGV[2].to_i
sample_size = ARGV[3].to_i
password = ARGV[4]
redis = Redis.new(:host => host, :port => port, :password => password, :db => db)
begin
redis.readonly()
rescue Exception => e
puts "The redis instance is not a cluster member."
End
修改后redis audit可以支持在redis cluster从库上进行分析了。
总结:
在使用redis audit分析redis cluster从库实例时,需要修改脚本redis-audit.rb,在连接redis时调用方法redis.readonly()方法,这个是redis cluster从库实例特殊要求的。随着redis cluster的广泛使用,希望能够对后续redis cluster从库的内存问题分析有所帮助。