1.线上为什么不能使用keys * ,flushdb,flushall命令?怎么禁用?
首先是为什么不能使用keys*,结合我们前几篇的内容我们可以知道,执行读写的主线程是单线程的,而且我们线上的数据一般是非常大的数量集,所以使用keys * 查全部的key的时候肯定需要很长的时间通常都是秒级的,数据越多就越长,相对于我们redis的其他命令来说,这个时间导致的线程阻塞是灾难级的(因为其他客户端命令必须等待keys*执行完成,才能执行),这样就可以理解为什么不能使用了吧。
然后这个flushdb,flushall命令就不用多说了,首先他是十分危险的,其次他删除数据时间虽然没有keys*慢,但是基本上也是秒级的,100w的数据大概是1-2秒,其他先不说,这个危险的命令线上肯定是不能使用的。
那怎么禁用这个命令?我们需要在redis的配置文件中找到security,然后找到rename-command 这个关键词,进行设置,把需要禁用的命令如rename-command keys ""这样配置好,就可以了。rename-command <需要禁用的命令> ""这个是语法格式,用来禁用命令的。
2.keys *被禁用我们应该使用什么来查询?
我们可以使用scan来进行扫描查询,这个是扫描命令的一些延续,语法格式都是一样的只是查询的数据类型不同罢了。大家可以去查看https://redis.com.cn/commands/scan.html
这个游标在我们学C的时候应该就有出现过,主包记得应该是11章还是10章反正就是讲文件的时候,我们之前说mysql的窗口函数的时候应该也说过,在redis中游标是从0开始的,也是从0结束的,为什么这么说?假如我们想查询所以是k1、k2、k3这种格式的key,那么查询的命令就是如下:scan 0 match k* 10,这个时候会返回你一个数组,第一个元素返回的就是下一次开始的游标,第二个就是这次查询到的key,如果这一次查询没有查到,那么就再查scan 100(假设返回的是100) match k* 10,然后我们再进行查询,返回的数据结构还是一样的,如果还没有我们以此类推,直到返回的游标是0了表示已经全部扫描完了,再扫描又是从头开始了。
3.什么是BigKey?什么是MoreKey?
首先我们先来看Bigkey,这个从字面上就很好理解,也就是是大建的意思,但其实指的是一个键的值非常大,这个key被称之为Bigkey,通常情况下String类型的大于10kb,hash、zset、set、lst等超过5k个成员的就被称之为BigKey,并不是因为数据类型放不下,而是因为值或者成员太多了造成了很多问题,比如说:我们删除的时候会阻塞,为什么上面已经说过了,异曲同工之秒属于是,还有就是网络拥堵,读取一个大key需要的网络带宽更大,延迟就更高,还有就是迁移问题、RDB、AOF保存问题、内存分布不均问题,迁移和RDB、AOF是因为BigKey迁移需要跟多的时间,bigKey会让集群中部分节点的内存压力过大。
好了我们再来看MoreKey,这个就是和bigKey相反了,bigkey是值键的值过大,而moreKey是相同前缀的键太多,和值没多大关系,通常情况下相同前缀的key大于10w就是morekey了,这种情况下就需要调整一下key的前缀了,morekey的危害有如下:首先就是内存碎片化,通常情况下morekey的值都不大,所以内存就会不连续,然后就是使用scan命令会变慢,查询的次数会变多,这个很好理解就不锁了,其他就是都是小问题了,所以morekey没有bigkey棘手。
4.怎么删除BigKey?怎么优化BigKey?
首先删除,我们除了string类型可以使用del外,其他的数据类型应该使用渐进式删除,也就是通过scan命令一遍一遍的查询,把每次查询出来的成员一步一步的使用del命令删除掉,等到scan的游标为0就可以删除key了,区别就是string直接删除key,而其他bigkey的数据类型需要先删除value中的成员,把成员删除完了才能删除key。
然后我们再来看看如何优化Bigkey,lazy freeing 这个配置再redis的配置文件中,这些配置就能优化bigkey的问题,主要的解决思路就是异步删除,因为不止有bigkey会导致阻塞,还有大量过期时间相同的key清理的时候也会阻塞。
| 配置项 | 默认值 | 作用 | 适用场景 | 性能影响 | 风险 |
|---|---|---|---|---|---|
**lazyfree-lazy-eviction** |
no |
是否开启Lazy Free用于maxmemory策略的驱逐操作。 如果设置为yes,当Redis因为内存达到上限而需要删除键时,会尝试使用Lazy Free。 |
内存淘汰策略为volatile-*或allkeys-*时 | 减少淘汰阻塞,提高响应速度 | 内存回收延迟,可能短暂内存压力 |
**lazyfree-lazy-expire** |
no |
是否开启Lazy Free用于键的过期删除。 如果设置为yes,当Redis发现某个键已经过期并需要删除时,会尝试使用Lazy Free。 |
大量键同时过期场景 | 避免过期键删除阻塞主线程 | 过期键不会立即释放内存 |
**lazyfree-lazy-server-del** |
no |
如果是yes则异步处理RENAME/DEL等命令 | 执行RENAME、DEL等服务器命令时 | 减少命令执行阻塞时间 | 删除操作有延迟 |
**lazyfree-lazy-user-del** |
no |
如果是yes则将DEL命令自动转为UNLINK | 所有DEL命令执行场景 | DEL自动非阻塞,兼容性好 | 内存不会立即释放 |
**repl-disable-lazyfree** |
no |
如果是yes则在主从复制中使用lazyfree | 需要强一致性复制场景 | 保持同步复制,数据一致 | 可能增加主节点阻塞风险 |
5.如何查看bigkey?怎么计算key值的大小?
我们可以使用这个命令进行查看都有哪些bigkeys,这个命令需要在Linux中执行,不是在redis中执行的,他会扫描真个数据库,然后把bigkey给你列出来,
"命令": "redis-cli -h host -p port --bigkeys",
# 扫描整个数据库
Biggest string found 'user:session:123' has 1024 bytes
Biggest hash found 'product:stats' has 10000 fields
Biggest list found 'message:queue' has 50000 items
Biggest set found 'user:tags' has 20000 members
Biggest zset found 'leaderboard' has 30000 members""",
"优点": "官方工具,简单易用",
"缺点": "扫描期间影响性能,不能实时"
另外我们可以使用memory usage <键名>来查看这个key的值占了多少个字节。
{
"方法": "MEMORY USAGE",
"命令": "MEMORY USAGE key_name",
"输出示例": "127.0.0.1:6379> MEMORY USAGE user:profile:123\n(integer) 1048576",
"优点": "精确查看单个键内存使用",
"缺点": "需要知道键名,不能批量"
},
总结
本篇主要讲的就是Bigkey和MoreKey相关的内容。