今天就来聊一聊关于Redisbigkey的四个问题。什么是Redis大密钥?大键会导致什么问题?如何找到大钥匙?如何删除大密钥?什么是Redis大密钥?大key并不是说key的value大,而是key对应的value大。一般来说,以下两种情况称为大键:String类型的值大于10KB;Hash、List、Set、ZSet类型的元素个数超过5000个;大key会导致什么问题?大key会带来以下四种效果:客户端超时阻塞。由于Redis在单线程进程中执行命令,操作大key需要时间,Redis会被阻塞。从客户的角度来看,长时间没有任何反应。造成网络拥塞。每次获取大密钥,网络流量都比较大。如果一个key的大小是1MB,每秒的访问次数是1000,那么每秒就会产生1000MB的流量,对于普通千兆网卡的服务器来说是灾难性的。的。阻止工作线程。如果用del删除一个大的key,工作线程就会被阻塞,以至于后面的命令没法处理。内存分布不均。当集群模型存在均匀槽分片时,会出现数据和查询倾斜。一些key大的Redis节点占用内存大,QPS会比较大。如何找到大钥匙?1.redis-cli--bigkeys查找大键通过redis-cli--bigkeys命令可以找到大键:redis-cli-h127.0.0.1-p6379-a"password"--bigkeys使用时的注意事项:最好选择在从节点上执行命令。因为在主节点上执行时,主节点会被阻塞;如果没有从节点,可以选择在Re??dis实例业务压力低峰期进行扫描查询,以免影响实例的正常运行;或者您可以使用-i参数来控制扫描间隔,以避免长时间扫描降低Redis实例的性能。该方法的缺点:该方法只能返回每种类型最大的bigkey,无法获取前N个bigkey;对于集合类型,该方法只统计集合元素的数量,而不是实际使用的内存量。但是,集合中的元素数量多并不一定会占用大量内存。因为有可能每个元素占用的内存都很少。这样的话,即使元素很多,总的内存开销也不大;2.使用SCAN命令查找大key使用SCAN命令扫描数据库,然后使用TYPE命令获取返回的每个key的类型。对于String类型,可以直接使用STRLEN命令获取字符串的长度,即占用内存空间的字节数。对于集合类型,有两种获取其占用内存大小的方法:如果可以事先从业务层知道集合元素的平均大小,那么可以使用如下命令获取集合元素的个数,然后将集合元素乘以集合的平均大小,这样就可以得到集合占用的内存大小。列表类型:LLEN命令;哈希类型:HLEN命令;设置类型:SCARD命令;排序集类型:ZCARD命令;如果无法事先知道写入set中元素的大小,可以使用MEMORYUSAGE命令(Redis4.0及以上版本需要),查询一个键值对占用的内存空间。3、使用RdbTools工具查找bigkey使用RdbTools第三方开源工具,可以用来解析Redis快照(RDB)文件,查找bigkey。例如,以下命令将大于10kb的键输出到表文件。rdbdump.rdb-cmemory--bytes10240-fredis.csv如何删除bigkey?删除操作的本质是释放键值对占用的内存空间。不要小看内存释放过程。释放内存只是第一步。为了更有效地管理内存空间,当应用程序释放内存时,操作系统需要将释放的内存块插入到空闲内存块的链表中,以供后续管理和重新分配。该进程本身需要一些时间并阻塞当前正在释放内存的应用程序。因此,如果一次性释放大量内存,就会增加空闲内存块链表的运行时间,相应的Redis主线程就会被阻塞。如果主线程被阻塞,所有其他请求都可能超时,并且超时会增加,会导致Redis连接耗尽,引发各种异常。所以我们在删除大key的时候一定要小心。怎么做?这里给出两种方法:批量删除异步删除(Redis4.0以上)1.批量删除对于删除大的Hash,使用hscan命令每次获取100个字段,然后使用hdel命令一次删除1个领域。Python代码:defdel_large_hash():r=redis.StrictRedis(host='redis-host1',port=6379)large_hash_key="xxx"#待删除的大哈希键名cursor='0'whilecursor!=0:#使用hscan命令每次获取100个字段cursor,data=r.hscan(large_hash_key,cursor=cursor,count=100)foritemindata.items():#然后使用hdel命令一次删除一个字段timer.hdel(large_hash_key,item[0])删除一个大列表,通过ltrim命令每次删除少量元素。Python代码:defdel_large_list():r=redis.StrictRedis(host='redis-host1',port=6379)large_list_key='xxx'#要删除的大列表的键名whiler.llen(large_list_key)>0:#每次只删除最右边的100个元素r.ltrim(large_list_key,0,-101)删除一个大的Set,使用sscan命令每次扫描set中的100个元素,然后使用srem命令删除每次一个键。Python代码:defdel_large_set():r=redis.StrictRedis(host='redis-host1',port=6379)large_set_key='xxx'#要删除的largeset的键名cursor='0'whilecursor!=0:#使用sscan命令每次扫描集合中的100个元素cursor,data=r.sscan(large_set_key,cursor=cursor,count=100)foritemindata:#然后使用srem命令删除一个key每次r.srem(large_size_key,item)删除一个大的ZSet,每次使用zremrangebyrank命令删除前100个元素。Python代码:defdel_large_sortedset():r=redis.StrictRedis(host='large_sortedset_key',port=6379)large_sortedset_key='xxx'whiler.zcard(large_sortedset_key)>0:#使用zremrangebyrank命令每次删除前100个元素r.zremrangebyrank(large_sortedset_key,0,99)2.异步删除从Redis4.0版本开始可以使用异步删除的方式,使用unlink命令代替del进行删除。这样Redis会把key放到一个异步线程中删除,这样就不会阻塞主线程。
