• 如何高效且优雅地使用Redis


    本文从如下7个维度,带你全面理解Redis的最佳实践和优化:

    • 内存
    • 性能
    • 可靠性
    • 运维
    • 安全
    • 资源规划
    • 监控

    1、如何节省内存

    1.1、控制Key的长度

    • 在开发业务时,要提前预估Redis中写入key的数量,如果key数量达到了百万级别,那过长的key也会占用过多的内存空间
    • 在保证key简单、清晰的前提下,key要尽可能地简短,这对内存的优化非常直接和高效

    1.2、避免存储bigkey

    • 如果大量存储bigkey(value过大或过多),会占用大量Redis内存;此外,客户端读写bigkey也会产生性能问题
    • 避免Redis存储bigkey,建议如下:
      1)String类型:控制在10KB以下
      2)List/Hash/Set/ZSet:元素数量控制在1万以下

    1.3、选择合适的数据类型

    • Redis提供了丰富的数据类型,这些数据类型底层对应着多种数据结构来实现(可进一步节约内存资源):如,String、Set在存储int数据时,会采用整数编码来存储;Hash、ZSet在存储元素较少时,会采用压缩列表(ZipList)存储,在元素较多时,会转为哈希表(HashTable)和跳表(SkipList)存储 ==》 Hash:HashTable;ZSet:HashTable + SkipTable
      在这里插入图片描述
    • 利用如上数据存储的特性,建议如下:
      1)String、Set:尽量存储int类型数据
      2)List/Hash/Set/Zset:存储的元素数量尽可能地控制在转换阈值之下(元素数量尽量控制在1万以下),以便节省内存

    1.4、把Redis当做缓存而非数据库

    • 要把Redis当作缓存而非数据库,即Redis只存储经常访问的热点数据,以此保证较高的内存利用率
    • 写入到Redis中的数据,尽可能设置过期时间
    • 业务应用在Redis中无法查询到数据时,才从后端数据库查询并加载到Redis

    1.5、设置内存上限和淘汰策略

    • Redis中key都设置了过期时间,但如果业务应用写入数据量很大,并设置的过期时间比较久,则短期内Redis内存依旧会快速增长。如果控制Redis内存的上限,就会导致使用过多的内存资源
    • 对于此种场景,你需要提前预估业务数据量,并为Redis实例设置maxmemory来控制实例的内存上限,如此来避免Redis占用内存持增长
    • 此外,还需要结合业务特点设置数据的淘汰策略(默认情况下,推荐使用allkeys-lru)
      1)基于LFU算法的淘汰策略(对是否设置了过期时间TTL进行再分类)
      allkeys-lfu:对全体key,基于LFU算法进行淘汰(默认推荐)
      volatile-lfu: 对设置了TTL的key,基于LFU算法进行淘汰
      2)基于LFU算法的淘汰策略(对是否设置了过期时间TTL进行再分类)
      allkeys-lru:对全体key,基于LRU算法进行淘汰
      volatile-lru:对设置了TTL的key,基于LRU算法进行淘汰
      3)随机淘汰(对是否设置了过期时间TTL进行再分类)
      allkeys-random:对全体key ,随机进行淘汰
      volatile-random:对设置了TTL的key ,随机进行淘汰
      4)其他
      volatile-ttl:对设置了TTL的key,比较key的剩余TTL值,TTL越小越先被淘汰
      noeviction:不淘汰任何key,但是内存满时不允许写入新数据,默认就是这种策略

    1.6、数据压缩后写入Redis

    • 如果想进一步优化Redis内存,还可以在业务应用中先将数据压缩,再写入到Redis(采用snappy、gzip等压缩算法),但是,客户端在读取压缩存储的数据时要解压数据,这会消耗一定的CPU资源,这需要根据实际情况来权衡

    2、如何发挥Redis的高性能

    • 在使用Redis时,往往需要我们关注于如何持续发挥Redis的高性能,从而避免操作延迟的情况发生,结合实战经验给出的建议如下:

    2.1、避免存储bigkey

    • 由于Redis处理请求是单线程的,当写入一个bigkey时,会在内存分配上耗费较多时间,导致操作延迟会增加,同理,删除bigkey会耗时地释放内存
    • 在读取bigkey时,会在网络数据传输上花费更多时间,后续执行的请求会发生排队,导致Redis性能下降
    • 如果确实有存储bigkey的需求,可以将bigkey拆分为多个小key存储

    2.2、开启lazy-free机制

    • 如果无法避免存储bigkey的情况,建议开启Redis的lazy-free机制。当开启lazy-free后,删除bigkey时释放内存的耗时操作将会交由后台线程去执行,如此以避免对主线程的影响

    2.3、批量命令代替单个命令

    • 当需要一次性操作多个key时,推荐使用批量命令来处理,相较于多次单个操作的优势在于,可减少客户端和服务端之间的网络IO次数
      1)String或Hash结构使用MGET/MSET代替GET/SET命令,HMGET/HMSET代替HGET/HSET命令
      2)其他数据类型使用Pipeline,一次性打包发送多个命令到服务端执行
      在这里插入图片描述

    2.4、注意DEL操作的时间复杂度

    • 当删除String类型的Key时,时间复杂度为O(1)
    • 当删除数据类型为List、Hash、Set、ZSet,时间复杂度为O(N),N为集合的元素个数,即集合中的元素越多,DEL操作花费时间越多,原因在于删除大量元素时,需要依次回收每个元素占用的内存,元素越多,花费时间也就越久,该过程在主线程中执行,存在阻塞主线程的风险,推荐分批删除,操作如下:
    • List类型:多次执行LPOP/RPOP,直到所有元素都删除完成
    • Hash/Set/ZSet类型:先对应执行HSCAN/SSCAN/SCAN查询元素,再对应执行HDEL/ SREM / ZREM依次删除每个元素

    2.5、避免key集中过期

    • Redis通过定时+懒惰的方式来清理过期key,该过程在主线程中执行
    • 如果业务中存在大量key过期的情况,Redis在清理过期key的过程中,存在主线程阻塞的风险,可以通过给过期时间增加一个随机值,将key的过期时间分散开来,从而避免大量key集中过期对主线程的影响
      -

    2.6、使用长连接操作Redis,合理配置连接池

    • 业务应用要使用长连接操作Redis,而非短连接
    • 当使用短连接操作Redis时,每次都需要经过TCP三次握手,四次挥手,会徒增操作耗时
    • 客户端应该使用连接池的方式访问Redis,并设置合理的参数,长时间不操作Redis时,应及时释放连接资源

    2.7、只使用db0

    • Redis尽管提供了16个db,但推荐只使用db0,理由如下:
      1)在一个连接上操作多个db数据时,每次都需要执行SELECT,会给Redis带来额外的压力
      2)Redis Cluster只支持db0,如果想要在集群中迁移数据,迁移成本会很高

    2.8、使用读写分离 & 分片集群

    • 如果业务读请求量很大,则可以采用部署多个从库的方式,实现读写分离,通过Redis的从库来分担读请求的压力,进而提升性能
      在这里插入图片描述
    • 如果业务写请求量很大,单个Redis实例已经无法支撑写流量,则可以使用分片集群来减轻写请求的压力
      在这里插入图片描述

    2.9、不开启AOF或AOF配置为每秒刷盘

    • 如果是丢失数据不敏感的业务,不建议开启AOF,避免AOF写磁盘影响Redis的性能;如果确实需要开启AOF,则建议配置为appendfsync everysec把数据持久化的刷盘操作交给后台线程执行,尽量降低Redis写磁盘对性能的影响

    2.10、使用物理机部署Redis

    • Redis通过创建子进程来完成数据的持久化操作,而创建子进程 需要使用系统调用fork函数【fork函数:用于从一个已经存在的进程内创建一个新进程,这会极大地浪费时间和系统资源,Linux内核需要采取写时拷贝技术(Copy On Write)来提高效率】
    • 虚拟机环境执行fork操作的耗时要比物理机慢得多,所以,Redis应尽可能地部署在物理机上

    3、如何保证Redis的可靠性

    下面从资源隔离、多副本、故障恢复三个维度,带你分析保障Redis可靠性的最佳实践

    3.1、按照业务线部署实例

    • 提高可靠性的首要步骤就是资源隔离,即按照不同的业务线部署Redis实例,当其中的某个实例发生故障时,不会影响到其他实例,其实施成本,但收效巨大

    3.2、部署主从集群

    • 如果只是使用单机Redis,就会存在机器宕机服务不可用的风险;而如果部署了【多副本】实例(主从集群),当主库宕机后,依旧有从库可以使用,避免了数据丢失的风险,也降低了服务不可用的时间
    • 在部署主从集群时,主从库需要分布在不同机器上,避免交叉部署,原因在于,Redis的主库往往会承担所有的读写流量,开发者一定要优先保证主库的稳定性,即便从库机器异常,也要保证不会对主库产生影响
    • 当我们对Redis进行日常维护时,如数据定时备份等操作,可以只在从库上进行,只会消耗从库机器的资源,可避免对主库的影响

    3.3、合理配置主从复制参数

    在部署主从集群时,如果参数配置不合理,有可能发生的主从复制问题以及建议如下:

    • 1)主从复制中断
    • 2)从库发起全量复制,主库性能受到影响
      两点建议如下:
    • 1)设置合理的repl-backlog参数,过小的repl-backlog在写流量比较大的场景下,主从复制中断会引发全量复制数据的风险
    • 2)设置合理的slave client-output-buffer-limit,当从库复制发生问题时,过小的buffer会让从库缓冲区溢出, 从而导致复制中断

    3.4、部署哨兵集群,实现故障自动切换

    • 只部署了主从节点,在故障发生时是无法自动切换的,所以,还需要部署哨兵集群,实现故障的自动切换
    • 多个哨兵节点需要分布在不同机器上,实例为奇数个,防止哨兵选举失败,影响切换时间

    4、Redis日常运维总结

    在平时运维Redis时,需要注意如下几点:

    4.1、禁用KEYS/ FLUSHALL/ FLUSHDB命令

    • 执行KEYS/ FLUSHALL/ FLUSHDB命令,会长时间阻塞Redis主线程,应禁用或
    • SCAN代替KEYS
    • 4.0+版本使用FLUSHALL/ FLUSHDB ASYNC,清空数据的操作交由后台线程执行

    4.2、扫描线上实例时,设置休眠时间

    • 使用SCAN扫描线上实例,和对实例的bigkey做统计分析,建议在扫描时要设置休眠时间,防止在扫描过程中,实例OPS过高对Redis性能产生抖动(OPS:operation per second ,每秒操作数,即每秒对Redis的持久化操作)

    4.3、慎用MONITOR命令

    • 在排查Redis问题时,有时会使用MONITOR查看Redis正在执行的命令,但如果Redis OPS较高,在执行MONITOR会导致Redis输出缓冲区的内存持续增长,会严重消耗Redis的内存资源,导致实例内存超过maxmemory,引发数据淘汰,所以,慎用MONITOR命令
      在这里插入图片描述

    4.4、从库必须设置为slave-read-only

    • 必须要避免从库写入数据,从而导致数据不一致的问题,即从库必须设置为slave-read-only状态(只读状态)
    • 如果 从库不是read-only状态,且使用4.0以下版本的Redis,就会出现问题:从库写入了有过期时间的数据,不会做定时清理和释放内存 ==》这会造成从库的内存泄露

    4.5、合理配置timeout和tcp-keepalive参数

    • 如果由于网络原因,导致大量客户端连接与Redis意外中断,而Redis配置的maxclients比较小,则有可能导致客户端无法与服务端建立新的连接(服务端会认为超过maxclients),其原因在于客户端与服务端每建立一个连接,Redis都会给客户端分配一个client fd,当客户端和服务端网络发生问题时,服务端并不会立即释放该client fd
    • Redis内部有定时任务,会定时检测所有client的空闲时间是否超过配置的timeout值,如果Redis没有开启tcp-keepalive,服务端会等到配置的timeout后,才会清理释放该client fd,而在没有清理前,如果还有大量新连接进来,就可能导致Redis服务端内部持有的client fd超过了maxclients,这时新连接就会被拒绝
    • 优化建议如下:
      1)不要配置过高的timeout:让服务端尽快将无效的client fd清理掉
      2)Redis开启tcp-keepalive:服务端会定时给客户端发送TCP心跳包,检测连接的连通性,当网络异常时,可以尽快清理掉僵尸client fd

    4.6、调整maxmemory时,注意主从库的调整顺序

    • Redis5.0以下版本存在如下问题:从库内存如果超过了maxmemory,也会触发数据淘汰
    • 在某些场景下,从库是可能优先主库达到maxmemory的(如在从库执行MONITOR命令,输出缓冲区占用大量内存),则此时从库开始淘汰数据,主从库就会产生不一致
    • 在调整maxmemory时,一定要注意主从库的修改顺序:
      1)调大maxmemory:先修改从库,再修改主库
      2)调小maxmemory:先修改主库,再修改从库
      直到Redis5.0,Redis新增了参数配置replica-ignore-maxmemory,默认从库超过maxmemory不会淘汰

    5、Redis安全如何保证

    • 针对Redis可能存在的安全问题,给出的建议如下:
      1)部署Redis时不使用默认端口6379
      2)以普通用户启动Redis进程,禁止root用户启动
      3)限制Redis配置文件的目录的访问权限
      4)推荐开启密码认证
      5)禁用高危命令(KEYS/FLUSHALL/ CONFIG / EVAL)
      如上,基本上即可保证Redis的安全风险在可控范围内

    6、Redis问题预防

    • 预防Redis问题,需要做好合理的资源规划完善的监控预警两个方面

    6.1、资源规划

    在部署Redis时,如果可以提前做好资源规划,可以避免很多因为资源不足产生的问题,建议如下:

    • 保证机器有足够的CPU、内存、带宽、磁盘资源
    • 提前做好容量规划,主库机器预留一半的内存资源,防止主从机器产生网络故障,引发大面积全量同步,导致主库机器内存不足
    • 单个实例内存建议控制在10G以下,大实例在主从全量同步、RDB备份时有阻塞风险

    6.2、监控预警

    • 监控预警是提高稳定性的重要环节,完善的监控预警,可以提前暴露问题,便于我们快速反应,最小化问题,建议如下:
    • 做好机器CPU、内存、带宽、磁盘监控,资源不足时及时报警,任意资源不足都会影响Redis性能
    • 设置合理的slowlog阈值,并对其进行监控,slowlog过多及时报警
    • 监控组件采集Redis INFO信息时,采用长连接,避免频繁的短连接
    • 做好实例运行时监控,重点关注expired_keys、evicted_keys、latest_fork_usec指标,这些指标短时间内突增可能会有阻塞风险

    7、总结

  • 相关阅读:
    建筑设计资质怎么办理,建筑设计乙级资质申请条件有哪些
    大学生HTML作业篮球网页 HTML作业篮球网页期末作业 HTML+CSS篮球网页 HTML学生作业体育篮球网页
    编程每日一练(多语言实现)基础篇:满足abcd=(ab+cd)^2的数 (增加Go语言实现)
    KubeSphere 社区双周报 | 2022-07-22
    【JVM】——JVM中内存划分
    面试题精讲丨MySQL的隔离级别真的越高越好吗?!
    CSS3提高: CSS3 3D转换
    代码随想录算法训练营第三十一天 | LeetCode 455. 分发饼干、376. 摆动序列、53. 最大子数组和
    如何科学的设定KR?
    力扣(LeetCode)21. 合并两个有序链表(C++)
  • 原文地址:https://blog.csdn.net/FlyingFish868/article/details/132895921