如何高效且优雅地使用Redis

本文详细阐述了Redis的最佳实践,包括内存管理(如控制Key长度、避免bigkey、数据类型选择)、性能优化(如批量操作、延迟清理机制)、可靠性保障(主从集群、故障恢复)、安全措施和资源规划、监控预警,旨在提升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、总结

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值