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-tool
文章转载自涂鸦图开心,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。




