redis查看某一个key的大小_学会这几个技巧,让Redis大key问题远离你

bad068ceb255b73713832ac32ff801e9.gif

作者 | 个推数据库工程师  嘉木

d2f6cf66f7ac29f4077846a2762c77b1.png

个推作为国内第三方推送市场的早期进入者,专注于为开发者提供高效稳定的推送服务,经过9年的积累和发展,服务了包括新浪、滴滴在内的数十万APP。由于我们推送业务对并发量、速度要求很高,为此,我们选择了高性能的内存数据库Redis。然而,在实际业务场景中我们也遇到了一些Redis大key造成的服务阻塞问题,因此积累了一些应对经验。本文将对大key的发现、解决大key删除造成的阻塞做相应的介绍。

Redis大key的一些场景及问题

大key场景

Redis使用者应该都遇到过大key相关的场景,比如:

1、热门话题下评论、答案排序场景。

2、大V的粉丝列表。

3、使用不恰当,或者对业务预估不准确、不及时进行处理垃圾数据等。

大key问题

由于Redis主线程为单线程模型,大key也会带来一些问题,如:

1、集群模式在slot分片均匀情况下,会出现数据和查询倾斜情况,部分有大key的Redis节点占用内存多,QPS高。

2、大key相关的删除或者自动过期时,会出现qps突降或者突升的情况,极端情况下,会造成主从复制异常,Redis服务阻塞无法响应请求。大key的体积与删除耗时可参考下表:

key类型

 field数量

耗时

Hash

~100万

~1000ms

List

~100万

~1000ms

Set

~100万

~1000ms

Sorted Set

~100万

~1000ms

Redis 4.0之前的大key的发现与删除方法

1、redis-rdb-tools工具。redis实例上执行bgsave,然后对dump出来的rdb文件进行分析,找到其中的大KEY。

2、redis-cli --bigkeys命令。可以找到某个实例5种数据类型(String、hash、list、set、zset)的最大key。

3、自定义的扫描脚本,以Python脚本居多,方法与redis-cli --bigkeys类似。

4、debug object key命令。可以查看某个key序列化后的长度,每次只能查找单个key的信息。官方不推荐。

redis-rdb-tools工具 

关于rdb工具的详细介绍请查看链接https://github.com/sripathikrishnan/redis-rdb-tools,在此只介绍内存相关的使用方法。基本的命令为 rdb -c memory dump.rdb (其中dump.rdb为Redis实例的rdb文件,可通过bgsave生成)。

输出结果如下:

database,type,key,size_in_bytes,encoding,num_elements,len_largest_element

0,hash,hello1,1050,ziplist,86,22,

0,hash,hello2,2517,ziplist,222,8,

0,hash,hello3,2523,ziplist,156,12,

0,hash,hello4,62020,hashtable,776,32,

0,hash,hello5,71420,hashtable,1168,12,

可以看到输出的信息包括数据类型,key、内存大小、编码类型等。Rdb工具优点在于获取的key信息详细、可选参数多、支持定制化需求,结果信息可选择json或csv格式,后续处理方便,其缺点是需要离线操作,获取结果时间较长。

redis-cli --bigkeys命令

Redis-cli --bigkeys是redis-cli自带的一个命令。它对整个redis进行扫描,寻找较大的key,并打印统计结果。

例如redis-cli -p 6379 --bigkeys

#Scanning the entire keyspace to find biggest keys as well as

#average sizes per key type.  You can use -i 0.1 to sleep 0.1 sec

#per 100 SCAN commands (not usually needed).

[00.72%] Biggest hash   found so far 'hello6' with 43 fields

[02.81%] Biggest string found so far 'hello7' with 31 bytes

[05.15%] Biggest string found so far 'hello8' with 32 bytes

[26.94%] Biggest hash   found so far 'hello9' with 1795 fields

[32.00%] Biggest hash   found so far 'hello10' with 4671 fields

[35.55%] Biggest string found so far 'hello11' with 36 bytes

-------- summary -------

Sampled 293070 keys in the keyspace!

Total key length in bytes is 8731143 (avg len 29.79)

Biggest string found 'hello11' has 36 bytes

Biggest   hash found 'hello10' has 4671 fields

238027 strings with 2300436 bytes (81.22% of keys, avg size 9.66)

0 lists with 0 items (00.00% of keys, avg size 0.00)

0 sets with 0 members (00.00% of keys, avg size 0.00)

55043 hashs with 289965 fields (18.78% of keys, avg size 5.27)

0 zsets with 0 members (00.00% of keys, avg size 0.00)

我们可以看到打印结果分为两部分,扫描过程部分,只显示了扫描到当前阶段里最大的key。summary部分给出了每种数据结构中最大的Key以及统计信息。

redis-cli --bigkeys的优点是可以在线扫描,不阻塞服务;缺点是信息较少,内容不够精确。扫描结果中只有string类型是以字节长度为衡量标准的。List、set、zset等都是以元素个数作为衡量标准,元素个数多不能说明占用内存就一定多。

自定义Python扫描脚本

通过strlen、hlen、scard等命令获取字节大小或者元素个数,扫描结果比redis-cli --keys更精细,但是缺点和redis-cli --keys一样,不赘述。

总之,之前的方法要么是用时较长离线解析,或者是不够详细的抽样扫描,离理想的以内存为维度的在线扫描获取详细信息有一定距离。由于在redis4.0前,没有lazy free机制;针对扫描出来的大key,DBA只能通过hscan、sscan、zscan方式渐进删除若干个元素;但面对过期删除键的场景,这种取巧的删除就无能为力。我们只能祈祷自动清理过期key刚好在系统低峰时,降低对业务的影响。

Redis 4.0之后的大key的发现与删除方法

Redis 4.0引入了memory usage命令和lazyfree机制,不管是对大key的发现,还是解决大key删除或者过期造成的阻塞问题都有明显的提升。

下面我们从源码(摘自Redis 5.0.4版本)来理解memory usage和lazyfree的特点。

memory usage

{"memory",memoryCommand,-2,"rR",0,NULL,0,0,0,0,0}(server.c 285⾏)void memoryCommand(client *c) {      /*...*/      /*计算key大小是通过抽样部分field来估算总大小。*/      else if (!strcasecmp(c->argv[1]->ptr,"usage") && c->argc >= 3) {        size_t usage = objectComputeSize(dictGetVal(de),samples);      /*...*/    }}(object.c 1299⾏)
从上述源码看到memory usage是通过调用objectComputeSize来计算key的大小。我们来看objectComputeSize函数的逻辑。
#define OBJ_COMPUTE_SIZE_DEF_SAMPLES 5 /* Default sample size. */size_t objectComputeSize(robj *o, size_t sample_size) {        /*...代码对数据类型进行了分类,此处只取hash类型说明*/        /*...*/            /*循环抽样个field,累加获取抽样样本内存值,默认抽样样本为5*/            while((de = dictNext(di)) != NULL && samples < sample_size) {                ele = dictGetKey(de);                ele2 = dictGetVal(de);                elesize += sdsAllocSize(ele) + sdsAllocSize(ele2);                elesize += sizeof(struct dictEntry);                samples++;            }            dictReleaseIterator(di);            /*根据上一步计算的抽样样本内存值除以样本量,再乘以总的filed个数计算总内存值*/            if (samples) asize += (double)elesize/samples*dictSize(d);        /*...*/        }(object.c 779⾏)
由此,我们发现memory usage默认抽样5个field来循环累加计算整个key的内存大小,样本的数量决定了key的内存大小的准确性和计算成本,样本越大,循环次数越多,计算结果更精确,性能消耗也越多。我们可以通过Python脚本在集群低峰时扫描Redis,用较小的代价去获取所有key的内存大小。以下为部分伪代码,可根据实际情况设置大key阈值进行预警。
for key in r.scan_iter(count=1000):        redis-cli = '/usr/bin/redis-cli'        configcmd = '%s -h %s -p %s memory usage %s' % (redis-cli, rip,rport,key)        keymemory = commands.getoutput(configcmd)

lazyfree机制

Lazyfree的原理是在删除的时候只进行逻辑删除,把key释放操作放在bio(Background I/O)单独的子线程处理中,减少删除大key对redis主线程的阻塞,有效地避免因删除大key带来的性能问题。在此提一下bio线程,很多人把Redis通常理解为单线程内存数据库, 其实不然。Redis将最主要的网络收发和执行命令等操作都放在了主工作线程,然而除此之外还有几个bio后台线程,从源码中可以看到有处理关闭文件和刷盘的后台线程,以及Redis4.0新增加的lazyfree线程。

/* Background job opcodes */#define BIO_LAZY_FREE     2 /* Deferred objects freeing. */(bio.h 38⾏)

下面我们以unlink命令为例,来理解lazyfree的实现原理。

{"unlink",unlinkCommand,-2,"wF",0,NULL,1,-1,1,0,0},(server.c 137⾏)void unlinkCommand(client *c) {    delGenericCommand(c,1);}(db.c 490⾏)

通过这几段源码可以看出del命令和unlink命令都是调用delGenericCommand,唯一的差别在于第二个参数不一样。这个参数就是异步删除参数。

/* This command implements DEL and LAZYDEL. */void delGenericCommand(client *c, int lazy) {        /*...*/        int deleted  = lazy ? dbAsyncDelete(c->db,c->argv[j]) :                              dbSyncDelete(c->db,c->argv[j]);        /*...*/}(db.c 468⾏)

可以看到delGenericCommand函数根据lazy参数来决定是同步删除还是异步删除。当执行unlink命令时,传入lazy参数值1,调用异步删除函数dbAsyncDelete。否则执行del命令传入参数值0,调用同步删除函数dbSyncDelete。我们重点来看异步删除dbAsyncDelete的实现逻辑:

#define LAZYFREE_THRESHOLD 64/*定义后台删除的阈值,key的元素大于该阈值时才真正丢给后台线程去删除*/int dbAsyncDelete(redisDb *db, robj *key) {    /*...*/        /*lazyfreeGetFreeEffort来获取val对象所包含的元素个数*/        size_t free_effort = lazyfreeGetFreeEffort(val);        /* 对删除key进行判断,满足阈值条件时进行后台删除 */        if (free_effort > LAZYFREE_THRESHOLD && val->refcount == 1) {            atomicIncr(lazyfree_objects,1);            bioCreateBackgroundJob(BIO_LAZY_FREE,val,NULL,NULL);            /*将删除对象放入BIO_LAZY_FREE后台线程任务队列*/            dictSetVal(db->dict,de,NULL);            /*将第一步获取到的val值设置为null*/        }    /*...*/}(lazyfree.c 53⾏)

上面提到了当删除key满足阈值条件时,会将key放入BIO_LAZY_FREE后台线程任务队列。接下来我们来看BIO_LAZY_FREE后台线程。

/*...*/else if (type == BIO_LAZY_FREE) {    if (job->arg1)        /* 后台删除对象函数,调用decrRefCount减少key的引用计数,引用计数为0时会真正的释放资源 */        lazyfreeFreeObjectFromBioThread(job->arg1);    else if (job->arg2 && job->arg3)        /* 后台清空数据库字典,调用dictRelease循环遍历数据库字典删除所有key */        lazyfreeFreeDatabaseFromBioThread(job->arg2,job->arg3);    else if (job->arg3)        /* 后台删除key-slots映射表,在Redis集群模式下会用*/        lazyfreeFreeSlotsMapFromBioThread(job->arg3);}(bio.c 197⾏)
unlink命令的逻辑可以总结为:执行unlink调用delGenericCommand函数传入lazy参数值1,来调用异步删除函数dbAsyncDelete,将满足阈值的大key放入BIO_LAZY_FREE后台线程任务队列进行异步删除。类似的后台删除命令还有flushdb async、flushall async。它们的原理都是获取删除标识进行判断,然后调用异步删除函数emptyDbAsnyc来清空数据库。这些命令具体的实现逻辑可自行查看flushdbCommand部分源码,在此不做赘述。除了主动的大key删除和数据库清空操作外,过期key驱逐引发的删除操作也会阻塞Redis服务。因此Redis4.0除了增加上述三个后台删除的命令外,还增加了4个后台删除配置项,分别为slave-lazy-flush、lazyfree-lazy-eviction、lazyfree-lazy-expire和lazyfree-lazy-server-del。slave-lazy-flush:slave接收完RDB文件后清空数据选项。建议大家开启slave-lazy-flush,这样可减少slave节点flush操作时间,从而降低主从全量同步耗时的可能性。lazyfree-lazy-eviction:内存用满逐出选项。若开启此选项可能导致淘汰key的内存释放不够及时,内存超用。lazyfree-lazy-expire:过期key删除选项。建议开启。lazyfree-lazy-server-del:内部删除选项,比如rename命令将oldkey修改为一个已存在的newkey时,会先将newkey删除掉。如果newkey是一个大key,可能会引起阻塞删除。建议开启。上述四个后台删除相关的参数实现逻辑差异不大,都是通过参数选项进行判断,从而选择是否采用dbAsyncDelete或者emptyDbAsync进行异步删除。

总结

在某些业务场景下,Redis大key的问题是难以避免的,但是,memory usage命令和lazyfree机制分别提供了内存维度的抽样算法和异步删除优化功能,这些特性有助于我们在实际业务中更好的预防大key的产生和解决大key造成的阻塞。关于Redis内核的优化思路也可从Redis作者Antirez的博客中窥测一二,他提出"Lazy Redis is better Redis"、"Slow commands threading"(允许在不同的线程中执行慢操作命令),异步化应该是Redis优化的主要方向。

Redis作为个推消息推送的一项重要的基础服务,性能的好坏至关重要。个推将Redis版本从2.8升级到5.0后,有效地解决了部分大key删除或过期造成的阻塞问题。未来,个推将会持续关注Redis 5.0及后续的Redis 6.0,与大家共同探讨如何更好地使用Redis。

参考文档:

 1、http://antirez.com/news/93

 2、http://antirez.com/news/126

88ef6236fe9b63d54473fbdee2894dfa.giff6b5a491964cd11170528eed1962c506.png030b0731aa9cfb8b77f5763ebb327955.png62967d132ca5c8ee7b9b45291d9e4fc9.gif

ce18094b7b9d29360711cee05cf228b9.gif

【WorkBuddy】WorkBuddy 智能体快速上手:从零到一的实践指南 文章摘要: 传统自动化脚本难以应对灵活多变的自然语言需求,导致逻辑复杂、维护成本高。智能体(Agent)通过理解语义意图、动态决策和松耦合组件解决了这些问题。WorkBuddy采用声明式配置,通过可视化界面定义意图、实体和对话策略,无需编码即可快速构建智能体。其核心组件包括意图分类、实体提取、对话策略、故事样本和回退机制,适用于客服支持、知识问答等高频场景。通过环境准备(Python/Node.js/Redis)和简单配置,用户可以快速搭建可运行的智能体,显著提升自动化效率。 阅读详情

相关推荐

10.redis的RDB工具分析key大小

redis的RDB工具分析key大小

qq_34953582的博客 431

Redis 排查 key 的3种方法,优化必备

Redis 实践总结(仅供参考):合理的 Key 中 Value 的字节大小,推荐小于 10 KB。过的 Value 会引发数据倾斜、热点Key、实例流量或 CPU 性能被占满等问题,应从设计源头上避免此类问题带来的性能影响。那么 value Bytes > 10 kb 可以作为判断 key一个参考值。 是 redis 自带的命令,对整个 Key 进行扫描,统计 string,list,set,zset,hash 这几个常见数据类型中每种类型里的最key。string 类型统计的是 valu

LoveSummer 1万+

【PINN高级教程】 第1讲:函数空间与逼近理论

结论:在解决偏微分方程(PDE)时,解往往属于某个 Sobolev 空间 HsHs,其正则性受限于 PDE 的系数与边界条件;而 PINN 在训练过程中实际上是在逼近 Barron 空间中的函数。由于 Barron 空间允许高频信息的压缩,PINN 可以在参数规模较小的情况下捕捉到 PDE 解所需的细节,但其正则性保证仍然比 Sobolev 更弱。因此,在设计 PINN 时,需要通过正则化或网络结构(如残差连接、归一化层)来“逼近”Sobolev 正则性。

u012133341的博客 220

redis查看一个key大小_分析redis key大小的几种方法

redis被用作缓存时,有时我们希望了解key大小分布,或者想知道哪些key占的空间比较。本文提供了几种方法。一. bigKeys这是redis-cli自带的一个命令。对整个redis进行扫描,寻找较key。例:redis-cli -h b.redis -p 1959 --bigkeys1输出:# Scanning the entire keyspace to find biggest ...

weixin_35478664的博客 5652

分析redis key大小的几种方法

本文系转载,便于整理和归档。原文地址 https://cloud.tencent.com/developer/article/1757281 当redis被用作缓存时,有时我们希望了解key大小分布,或者想知道哪些key占的空间比较。本文提供了几种方法。 一. bigKeys 这是redis-cli自带的一个命令。对整个redis进行扫描,寻找较key。例: redis-cli -h b.redis -p 1959 --bigkeys 输出: # Scanning the entire key.

wejack的专栏 2289

redis查看一个key占用了多少内存

查看 Redis一个键(key)占用的内存大小,可以使用 Redis 的 MEMORY USAGE 命令。该命令会返回指定键的内存占用大小(以字节为单位)。

CherryXieのblog 6520

如何优化 Redis Key 问题

Redis 中, Key 是指单个键值对的数据量非常,可能包含量数据。例如,存储一个非常的列表、哈希表、集合或有序集合等。这种 Key 可能会影响 Redis 的性能和可用性,因为 Redis 需要在内存中处理这些数据,并且在操作这些 Key 时可能会导致网络传输延迟。处理 Redis Key 的挑战需要结合多种策略和工具,包括识别 Key、优化存储方式、使用分布式功能以及定期监控性能。通过实施这些优化措施,可以显著提高 Redis 的性能和稳定性,确保应用程序的高效运行。

m0_73467713的博客 2417

Rediskey 问题

Rediskey 问题 背景 双十一促期间, 收到客服反馈通知,说 APP 领券接口缓慢。找到一个case,通过调用链路发现,是操作redis 缓慢,并且还搜到一些redis 异常。 最后定位到原因:是发券场景下拿redis 做了一个缓存券批次的操作,记录用户当天领取的所有券批次 发券场景: key = userId, value = 券批次ID 列表, 而redis 查询发现多了许多key,体现在 一个用户领取的几千甚至上万张优惠券,导致Redis 查询缓慢,甚至异常。至于为何有的用户会领取这么多优

只有变秃 才能变强 1万+

Redis如何解决Key问题

### **1. 什么是 Key?** ** Key(Big Key)** 指的是 **单个 Key 的数据量特别**,通常体现在: - **单个 String 类型的 Key** 存储了超长的内容(如超 JSON、Base64 图片)。 - **单个 List/Set/Zset/Hash 存储量元素**,导致: - **查询效率下降**(一次查询数据过多)。 - **删除或过期开销**(删除一个 Key 可能会卡 Redis)。 - **主从复制或数据持久化时阻塞 Redis**(

人心有反复,好在山水有重逢。 1822

RedisKey问题全解析

RedisKey是指单个Key对应的数据量过,占用过多的内存或导致操作耗时较长的现象。String类型:单个字符串的长度过。List类型:包含量元素的列表。Hash类型:存储量字段的哈希表。Set或ZSet类型:存储量成员的集合或有序集合。Key并不直接导致系统问题,但其潜在影响和风险非常显著,尤其在生产环境中。编写脚本遍历Redis中的Key,并分析每个Key大小和类型。

hello.reader 2357

google的api key调用次数是多少_学会几个技巧,让Rediskey问题远离

作者 | 个推数据库工程师 嘉木个推作为国内第三方推送市场的早期进入者,专注于为开发者提供高效稳定的推送服务,经过9年的积累和发展,服务了包括新浪、滴滴在内的数十万APP。由于我们推送业务对并发量、速度要求很高,为此,我们选择了高性能的内存数据库Redis。然而,在实际业务场景中我们也遇到了一些Rediskey造成的服务阻塞问题,因此积累了一些应对经验。本文将对key的发现、解决...

weixin_39909366的博客 591

RedisKey问题的解决方案

Redis中某些键(key)所对应的值(value)特别,或者集合类数据结构(如hash、set、zset、list)中存储的元素数量过多,这就是Key问题。具体大小标准依据业务场景不同而有所变化,一般认为在普通业务场景下,如果单个String类型的value于1MB,或者在高并发低延迟场景中于10KB,就可能被视为Key

m0_64469199的博客 3486

redis 怎么查询一个key有多

在项目开发过程中,最好是能估计出自己开发的功能要使用多redis内存 使用redis自带的方法 debug object key 127.0.0.1:6006> get testkey "this is a test" 127.0.0.1:6006> debug object testkey Value at:0x7f4f19e2b500 refcount:1 encoding:embstr serializedlength:15 lru:14327341 lru_seconds_idle:

班门弄斧 9666

redis中查找key方法汇总

redis作为一个高性能内存数据库,在实际业务中应用的非常广泛,虽然redis的性能很好,但是在实际使用过程中,如果使用不当,也会造成一些性能问题,比如数据中存在key。什么是key?顾名思义就是单个key中的数据比较,通常来说,单个key的value值不会很,这种情况下,key的读取,删除操作不会影响性能,如果value过,读取或删除会相对耗时,家都知道,redis是单线程,耗时操作就会阻塞其它请求,给性能上带来一些影响。

2401_87878486的博客 2993

redis查看一个key大小_redis查询key的内存大小

通过redis-rdb-tools工具进行查询。1、环境:centos7.x、python2.7.7查询python版本:python -V2、安装pip:下载:wget https://bootstrap.pypa.io/get-pip.py安装:python get-pip.py查询版本:pip -V3、安装redis-rdb-tools解压:unzip master.zip进入解压目录:c...

weixin_39716921的博客 4676

redis hash删除所有key_Redis笔记

一、Redis基础1、简介remote dictionary server 远程字典服务 C语言编写,基于内存,可持久化的日志型key value数据库。他会周期性将更新的数据写入磁盘或把修改操作追加到记录文件,且在此基础上实现了主从同步。2、redis能干嘛内存存储,持久化,内存中是断电及失,所以持久化很重要(rdb/aof)效率高,用于高速缓存发布订阅系统地图信息分析3、redis...

weixin_39941721的博客 5395

[redis]-redis查看key大小

方法一(推荐)使用rbdtools 高效 #安装rdbtools yum -y install gcc yum -y install epel-release yum -y install python-pip yum install python-devel 不安装会报错Python.h:没有那个文件或目录 pip install rdbtools rdb -c memory...

爷来辣的博客 2万+

realtek_8367rb 8363nb_switch_rk调试总述1

1、swtich 相关口线配置2、需要确定 RTL8367RB 的复位管脚,以及复位延迟3、需要确认我们主控端网口 pin 脚的复用关系是否配置正确1、确

上一篇: 完全复制一个dict_Python——dict字典和set集合
下一篇: 手机号脱敏处理_php数据脱敏处理(身份证,手机号等)
weixin_39576018
博客等级 码龄9年 102粉丝 138原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值