当前位置: 首页 > article >正文

Redis 优化

目录

优雅的 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并行 slothash_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 优化完成,有什么不足还请大佬指点


http://www.kler.cn/a/320417.html

相关文章:

  • uniapp 微信小程序地图标记点、聚合点/根据缩放重合点,根据缩放登记显示气泡marik标点
  • 基于Java和Vue实现的上门做饭系统上门做饭软件厨师上门app
  • 【Fargo】23:采集时间转rtp时间
  • C语言剖析:srand()/rand()/time()
  • 基于opencv制作GUI界面
  • WebRTC视频 04 - 视频采集类 VideoCaptureDS 中篇
  • gitlab-runner集成CI/CD完整项目部署
  • 智源研究院与百度达成战略合作 共建AI产研协同生态
  • php strtotime常见用法
  • NLP:命名实体识别及案例(Bert微调)
  • Github 2024-09-22 php开源项目日报 Top10
  • 零基础入门ComfyUI(一)初识ComfyUI
  • 软件架构设计-系统架构师(七十二)
  • SQL_over_partition_by_order_by
  • go 安装三方库
  • ShiftAddAug:基于乘法算子训练的最新无乘法网络方案 | CVPR‘24
  • Wpf使用NLog将日志输出到LogViewer
  • 8.5 矢量图层点要素分级(Graduated)渲染使用
  • 用 CSS 动画记录宝宝0-280天的变化
  • 传输大咖46 | 还在为如何快速传输大文件困扰?镭速帮你解决
  • 数据集-目标检测系列-老虎检测数据集 tiger>> DataBall
  • 理解JVM中的死锁:原因及解决方案
  • 2015年国赛高教杯数学建模B题互联网+时代的出租车资源配置解题全过程文档及程序
  • 炉石传说辅助攻略—VMOS云手机助攻:国服回归任务要点,哪个辅助更好?
  • TypeScript 设计模式之【享元模式】
  • django项目添加测试数据的三种方式