最佳实践

予早 2024-10-19 16:11:16
Categories: Tags:

键值设计

key 结构设计

Redis 中对 key 并未规定格式,可以任意设置。key 结构设计应当具有可读性强、避免冲突的特点,最佳实践约定如下设计规范:

value 类型选用

例1:存储一个User对象。

方案一:整个对象转 JSON 字符串,统一存储

一个值为 string 类型的键值对
user:1  =>  {"name": "Jack", "age": 21}

优点:实现简单粗暴

缺点:数据耦合,不够灵活

方案二:字段打散,分别存储

两个值为 string 类型的键值对
user:1:name  =>  Jack
user:1:age  =>  21

优点:可以灵活访问对象任意字段

缺点:占用空间大、没办法做统一控制

方案三:hash

一个值为 hash 类型的键值对
user:1  => name: jack, age: 21

优点:底层使用ziplist,空间占用小,可以灵活访问对象的任意字段

缺点:代码相对复杂

上述三个方案,站在 Redis 应用角度上分析,hash 是最合适的

例2:百万键值对常驻内存

方案一:一个 value 为 hash 类型的键值对

优点:结构简单,可直接访问相关字段

缺点:单个 value 内存占用大,包含元素过多

方案二:拆分为百万 value 为 string 类型的键值对

优点:简短粗暴,可访问任意字段

缺点:string结构底层没有太多内存优化,内存占用较多,且批量获取数据比较麻烦

方案三:分组拆分,拆分为小的 hash,将 id / 100 作为 key,将 id % 100 作为 field,这样每组100个元素构成一个hash,hash 元素在500个以内使用 ZipList ,内存占用相对较小

优点:内存占用小

缺点:代码逻辑复杂

批处理

此处批处理用以节约网络传输耗时。一次命令的响应时间 = 1次往返的网络传输耗时 + 1次Redis执行命令耗时,则 N次命令的响应时间 = N次往返的网络传输耗时 + N次Redis执行命令耗时,若将N次命令打包,则只需一次网络传输,此时N次命令的响应时间 = 1次往返的网络传输耗时 + N次Redis执行命令耗时

批处理命令

Redis提供了很多Mxxx这样的命令,可以实现批量插入数据,例如:
mset
hmset

利用mset批量插入10万条数据:

@Test
void testMxx() {
    String[] arr = new String[2000];
    int j;
    long b = System.currentTimeMillis();
    for (int i = 1; i <= 100000; i++) {
        j = (i % 1000) << 1;
        arr[j] = "test:key_" + i;
        arr[j + 1] = "value_" + i;
        if (j == 0) {
            jedis.mset(arr);
        }
    }
    long e = System.currentTimeMillis();
    System.out.println("time: " + (e - b));
}

pipeline

MSET虽然可以批处理,但是却只能操作部分数据类型,因此如果有对复杂数据类型的批处理需要,建议使用Pipeline功能:

@Test
void testPipeline() {
    // 创建管道
    Pipeline pipeline = jedis.pipelined();
    long b = System.currentTimeMillis();
    for (int i = 1; i <= 100000; i++) {
        // 放入命令到管道
        pipeline.set("test:key_" + i, "value_" + i);
        if (i % 1000 == 0) {
            // 每放入1000条命令,批量执行
            pipeline.sync();
        }
    }
    long e = System.currentTimeMillis();
    System.out.println("time: " + (e - b));
}

注意事项

  1. 批处理时不建议一次携带太多命令,否则单次命令占用带宽过多,会导致网络阻塞
  2. Pipeline的多个命令之间不具备原子性
  3. 集群批处理

如MSET或Pipeline这样的批处理需要在一次请求中携带多条命令,而此时如果Redis是一个集群,那批处理命令的多个key必须落在一个插槽中,否则就会导致执行失败。

串行命令 串行slot 并行slot hash_tag
实现思路 for循环遍历,依次执行每个命令 在客户端计算每个key的slot,将slot一致分为一组,每组都利用Pipeline批处理。串行执行各组命令 在客户端计算每个key的slot,将slot一致分为一组,每组都利用Pipeline批处理。并行执行各组命令 将所有key设置相同的hash_tag,则所有key的slot一定相同
耗时 N次网络耗时 + N次命令耗时 m次网络耗时 + N次命令耗时m = key的slot个数 1次网络耗时 + N次命令耗时 1次网络耗时 + N次命令耗时
优点 实现简单 耗时较短 耗时非常短 耗时非常短、实现简单
缺点 耗时非常久 实现稍复杂slot越多,耗时越久 实现复杂 容易出现数据倾斜

命令执行原子性

命令执行原子性首选Lua脚本

服务端优化

持久化配置

Redis的持久化虽然可以保证数据安全,但也会带来很多额外的开销,因此持久化请遵循下列建议:

  1. 用来做缓存的Redis实例尽量不要开启持久化功能

  2. 建议关闭RDB持久化功能,使用AOF持久化

  3. 利用脚本定期在slave节点做RDB,实现数据备份

  4. 设置合理的rewrite阈值,避免频繁的bgrewrite

  5. 配置no-appendfsync-on-rewrite = yes,禁止在rewrite期间做aof,避免因AOF引起的阻塞

部署有关建议:

  1. Redis实例的物理机要预留足够内存,应对fork和rewrite

  2. 单个Redis实例内存上限不要太大,例如4G或8G。可以加快fork的速度、减少主从同步、数据迁移压力

  3. 不要与CPU密集型应用部署在一起

  4. 不要与高硬盘负载应用一起部署。例如:数据库、消息队列

redis缓存业务,单独redis实例,建议不配置持久化,要求安全性不高

redis分布式锁,单独redis实例,建议配置持久化。要求安全性比价高

rdb与aof,建议使用aof作为持久化措施。但是由于aof体积比较大,耗费大,需要设定合适的rewrite机制

rdb进行手动措施作为数据备份

no-appendfsync-on-rewrite=yes,

redis慢查询

应用部署,CPU密集、IO密集分离

慢查询

慢查询的阈值可以通过配置指定:

slowlog-log-slower-than:慢查询阈值,单位是微秒。默认是10000,建议1000

慢查询会被放入慢查询日志中,日志的长度有上限,可以通过配置指定:

slowlog-max-len:慢查询日志(本质是一个队列)的长度。默认是128,建议1000

修改这两个配置可以使用:config set命令:

查看慢查询日志列表:

安全

Redis会绑定在0.0.0.0:6379,这样将会将Redis服务暴露到公网上,而Redis如果没有做身份认证,会出现严重的安全漏洞.

漏洞重现方式:https://cloud.tencent.com/developer/article/1039000

漏洞出现的核心的原因有以下几点:

为了避免这样的漏洞,这里给出一些建议:

  1. Redis一定要设置密码
  2. 禁止线上使用下面命令:keys、flushall、flushdb、config set等命令。可以利用rename-command禁用。命令隐匿,uuid形式使用命令或者禁用命令,keys、flushall、flushdb、config set等命令,rename-command
  3. bind:限制网卡,禁止外网网卡访问
  4. 开启防火墙
  5. 不要使用Root账户启动Redis
  6. 尽量不是有默认的端口

内存配置

当Redis内存不足时,可能导致Key频繁被删除、响应时间变长、QPS不稳定等问题。当内存使用率达到90%以上时就需要我们警惕,并快速定位到内存占用的原因。

内存占用 说明
数据内存 是Redis最主要的部分,存储Redis的键值信息。主要问题是BigKey问题、内存碎片问题
进程内存 Redis主进程本身运⾏肯定需要占⽤内存,如代码、常量池等等;这部分内存⼤约⼏兆,在⼤多数⽣产环境中与Redis数据占⽤的内存相⽐可以忽略。
缓冲区内存 一般包括客户端缓冲区、AOF缓冲区、复制缓冲区等。客户端缓冲区又包括输入缓冲区和输出缓冲区两种。这部分内存占用波动较大,不当使用BigKey,可能导致内存溢出。

数据内存

Redis提供了一些命令,可以查看到Redis目前的内存分配状态:

内存缓冲区

内存缓冲区常见的有三种:

client-output-buffer-limit <class> <hard limit> <soft limit> <soft seconds>

<class>
客户端类型
normal:普通客户端
replica:主从复制客户端
pubsub:PubSub客户端

<hard limit>
缓冲区上限在超过limit后断开客户端

<soft limit> <soft seconds>
缓冲区上限,在超过soft limit 并且持续了 soft seconds秒后断开客户端 

默认配置

集群还是主从

集群虽然具备高可用特性,能实现自动故障恢复,但是如果使用不当,也会存在一些问题:

  1. 集群完整性问题
  2. 集群带宽问题
  3. 数据倾斜问题
  4. 客户端性能问题
  5. 命令的集群兼容性问题
  6. lua和事务问题

单体Redis(主从Redis)已经能达到万级别的QPS,并且也具备很强的高可用特性。如果主从能满足业务需求的情况下,尽量不搭建Redis集群。

集群带宽

集群节点之间会不断的互相Ping来确定集群中其它节点的状态。每次Ping携带的信息至少包括:

集群中节点越多,集群状态信息数据量也越大,10个节点的相关信息可能达到1kb,此时每次集群互通需要的带宽会非常高。
解决途径:

  1. 避免大集群,集群节点数不要太多,最好少于1000,如果业务庞大,则建立多个集群。

  2. 避免在单个物理机中运行太多Redis实例

  3. 配置合适的cluster-node-timeout值

集群插槽

在Redis的默认配置中,如果发现任意一个插槽不可用,则整个集群都会停止对外服务

为了保证高可用特性,这里建议将 cluster-require-full-coverage配置为false

注意,jedis没有解决集群下slot分组问题,spring提供的lettuce中解决了这个问题(异步slot)

Redis常见性能问题和解决方案?

  1. Master最好不要做任何持久化工作,包括内存快照和AOF日志文件,特
    别是不要启用内存快照做持久化。
  2. 如果数据比较关键,某个Slave开启AOF备份数据,策略为每秒同步一
    次。
  3. 为了主从复制的速度和连接的稳定性,Slave和Master最好在同一个局
    域网内。
  4. 尽量避免在压力较大的主库上增加从库
  5. Master调用BGREWRITEAOF重写AOF文件,AOF在重写的时候会占大
    量的CPU和内存资源,导致服务load过高,出现短暂服务暂停现象。
  6. 为了Master的稳定性,主从复制不要用图状结构,用单向链表结构更稳
    定,即主从关系为:Master<–Slave1<–Slave2<–Slave3…,这样的结构
    也方便解决单点故障问题,实现Slave对Master的替换,也即,如果
    Master挂了,可以立马启用Slave1做Master,其他不变。

假如Redis里面有1亿个key,其中有10w个key是以某个
固定的已知的前缀开头的,如果将它们全部找出来?
使用keys指令可以扫出指定模式的key列表。
对方接着追问:如果这个redis正在给线上的业务提供服务,那使用keys指令会
有什么问题?
这个时候你要回答redis关键的一个特性:redis的单线程的。keys指令会导致线
程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时
候可以使用scan指令,scan指令可以无阻塞的提取出指定模式的key列表,但是
会有一定的重复概率,在客户端做一次去重就可以了,但是整体所花费的时间会
比直接用keys指令长。

阿里广告平台,海量数据里查询某一固定前缀的key?

答:使用keys pattern来查询,但是面度大数量的数据,不推荐。

使用scan cursor [Match pattern] [Count count] 来限制匹配模式,和数量,是更好地选择。


小红书,你如何生产上限制keys */flushdb/flushall等危险命令以防止误删误用?

答:可以在配置文件加上 rename-command keys ""  来加以限制。


美团,MEMORY USAGE 命令你用过吗?

答:用过,这是用来查询特定key占用了多大内存的命令

memory usage key [Samples count] 对于嵌套式数据还可以加上一个count来表示抽样查询该key内的元素的个数,设为0表示查询所有。


BigKey问题,多大算big? 你如何发现? 如何删除? 如何处理?

答:String类型认为大于10k就算bigkey。其余类型的元素个数超过5000就视为bigkey。

使用 redis-cli -a 111111 --bigkeys 和 memory usage来发现他们

使用del 或 unlink来删除string类型的key

其余类型要配合scan来渐进式删除


BigKey你做过调优吗? 惰性释放lazyfree了解过吗?

答:可以在配置文件上设置懒惰模式,延后删除操作的释放内存的时间,优化数据同步策略,添加异步删除的配置等等。


Morekey问题,生产上redis数据库有1000W记录,你如何遍历? key *可以吗?

答:不能用keys * 会阻塞主线程非常长的时间,很危险。

要用scan cursor pattern count命令来少量多次的遍历

BigKey

BigKey 是指键值对中 value 占用内存多或包含数据数量多的 key。

危害

在Redis中,每个key都有一个对应的value,如果某个key的value过大,就会导致Redis的性能下降或者崩溃。

因为Redis需要将大key全部加载到内存中,这会占用大量的内存空间,会降低Redis的响应速度,这个问题被称为Big Key问题。

不要小看这个问题,它可是能让你的Redis瞬间变成“乌龟”,由于Redis单线程的特性,操作Big Key的通常比较耗时,也就意味着Big Key阻塞Redis的可能性很大,这样会造成客户端阻塞或者引起故障切换,有可能导致“慢查询”或其他连锁反应。

网络阻塞

对BigKey执行读请求时,少量的QPS就可能导致带宽使用率被占满,导致Redis实例,乃至所在物理机变慢

数据倾斜

BigKey所在的Redis实例内存使用率远超其他实例,无法使数据分片的内存资源达到均衡

Redis阻塞

对元素较多的hash、list、zset等做运算会耗时较旧,使主线程被阻塞

CPU压力

对BigKey的数据序列化和反序列化会导致CPU的使用率飙升,影响Redis实例和本机其它应用

判定

一般而言,下面这两种情况可以被称为Big Key:

以上对Big Key的判断标准并不唯一,只是一个大体的标准。在实际业务开发中,对Big Key的判断是需要根据具体的使用场景做不同的判断。比如操作某个 key 导致请求响应时间变慢,那么这个 key 就可以判定成 Big Key。

BigKey 的判定需要从 value 占用内存大小和 value 包含元素数量综合分析,也就是从 value 的数据类型这一数据结构的空间复杂度和时间复杂度两个维度分析,最佳实践约定如下判定标准:

正例:

  1. 单个 key 的 value 内存占用小于等于 10 kb
  2. 对于容器类型的 key,元素数量小于等于 1000

反例:

  1. string 类型 value 超过 5M
  2. ZSET 类型 value 包含元素超过 10000
  3. hash 类型 value 包含元素 300 但内存占用 超过 100 M

发现

redis-cli --bigkeys -c -a 123456 -p 8001 -i 0.1
info keyspace
[root@yc-cn-centos-abc ~]# redis-cli --bigkeys -c -a 123456 -p 8001 -i 0.1
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.

# 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.00%] Biggest list   found so far '"bk:list:key_162"' with 400 items
[00.00%] Biggest set    found so far '"bk:set:key_39"' with 1173 members
[00.00%] Biggest string found so far '"bk:str:key_32"' with 2263 bytes
[00.00%] Biggest string found so far '"bk:str:key_144"' with 32627 bytes
[00.00%] Biggest zset   found so far '"bk:zset:key_113"' with 187 members
[00.00%] Biggest zset   found so far '"bk:zset:key_54"' with 4745 members
[00.00%] Biggest list   found so far '"bk:list:key_200"' with 1856 items
[00.00%] Biggest list   found so far '"bk:list:key_13"' with 8127 items
[06.12%] Biggest set    found so far '"bk:set:key_177"' with 5030 members
[06.12%] Biggest hash   found so far '"bk:hash:key_192"' with 1572 fields
[09.17%] Biggest hash   found so far '"bk:hash:key_123"' with 4904 fields
[09.17%] Biggest hash   found so far '"bk:hash:key_83"' with 5050 fields
[15.29%] Biggest zset   found so far '"bk:zset:key_51"' with 4948 members
[18.35%] Biggest hash   found so far '"bk:hash:key_11"' with 5215 fields
[24.77%] Biggest hash   found so far '"bk:hash:key_2"' with 5389 fields
[27.83%] Biggest set    found so far '"bk:set:key_49"' with 5302 members
[31.19%] Biggest zset   found so far '"bk:zset:key_43"' with 5053 members
[47.09%] Biggest set    found so far '"bk:set:key_2"' with 5389 members
[47.09%] Biggest list   found so far '"bk:list:key_1"' with 9079 items
[47.09%] Biggest zset   found so far '"bk:zset:key_58"' with 5176 members
[56.27%] Biggest zset   found so far '"bk:zset:key_11"' with 5215 members
[75.23%] Biggest list   found so far '"bk:list:key_40"' with 9842 items
[84.40%] Biggest zset   found so far '"bk:zset:key_73"' with 5322 members
[90.52%] Biggest list   found so far '"bk:list:key_16"' with 11010 items

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

Sampled 327 keys in the keyspace!
Total key length in bytes is 4597 (avg len 14.06)

Biggest   list found '"bk:list:key_16"' has 11010 items
Biggest   hash found '"bk:hash:key_2"' has 5389 fields
Biggest string found '"bk:str:key_144"' has 32627 bytes
Biggest    set found '"bk:set:key_2"' has 5389 members
Biggest   zset found '"bk:zset:key_73"' has 5322 members

67 lists with 188982 items (20.49% of keys, avg size 2820.63)
67 hashs with 142398 fields (20.49% of keys, avg size 2125.34)
66 strings with 1117562 bytes (20.18% of keys, avg size 16932.76)
0 streams with 0 entries (00.00% of keys, avg size 0.00)
62 sets with 162046 members (18.96% of keys, avg size 2613.65)
65 zsets with 133174 members (19.88% of keys, avg size 2048.83)

解读:

debug object key

debug object bk:list:key_16
127.0.0.1:8001> debug object bk:list:key_16
Value at:0x18a1f80 refcount:1 encoding:quicklist serializedlength:54860 lru:3185183 lru_seconds_idle:2616 ql_nodes:18 ql_avg_node:611.67 ql_ziplist_max:-2 ql_compressed:0 ql_uncompressed_size:140007

memory usage key

专用于分析key内存占用,string类型直接获取内存占用,list、hash、set、zset、stream类型采用抽样方式进行估计,抽样数量越接近总数估计值越准确,不指定抽样数量则默认为5个

# bk:list:key_16包含11010个元素
memory usage bk:list:key_16  # 121575
memory usage bk:list:key_16 samples 1000  # 140687

scan扫描

scan + memory usage

自己编程,利用scan扫描Redis中的所有key,利用strlen、hlen等命令判断key的长度(此处不建议使用MEMORY USAGE)

第三方工具

利用第三方工具,如 redis-rdb-tools 分析RDB快照文件,全面分析内存使用情况

redis-rdb-tools 是一个 python 的解析 rdb 文件的工具,在分析内存的时候,我们主要用它生成内存快照。可以把 rdb 快照文件生成 CSV 或 JSON 文件,也可以导入到 MySQL 生成报表来分析。

pip install rdbtools
rdb -c memory --bytes 10240 dump.rdb > memory.csv

在生成的 CSV 文件中主要有以下几列:

可以在MySQL中新建表然后导入进行分析,然后可以直接通过SQL语句进行查询分析。

代码语言:javascript

复制

CREATE TABLE `memory` (
     `database` int(128) DEFAULT NULL,
     `type` varchar(128) DEFAULT NULL,
     `KEY` varchar(128),
     `size_in_bytes` bigint(20) DEFAULT NULL,
     `encoding` varchar(128) DEFAULT NULL,
     `num_elements` bigint(20) DEFAULT NULL,
     `len_largest_element` varchar(128) DEFAULT NULL,
     PRIMARY KEY (`KEY`)
 );

解决

压缩

如果大key的产生原因主要是由于对象序列化后的体积过大,我们可以考虑使用压缩算法来减小对象的大小。需要在客户端使用一些压缩算法对数据进行压缩和解压缩操作,例如LZF、Snappy等。

分割

将Big Key拆分成多个小key。这个方法比较简单,但是需要修改应用程序的代码。就像是把一个大蛋糕切成小蛋糕一样,有点费力,但是可以解决问题。

或者尝试将Big Key转换成Redis的其他数据结构。例如,将Big Key转换成Hash,List或者Set等数据结构。

删除

BigKey内存占用较多,即便时删除这样的key也需要耗费很长时间,导致Redis主线程阻塞,引发一系列问题。

redis 3.0 及以下版本,分批部分删除

如果是集合类型,则遍历BigKey的元素,先逐个删除子元素,最后删除BigKey

# list
def del_large_list():
  r = redis.StrictRedis(host='redis-host1', port=6379)
  large_list_key = 'xxx'  #要删除的大list的键名
  while r.llen(large_list_key)>0:
      #每次只删除最右100个元素
      r.ltrim(large_list_key, 0, -101) 
    
# hash
def del_large_hash():
  r = redis.StrictRedis(host='redis-host1', port=6379)
    large_hash_key ="xxx" #要删除的大hash键名
    cursor = '0'
    while cursor != 0:
        # 使用 hscan 命令,每次获取 100 个字段
        cursor, data = r.hscan(large_hash_key, cursor=cursor, count=100)
        for item in data.items():
                # 再用 hdel 命令,每次删除1个字段
                r.hdel(large_hash_key, item[0])
                
# set
def del_large_set():
  r = redis.StrictRedis(host='redis-host1', port=6379)
  large_set_key = 'xxx'   # 要删除的大set的键名
  cursor = '0'
  while cursor != 0:
    # 使用 sscan 命令,每次扫描集合中 100 个元素
    cursor, data = r.sscan(large_set_key, cursor=cursor, count=100)
    for item in data:
      # 再用 srem 命令每次删除一个键
      r.srem(large_size_key, item)
# zset
def del_large_sortedset():
  r = redis.StrictRedis(host='large_sortedset_key', port=6379)
  large_sortedset_key='xxx'
  while r.zcard(large_sortedset_key)>0:
    # 使用 zremrangebyrank 命令,每次删除 top 100个元素
    r.zremrangebyrank(large_sortedset_key,0,99) 

Redis 4.0以后

Redis在4.0后提供了异步删除的命令:unlink

数据倾斜

redis集群出现倾斜的影响

倾斜问题对于redis这类纯内存和单线程服务影响较大,存在以下痛点:

分析完影响,那我们再看生产环境中,导致Redis集群严重“倾斜”的常见原因。

导致Redis集群倾斜的常见原因

一般是系统设计时,键空间(keyspace)设计不合理:

接下来,当集群出现内存容量、键数量或QPS请求量严重倾斜时,我们应该排查定位问题呢?

访问倾斜

redis-faina

使用monitor命令在紧急情况时找出热Key

Redis的monitor命令能够忠实的打印Redis中的所有请求,包括时间信息、Client信息、命令以及Key信息。在发生紧急情况时,我们可以通过短暂执行monitor命令并将输出重定向至文件,在关闭monitor命令后通过对文件中请求进行归类分析即可找出这段时间中的热Key。

由于monitor命令对Redis的CPU、内存、网络资源均有一定的占用。因此,对于一个已处于高压状态的Redis,monitor可能会起到雪上加霜的作用。同时,这种异步收集并分析的方案的时效性较差,并且由于分析的精确度取决于monitor的执行时间,因此在多数无法长时间执行该命令的线上场景中本方案的精确度也不够好。

三大Key与热Key带来的问题

在Redis的使用中,大Key及热Key会给Redis带来各种各样的问题,而最常见的问题为性能下降、访问超时、数据不均衡等。

1 大Key带来的常见问题

2 热Key带来的常见问题

四 大Key与热Key的常见产生原因

业务规划不足、Redis不正确的使用、无效数据的堆积、访问突增等都会产生大Key与热Key,如:

  1. 将Redis用在并不适合其能力的场景,造成Key的value过大,如使用String类型的Key存放大体积二进制文件型数据(大Key);
  2. 业务上线前规划设计考虑不足没有对Key中的成员进行合理的拆分,造成个别Key中的成员数量过多(大Key);
  3. 没有对无效数据进行定期清理,造成如HASH类型Key中的成员持续不断的增加(大Key);
  4. 预期外的访问量陡增,如突然出现的爆款商品、访问量暴涨的热点新闻、直播间某大主播搞活动带来的大量刷屏点赞、游戏中某区域发生多个工会间的战斗涉及大量玩家等(热Key);
  5. 使用LIST类型Key的业务消费侧代码故障,造成对应Key的成员只增不减(大Key);

监控

慢查询

# 超过5毫秒为慢命令
config set slowlog-log-slower-than 5000

bigkey

redis-cli --bigkeys
redis-cli --hotkeys

key数量、内存占用、客户端连接数、连接被拒绝数、请求数

redis-cli --stat -c -a 123456 -p 8001

内存碎片率

redis-cli -h {ip} -p {port} info | grep mem_fragmentation_ratio