1、什么是 big key?
对于不同的数据类型,big key 定义不同:
-
字符串类型:它的big体现在单个value值很大,一般认为超过10KB就是bigkey。 -
非字符串类型:哈希、列表、集合、有序集合,它们的big体现在元素个数太多。建议把集合类型的元素个数控制在1万个以下。
2、 big key 的危害:
-
影响性能,容易产生阻塞。 -
在 Redis 基本 IO 模型中,主要是主线程在执行操作,任何耗时的操作,例如 bigkey、全量返回等操作,都是潜在的性能瓶颈。 -
AOF 重写过程中:主进程 fork 出后台的子进程会阻塞住子进程,阻塞时间取决于整个实例的内存大小。当主线程收到新写或修改的操作时,主线程会申请新的内存空间,用来保存新写或修改的数据,如果操作的是 bigkey,也就是数据量大的集合类型数据,那么主线程会因为申请大空间而面临阻塞风险。因为操作系统在分配内存空间时,有查找和锁的开销,这就会导致阻塞。
-
-
造成网络拥塞:读取 bigkey 意味着需要消耗更多的网络流量,可能会对 Redis 服务器造成影响。 -
过期删除:过期删除时,容易产生阻塞。 -
迁移困难 -
内存空间不均匀:比如在 Redis cluster 或者 codis 中,会造成节点的内存使用不均匀。
总结就是:bigkey 的读写操作会阻塞线程,降低 Redis 的处理效率
阿里云建议:
当一个简单Key的Value过大或List、Hash等类型的数据中存储了大量的元素时,读取、删除这些数据的操作可能会花费过多的时间,阻塞单线程的Redis服务。此时您需要对内存结构进行优化,找出大Key并进行调整。
3、哪些场景下容易产生big key?
生产环境中遇到的几种不设置过期时间的情况:
-
多个服务共享相同的配置,把配置存储到Redis中。 -
把 Redis 当做队列,处理任务。消费没及时将导致队列越来越大。 -
把 Redis 当做数据库,存储信息,不设置过期时间。List, hash 表越来越大。
4、如何发现 Big key
-
使用 redis-cli 客户端的命令 --bigkeys -
生成 rdb 文件,离线分析 rdb 文件。比如:redis-rdb-cli,rdbtools; -
通过 scan 命令,对扫描出来的key进行类型判断,例如:string长度大于10K,list长度大于10240认为是big bigkeys
例子:寻找 big key 的 python 脚本:使用scan命令,每次扫描1000个key。 长度大于10240的key,则为big key。其中 string 类型,长度为10240,占用内存大约 10240 * 1 字节 = 10 kb
import sys
import redis
# 根据key的类型判断是否为big key
def check_big_key(r, k):
bigKey = False
length = 0
# 判断key的类型
try:
type = r.type(k)
if type == "string":
length = r.strlen(k)
elif type == "hash":
length = r.hlen(k)
elif type == "list":
length = r.llen(k)
elif type == "set":
length = r.scard(k)
elif type == "zset":
length = r.zcard(k)
except:
return
# 长度大于10240,则为 big key
if length > 10240:
bigKey = True
if bigKey :
print db,k,type,length
# 用scan命令寻找 big key
def find_big_key_normal(db_host, db_port, db_password, db_num):
r = redis.StrictRedis(host=db_host, port=db_port, password=db_password, db=db_num)
for k in r.scan_iter(count=1000):
check_big_key(r, k)
# 检查数据库分片是否有big key
def find_big_key_sharding(db_host, db_port, db_password, db_num, nodecount):
r = redis.StrictRedis(host=db_host, port=db_port, password=db_password, db=db_num)
cursor = 0
for node in range(0, nodecount) :
while True:
iscan = r.execute_command("iscan",str(node), str(cursor), "count", "1000")
for k in iscan[1]:
check_big_key(r, k)
cursor = iscan[0]
print cursor, db, node, len(iscan[1])
if cursor == "0":
break;
if __name__ == '__main__':
if len(sys.argv) != 4:
print 'Usage: python ', sys.argv[0], ' host port password '
exit(1)
db_host = sys.argv[1]
db_port = sys.argv[2]
db_password = sys.argv[3]
r = redis.StrictRedis(host=db_host, port=int(db_port), password=db_password)
# 节点数
nodecount = r.info()['nodecount']
keyspace_info = r.info("keyspace")
for db in keyspace_info:
print 'check ', db, ' ', keyspace_info[db]
if nodecount > 1:
# 有多个节点时
find_big_key_sharding(db_host, db_port, db_password, db.replace("db",""), nodecount)
else:
# 只有单个节点
find_big_key_normal(db_host, db_port, db_password, db.replace("db", ""))
5、如何优化 big key?
从两个方面来解决:
-
合理优化数据结构:
1、对较大的数据进行压缩处理。 2、拆分集合:将大的集合拆分成小集合(如以时间进行分片)或者单个的数据。
-
选择其他的技术来存储 big key:
3、或者使用其他的存储形式,考虑使用 cdn 或者文档性数据库 MongoDB。
6、如何删除 big key
直接使用 DEL 命令会发生什么?危险:同步删除 bigkey 会阻塞 Redis 其他命令,造成 Redis 阻塞。
-
推荐使用 UNLINK 命令,异步删除 bigkey,不影响主线程执行其他命令。 -
在业务的低峰期使用 scan 命令查找 big key,对于类型为集合的key,可以使用脚本逐一删除里面的元素。
[1] Redis中如何发现并优化big key? [2] Redis Big key 介绍 [3] Redis运维实战 [4] https://github.com/leonchen83/redis-rdb-cli [5] sripathikrishnan/redis-rdb-tools
Redis中的big key指的是超过一定大小的键值对,可能导致性能下降、内存不均匀分配和网络拥塞等问题。常见的产生场景包括配置存储、用作队列和数据库。通过redis-cli的--bigkeys命令、离线分析RDB文件或使用SCAN命令可以发现big key。优化策略包括数据结构的合理设计、压缩处理、拆分集合和选择其他存储技术。删除big key时,推荐使用UNLINK命令以避免阻塞。
1730

被折叠的 条评论
为什么被折叠?



