目录
优雅的 key
删除 Bigkey
恰当的数据类型
批处理优化
Pipeline
集群下的批处理
服务端优化
持久化配置
慢查询
命令以及安全配置
内存安全和配置
内存缓冲区配置
集群最佳实践
集群带宽问题
集群还是主从
优雅的 key
删除 Bigkey
Bigkey 内存占用较多,即便删除这样的 key 也是比较耗时的,这同样也会导致主线程阻塞,引发一系列问题
在 redis 3.0 版本以前,如果是集合类型,都是先遍历 big key 的元素,再逐个删除,最后再删除 key
来到 redis 4.0 及以后官方就提供了异步删除的命令 :unlink
恰当的数据类型
比如存储一个对象类型的数据,我们推荐三种存储方式
-  json 字符串:实现简单粗暴,缺点:数据耦合吗,不够灵活 
-  字段打散:可以灵活访问对象中的任意字段,缺点:占用空间大,没办法做到统一控制 
-  hash:低层使用 ziplist ,占用空间小,可以灵活的访问对象中的任意字段,缺点:代码相对复杂 
这么对比下来,hash 完美胜出,但是这还不是最优解,下面是 hash 可能还存在的问题
hash 的 entry 数量超过 500 时,会使用哈希表而不是 Ziplist ,这样也会导致内存占用过大的问题,但是我们可以通过 hash-max-ziplist-entries 配置 entry 上限,但是 entry 过多的话还是会导致 bigkey 的问题再次出现
那么如何优化呢?
拆分 string 类型,假如有 hash 类型的 key ,其中有 100 万对 field 和 value ,field 是自增 id,这个 key 存在什么问题?如何优化呢?
有一个方案就是将每一个 hash 再次才分成多个小的 hash ,这样外层的一个 key 就可以掌管多个内部的 key ,也就是相当于掌管里面所有的 hash ,我们只需要对内部的 hash 的做优化就可以了,如果内部 hash 过于庞大的话,再考虑对外部的 hash 做优化就可以基本解决这个问题
-  key 的最佳实践 -  固定格式的 key 
-  足够精简,不超过 44 个字节 
-  不包含特殊字符 
 
-  
-  value 的最佳实践 -  合理拆分数据,拒绝 big key 
-  选择适合的数据类型 
-  hash 结构的 entry 最好不要超过 1000 
-  设置合适的超时时间 
 
-  
批处理优化
Pipeline
单个命令的执行流程:一次命令的响应时间 = 1 次往返的网络传输耗时 + 1 次 Redis 执行命令耗时
那么 N 次命令的执行流程也是一个逻辑,但是如果是这样的话,网络传输的往返次数就会非常耗时,如果我们将要请求的 N 条数据一次打包好,然后只往返一次,那么就可以大大减少网络传输的耗时,也可以减少网络拥堵,在 Redis 中我们可以使用比如 mset 的命令可以打包一串数据只发送一次请求,但是在这里我们也还是要注意,在打包一次传输的时候,还是不能一次性打包太多数据,不然当带宽占满后还是会造成网络堵塞的情况,反而得不偿失,拿这里的 mset 举例子,利用 mset 批量插入数据时最好不要一次插入 10 万条数据
原生的批处理命令具有局限性,比如:mset 只能处理字符串,hmset 只能处理 hash 而且 key 是不变的,变的仅仅只有 value ,sadd 还是 key 不能变而且只能处理 set 集合,所以这些命令都具备局限性
这个时候我们就可以使用本章要说明的 pipeline ,它和 mset 很像,但是它允许你传入任何命令
注意事项:批处理时不建议一次携带太多数据,其次 pipeline 的多个命令之间不具备原子性
集群下的批处理
如 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 越多,耗时越久 | 实现复杂 | 容易出现数据倾斜 | 
在这张表上,我们可以看到最后一个 hash_tag 是最好的,但是也存在一定的风险,这相当于把 redis 中的所有的数据都同化了,这就是数据倾斜,所以在实际的开发中我们还是更加推荐并行 slot
服务端优化
持久化配置
Redis 的持久化是用来保护数据的安全性的
-  但是对于一些作为缓存的数据就不需要去做持久化操作了 
-  建议关闭 RDB 使用 AOF 
-  利用脚本定期为 slave 上做 RDB 数据备份 
-  设置合理的 rewrite 阈值,避免频繁的 bgrewrite 
-  配置 no-appendfsync-on-rewrite = yes , 禁止在 rewrite 期间做 aof ,避免因 aof 引起的阻塞 
部署的相关建议
-  redis 实例的物理机要预留足够的内存空间,应对 fork 和 rewrite 
-  单个 redis 实例内存上限不要太大,比如 4G 或者 8G 。可以加快 fork 的速度,减少主从同步,数据迁移的压力 
-  不要与 cpu 密集型的服务部署在一起 
-  不要与高硬盘负载的服务部署在一起 
慢查询
在 redis 中执行耗时达到了某个阈值的命令,称之为慢查询
-  slowlog-log-slower-than 慢查询阈值,单位是微秒。默认是 10000 ,建议是 1000 
-  慢查询会被放入慢查询日志当中,日志的长度有上限,可以通过配置指定 
-  slowlog-max-len 慢查询日志本质是一个列队默认长度是 128 ,建议 1000 
修改这两个配置可以使用 config set 命令,比如 config set slowlog-log-slower-than ,get 是同理,即查看配置
-  slowlog len 查询慢查询日志的长度 
-  slowlog get[n] 读取 n 条慢查询日志 
-  slowlog reset 清空慢查询列表 
命令以及安全配置
Redis 会绑定在 0.0.0.0:6379 ,这样将会使得 redis 服务暴露在公网上,而 redis 如果没有做身份认证,就会出现严重的安全漏洞
漏洞重现方式 Redis未授权访问配合SSH key文件利用分析-腾讯云开发者社区-腾讯云
漏洞出现的几个核心的原因
-  redis 未设置密码 
-  利用 redis 的 config set 命令动态修改 redis 配置 
-  使用了 root 账号权限启动了 redis 
为了避免这种情况的发生,这里给出一些建议:
-  Redis 一定要设置密码 
-  禁止线上使用下面命令:key,flushall,flushdb, config set 等命令,可以利用 rename-command 禁用(这条命令悠着点用,典型的弹道偏左) 
-  bind 限制网卡,禁止外网网卡访问 
-  开启防火墙 
-  不要使用 root 账号登录 redis 
-  不要使用默认端口 
内存安全和配置
当 redis 内存不足时,可能会导致 key 频繁被删除,响应时间变长,QPS 不稳定等问题。当内存使用率达到 90% 以上时我们就需要提高警惕了,并且快速定位到内存占用大的原因
| 内存占用 | 说明 | 
|---|---|
| 数据内存 | 是 redis 最主要的部分,存储 redis 的键值信息,主要问题是 bigkey 问题,内存碎片问题 | 
| 进程内存 | redis 主进程运行本身很定需要占用内存,如代码,常量池等,这部分内存大约几兆,在大多数生产环境中与 redis 数据占用内存相比可以忽略 | 
| 缓冲区内存 | 一般包括客户端缓冲区,AOF 缓冲区,复制缓冲区等,客户端缓冲区又包括输入缓冲区和输出缓冲区。这部分内存占用波动较大,不当使用 bigkey ,可能会导致内存溢出 | 
什么是内存碎片:内存碎片是在内存分配的过程中产生的,redis 低层采用的内存分配策略是 LRU ,这种策略它会提前鉴定很多个不同的内存大小,比如说 8 字节,16 字节,32 字节,48 字节 等,不过它是有一套自己的内存分配规则,也不一定是我这里说的 2 的次方,当我们要向 redis 申请一段内存的时候,比如我们这个数据的大小是 10 个字节,如果这个时候这个 10 个字节正好是在 8 - 16 之间的一个阈值,则 redis 就会给这段数据分配 16 个字节的内存,这个时候多出来的内存我们就称之为内存碎片,想要解决内存碎片的问题,我们可以定期的去重启 redis 服务
redis 提供了一些命令,可以查看目前 redis 的内存的一个分配情况分别是 info memory 和 memory xxx
-  memory doctor 内存诊断策略 
-  memory purge 清除一些数据 
-  memory status 查看内存状态 
-  memory usege 查看某个 key 的内存占用情况 
内存缓冲区配置
-  复制缓冲区:主从复制的 repl_backlog_buf ,如果太小可能导致频繁的全量复制,影响性能,通过 repl-backlog-size 来设置,默认 1mb 
-  AOF 缓冲区:AOF 刷盘之前的缓存区域,AOF 执行 rewrite 的缓冲区。无法设置容量上限 
-  客户端缓冲区:分为输入缓冲区和输出缓冲区,输入缓冲区最大 1G 且不能设置(这种概率还是比较小的,只要我们控制好慢查询就可以很好的避免这种情况的发生),输出缓冲区可以设置 

默认配置

当前 redis 连接了多少的客户端 info clients , clients list 可以查看当前所有的连接到 redis 的所有的客户端的详细信息
集群最佳实践
在 redis 的默认配置中,如果发现任意一条插槽不可用,则整个集群都会停止服务,为了保证高可用特性,这里建议 cluster-require-full-coverage 设置为 no ,这样做就是会牺牲一定的数据完整性,但是保障了业务的基本运作
集群带宽问题
集群节点之间会不断的 ping 来判断集群之间其他节点的状态,每次 ping 携带的信息至少包括
-  插槽信息 
-  集群状态信息 
集群中的节点越多,集群的信息就会越庞大,10 个节点的相关信息就可以达到大约 1kb,此时的节点之间的互通带宽的占用率就会提高,为了解决这一问题,我们可以:
-  避免大集群,集群节点数不要太多,最好少于 1000 ,如果业务庞大则建立多个集群 
-  避免在单个物理机上运行多个 redis 实例 
-  配置合适的 cluster-node-timeout 值 
集群还是主从
集群虽然具备高可用,能够实现故障自动修复,但是如果使用不当,一样会出现一些问题
-  集群完整性问题 
-  集群带宽问题 
-  数据倾斜问题 
-  客户端性能问题 
-  命令的集群兼容性问题 
-  lua 和事务问题 
单体 redis 已经能达到万进别的 QPS 了,并且也具备很高的高可用特性,如果主从能够满足业务需求,尽量不要搭建 redis 集群
至此,我总结的 Redis 优化完成,有什么不足还请大佬指点












![[附源码]宠物领养管理系统+SpringBoot](https://img-blog.csdnimg.cn/img_convert/599aeb98dc0038b79c49a08818afe545.webp?x-oss-process=image/format,png)






