• Redis高级篇——Redis的优化


    一、Redis的键值设计

    1.1key的结构

    Redis的Key在自定义时,最好遵循以下三个规则:

    • 基本格式:[业务名称]:[数据名]:[id]
    • 长度不超过44字节
    • 不包含特殊字符

    如:登录业务,保存用户信息的key 定义为 login:user:10( [业务名称]:[数据名]:[id] )

    优点:

    • 可读性强

    • 避免key冲突

    • 方便管理(使用可视化工具显示的是 层级结构 清晰明了)

    • 更节省内存:key是string类型,底层编码包含int、embstr和raw三种。embstr在小于44字节使用,采用连续内存空间,内存占用更小

    1.2 BigKey问题

    BigKey通常以key的大小和key中成员的数量来综合判定,如:

    • key本身的数据量过大:一个string类型的key,它的值为5MB
    • key中成员数过多:一个ZSET类型的key,它的成员数量为10000个
    • key中成员的数据量过大:比如一个Hash类型的key,它的成员数量可能不多,但是每个成员的value数据量大

    推荐值:

    • 单个key的value小于10KB
    • 对于集合类型的key,建议元素数量小于1000

    1.BigKey的危害

    • 网络阻塞
      对BigKey执行读请求时,少量的QPS就可能导致带宽使用率被占满,导致Redis实例甚至物理机变慢

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

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

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

    2.查找BigKey

    • redis-cli -a 密码 --bigkeys
      利用redis客户端提供的参数,可以遍历分析所有的key,并返回key的整体统计信息与每个数据类型的Top1 的big key
      在这里插入图片描述
      (每种数据类型的Top1可能是bigKey也可能不是,因为可能这种数据类型的key使用的数量少,而一些使用频率高的数据类型可能Top2也是BigKey,因此统计的并不完整)

    • scan
      自己编程,利用scan扫描Redis中的所有key,利用strlen、hlen等命令判断key的长度
      在这里插入图片描述scan 是对所有key分成多个部分进行扫描,避免在千万数量级key下 扫描所有key 影响主线程性能

    • 第三发工具
      利用第三方工具,如Redis-Rdb-Tools分析RDB快照文件,全面分析内存使用情况

    • 网络监控
      自定义工具,监控Redis的网络数据,超出预警值时主动告警 (一般使用云服务中的工具分析如:阿里云)

    3.删除BigKey

    使用 unlink 异步删除 (Redis 4.0以后)
    在这里插入图片描述

    4.避免BigKey

    例1: 比如在存储一个User对象,有三种存储方式:
    在这里插入图片描述
    由图可知,当存储User对象这类数据时,最好选择hash结构进行存储,空间占用小,且可以灵活访问对象的任意字段(hash结构存储注意数据类型转化)

    例2: 假如hash类型的key,有100万对field和value,这个key有什么问题?如何优化?
    在这里插入图片描述

    存在的问题:

    1. hash的entry数量超过500时,会使用哈希表而不是ZipList,内存占用多
    2. 可以通过hash-max-ziplist-entries配置entry上限。但是如果entry过多就会导致BigKey问题

    在这里插入图片描述
    string类型占用内存太高,且批量获取数据麻烦,没有hash结构的关联性

    在这里插入图片描述

    注: 一些短期使用的key或者不经常使用的key设置过期时间,这样就不会出现BigKey问题

    二、批处理优化

    2.1 Pipeline

    1.批处理的优势
    单个命令的执行流程

    一次命令的响应时间 = 1次往返的网络传输耗时+1次Redis执行命令耗时
    在这里插入图片描述

    网络传输耗时: ms毫秒级别 Redis执行命令耗时:us微秒级别
    如果执行N条命令,那么N次网络传输往返的耗时将被扩大N倍,而执行命令的耗时可以忽略不记,此时执行N条命令耗时很长,会影响Redis的性能

    如果一次网络传输就执行N次命令,那么就可以解决批量数据操作耗时长问题,提高了效率
    在这里插入图片描述

    Redis提供了很多批处理命令,可以实现批量插入数据,例如:

    • mset
    • hmset

    注: 不要在一次批处理中传输太多命令,否则单次命令占用带宽过多,会导致网络阻塞

    由于mset只能对string类型的数据进行批处理,而hmset只能对hash类型的数据进行操作,如果对复杂数据类型处理可以使用 Pipeline

    Pipeline类似于一个管道,将命令放入管道中进行传输。
    在这里插入图片描述

    Pipeline的多个命令之间不具备原子性,可能会出现多线程插队等待耗时问题。

    2.2 集群下的批处理

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

    解决方案:
    在这里插入图片描述
    由于hash_tag容易出现数据倾斜,在集群模式下数据倾斜可能会造成单点问题,因此常用并行slot方式实现集群下批处理。在spring整合的redis包中 stringRedisTemplate 有封装好的用法,底层实现时异步的管道传输模式。
    例如:stringRedisTemplate.opsForValue().multiSet();

    三、服务端优化

    3.1持久化配置

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

    1. 用来做缓存的Redis实例尽量不要开启持久化功能 (开多个Redis实例分别复杂不同的业务)
    2. 建议关闭RDB持久化功能,使用AOF持久化
    3. 利用脚本定期在slave节点做RDB,实现数据备份
    4. 设置合理的rewrite阈值,避免频繁的bgrewrite
    5. 配置no-appendfsync-on-rewrite = yes,禁止在rewrite期间做AOF,避免因AOF引起的阻塞 (可能出现数据丢失)
      Redis持久化详情

    部署相关建议:

    1. Redis实例的物理机要预留足够内存,应对fork和rewrite
    2. 单个Redis实例内存上限不要太大,例如4G或8G。可以加快fork的速度、减少主从同步、数据迁移压力
    3. 不要与CPU密集型应用部署在一起 (fork时比较耗CPU)
    4. 不要与高硬盘负载应用一起部署。例如:数据库、消息队列

    总结就是 redis最好独占一个服务器 hh

    3.2 慢查询

    在Redis执行时耗时超过某个阈值的命令,称为慢查询。
    由于Redis是单线程执行命令,所以执行一个命令会放入队列中,排队等待执行,此时若一个慢查询耗时较久,队列中等待的命令可能因等待超时出错
    在这里插入图片描述
    阈值 的配置:

    • slowlog-log-slower-than :慢查询阈值,单位微秒。默认10000,建议1000
      慢查询会被放入慢查询日志中,日志的长度有上限,可以通过配置指定:
    • slowlog-max-len: 慢查询日志(本质是一个队列)的长度。默认是128,建议1000
      在这里插入图片描述
      查看慢查询日志列表:
    • slowlog len : 查询慢查询日志长度
    • slowlog get [n] 读取n条慢查询日志
    • slowlog reset:清空慢查询列表

    在这里插入图片描述
    可以使用桌面客户端RESP
    在这里插入图片描述

    3.3 内存安全和配置

    当Redis内存不足时,可能导致key频繁被删除、响应时间变长、QPS不稳定等问题。当内存使用率达到90%以上时就需要警惕,并快速定位到内存被占用的原因。
    在这里插入图片描述
    查看Redis目前的内存分配状态

    • info memory
    • memory xxx

    内存缓冲区配置

    • 复制缓冲区:主从复制的repl_backlog_buf,如果太小可能导致频繁的全量复制,影响性能。通过repl-backlog-size来设置,默认1mb
    • AOF缓冲区:AOF刷盘之前的缓存区域,AOF执行rewrite的缓冲区。无法设置容量上限
    • 客户端缓冲区:分为输入缓冲区和输出缓冲区,输入缓冲区最大1G且不能设置。输出缓冲区可以设置
      在这里插入图片描述

    3.4 集群的问题

    1.数据完整性问题
    在Redis的默认配置中,如果发现任意一个插槽不可用,整个集群都会对外停止服务
    在这里插入图片描述

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

    • 插槽信息
    • 集群状态信息

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

    解决途径:

    1. 避免大集群,集群节点数不要太多,最好少于1000,如果业务庞大,则建立多个集群。
    2. 避免在单个物理机中运行太多Redis实例
    3. 配置合适的cluster-node-timeout

    3.数据倾斜问题

    BigKey或批处理时,使用相同的hash_tag来保证所有key落在同一个插槽slot,会导致某些结点数据量远大于其他节点

    4.客户端性能问题
    当使用客户端连接Redis集群时,需要进行节点选择、插槽slot判断、读写判断,会影响客户端性能

    5.命令的集群兼容性问题

    比如批处理 mset 在集群模式如果不能落在相同的插槽slot上就会报错。

    6.lua和事务问题
    多个命令的key必须在同一个slot,如果不在就会报错。因此集群模式下就不能使用lua和事务

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

    参考学习:黑马Redis入门到实战

  • 相关阅读:
    2022年深圳市福田区支持先进制造业发展若干措施
    数据结构题型20-第七章 查找
    【吴恩达机器学习笔记】六、过拟合及正则化
    JAVA面试题|AQS详细剖析
    Unity Shader ASE基础效果思路与代码(一):遮罩、硬边溶解、光边溶解、UV扰动
    【Linux网络】网卡配置与修改主机名,做好基础系统配置
    网络是如何进行通信
    信息安全-大数据安全需求分析与安全保护工程
    信息检索度量指标(MAP@N, P@N)
    从0到1建设智能灰度数据体系:以vivo游戏中心为例
  • 原文地址:https://blog.csdn.net/qq_52595134/article/details/128111806