BigKey

时间:2023-01-09 01:02:53

一、BigKey的常见面试题

在redis面试的时候,经常会遇到如下问题:

  • 怎么在海量数据中查询某一固定前缀的key?
  • 如何生产上限制危险命令的使用?
  • MEMORY USAGE 命令你用过吗
  • 多大算big,如何发现?如何删除?如何处理?
  • BigKey调优?lazyfree?
  • 数据库有1000w记录,如何遍历。

二、MoreKey案例

2.1.首先准备:Redis写入2000w条数据,在Linux下执行如下命令:

生成100W条redis批量设置kv的语句(key=kn,value=vn)写入到/tmp目录下的redisTest.txt文件中

for((i=1;i<=200*10000;i++)); do echo "set k$i v$i" >> /tmp/redisTest.txt ;done;

将上述生成的数据通过redis提供的 --pipe 命令插入到redis中

cat redisTest.txt | /opt/redis-7.0.8/src/redis-cli -h 127.0.0.1 -a 123456 -p 6379 --pipe

查看如下图:

BigKey

2.2.生产环境案例说明

具体参考:https://developer.aliyun.com/article/888757

BigKey

上述操作就说明了,在生产环境需要禁止一些危险命令的使用,例如:keys *、flushdb、flushall

2.3.禁止危险的命令

keys * 命令统计耗时比较耗时,这些危险的命令在生产环境必须禁止使用 ,通过在redis.conf配置文件中禁止这些命令的使用,,在配置文件1071行的位置,添加如下内容即可:

rename-command keys ""
rename-command flushdb ""
rename-command flushall ""

如下图所示:

BigKey

添加后,登录redis客户端,重启后,发现上述命令,已无法使用,如下图:

BigKey

2.4.不用keys*,避免卡顿应该用什么呢?

上述为了避免卡顿,禁用了keys *,那么后续查询键,改用什么命令呢?这时候就要scan命令出场了,scan命令类似于MySQL的limit命令,但是却不完全相同,参考网址:http://redis.cn/commands/scan/,

SCAN 命令及其相关的 SSCAN, HSCAN 和 ZSCAN 命令都用于增量迭代一个集合元素。

  • SCAN 命令用于迭代当前数据库中的key集合。
  • SSCAN 命令用于迭代SET集合中的元素。
  • HSCAN 命令用于迭代Hash类型中的键值对。
  • ZSCAN 命令用于迭代SortSet集合中的元素和元素对应的分值

以上列出的四个命令都支持增量式迭代,它们每次执行都只会返回少量元素,所以这些命令可以用于生产环境,而不会出现像 KEYS 或者 SMEMBERS 命令带来的可能会阻塞服务器的问题。

不过,SMEMBERS 命令可以返回集合键当前包含的所有元素, 但是对于SCAN这类增量式迭代命令来说,有可能在增量迭代过程中,集合元素被修改,对返回值无法提供完全准确的保证。

因为 SCAN, SSCAN, HSCAN 和 ZSCAN 四个命令的工作方式都非常相似, 所以这个文档会一并介绍这四个命令,需要注意的是SSCAN, HSCAN ,ZSCAN命令的第一个参数总是一个key; SCAN 命令则不需要在第一个参数提供任何key,因为它迭代的是当前数据库中的所有key。

 

BigKey

SCAN 命令是一个基于游标的迭代器,每次被调用之后, 都会向用户返回一个新的游标, 用户在下次迭代时需要使用这个新游标作为 SCAN 命令的游标参数, 以此来延续之前的迭代过程。

SCAN 返回一个包含两个元素的数组,

  • 第一个元素是用于进行下一次迭代的新游标,
  • 第二个元素则是一个数组, 这个数组中包含了所有被迭代的元素。如果新游标返回零表示迭代已结束。

SCAN的遍历顺序

  • 它不是从第一维数组的第零位一直遍历到末尾,而是采用了高位进位加法来遍历。之所以使用这样特殊的方式进行遍历,是考虑到字典的扩容和缩容时避免槽位的遍历重复和遗漏。

案例:

BigKey

2.5.BigKey发现删除优化策略

一个key对应的值不能无限制的扩大,那么会引发出来一些列问题,

(1).多大的才是Bitkey,如何界定

在阿里云redis开发规范一文中说明:string类型控制在10KB以内,hash、list、set、zset元素个数不要超过5000,非字符串的bigkey,不要使用del删除,使用hscan、sscan\zscan方式间进式删除.

(2).key值过大带来的危害

  • 内存不均,在集群中迁移困难
  • 超时删除,大key难以删除
  • 网络流量阻塞

(3).那些场景下会产生大key

  • 社交类app,某个主播的粉丝数不断的增长
  • 汇总统计:某个报表常年累月的积累

(4).如何发现这些大key

通过 –bigkeys 参数实现查找大key
  • 优势:给出每种数据结构Top 1 bigkey,同时给出每种数据类型的键值个数+平均大小
  • 缺点:想查询大于10kb的所有key,--bigkeys参数就无能为力了,需要用到memory usage来计算每个键值的字节数

如下:

redis-cli -a 123456 -p 6379 --bigkeys

上述命令每隔 100 条 scan 指令就会休眠 0.1s,ops 就不会剧烈抬升,但是扫描的时间会变长,执行效果如下图:

BigKey

通过 MEMORY USAGE键

memoy usage用于计算每个键值的字节数,语法结构如下:

MEMORY USAGE key [SAMPLES count]

命令MEMORY USAGE 给出一个key和它值在RAM中占用的字节数,返回的结果是key的值以及为管理该key分配的内存总字节数,对于嵌套数据类型,可以使用选项SAMPLES,其中COUNT表示抽样的元素个数,默认值为5。当需要抽样所有元素时,使用SAMPLES 0

127.0.0.1:6379>
127.0.0.1:6379> MEMORY USAGE "k1735682"
(integer) 72
127.0.0.1:6379>

(5).如何删除这些大key

  • string使用del,过于庞大使用unlink
# 删除string 
127.0.0.1:6379> del k1735682
(integer) 1
127.0.0.1:6379>
  • hash:使用hscan每次获取少量fileid-value,在使用hdel删除每个fileid
  • list使用ltrim渐进式逐步删除,直到全部删除

语法如下:

LTRIM key start stop

修剪(trim)一个已存在的 list,这样 list 就会只包含指定范围的指定元素。start 和 stop 都是由0开始计数的, 这里的 0 是列表里的第一个元素(表头),1 是第二个元素,以此类推。如下:

BigKey

  • set 使用sscan每次获取部分元素,在使用stem命令删除每个元素

BigKey

  • zset使用zscan每次获取部分元素,在使用ZREMRANGEBYRANK命令删除每个元素
# 创建一个有序集合
127.0.0.1:6379>  ZADD myzset 2 "zhangsan" 3 "lisi" 4 "wangwu" 5 "zhaoliu"
(integer) 4
# 查看集合中的值
127.0.0.1:6379>  ZRANGE myzset 0 -1 WITHSCORES
1) "zhangsan"
2) "2"
3) "lisi"
4) "3"
5) "wangwu"
6) "4"
7) "zhaoliu"
8) "5"
# 当 SCAN 命令的游标参数被设置为 0 时, 服务器将开始一次新的迭代, 而当服务器向用户返回值为 0 的游标时, 表示迭代已结束
127.0.0.1:6379> ZSCAN myzset 0
1) "0"
2) 1) "zhangsan"
   2) "2"
   3) "lisi"
   4) "3"
   5) "wangwu"
   6) "4"
   7) "zhaoliu"
   8) "5"
# 删除0到1之间的值
127.0.0.1:6379> ZREMRANGEBYRANK myzset 0 1
(integer) 2
# 再次查看已经被删除
127.0.0.1:6379> ZSCAN myzset 0
1) "0"
2) 1) "wangwu"
   2) "4"
   3) "zhaoliu"
   4) "5"
127.0.0.1:6379>

2.6.BigKey 生产调优

在redis.conf的1206行 LAZY FREEING 进行配置,下面是阻塞和非阻塞命令说明:

BigKey

优化配置如下:

BigKey