Skip to main content

润典Redis使用规范手册v1.0

本章思维导图

redis.svg ## 使用规范 使用Redis作为缓存时,有一些最佳实践和使用规范可以帮助您更高效、安全地利用Redis的功能。下面是一些关键的使用规范:

  1. 命名规范

    1. 避免使用保留字:不要使用Redis的保留关键字作为键名。
    2. 合理的键长度:避免使用过长的键名,这会增加内存消耗。
    3. 避免特殊字符:不要在键名中使用特殊字符,如空格、换行符、单双引号等。
    4. 有意义的键名:使用有意义且易于理解的键名,便于维护。
  2. 数据结构选择

    1. 根据需求选择合适的数据结构:例如使用字符串存储简单的键值对,使用哈希表存储多字段的数据,使用列表进行队列操作等。
    2. 避免不必要的序列化:尽量直接存储原始数据,减少序列化和反序列化的开销。
  3. 内存管理

    1. 设置最大内存限制:通过maxmemory配置项限制Redis使用的最大内存,防止过度占用系统资源。
    2. 选择合适的内存淘汰策略:根据应用的需求选择合适的内存淘汰策略,如volatile-lru、allkeys-lru等。
  4. 缓存设计

    1. 缓存数据的选择:不是所有的数据都需要缓存,考虑缓存热点数据和经常访问的数据。
    2. 缓存数据的有效期:合理设置键的有效期,避免无限期缓存导致内存浪费。
  5. 高可用性

    1. 主从复制:配置主从复制,实现读写分离,提高读取性能和冗余。
    2. 哨兵机制:使用Redis Sentinel来实现故障检测和自动故障转移。
    3. 集群部署:对于大型应用,可以使用Redis Cluster来实现水平扩展。
  6. 性能优化

    1. 减少网络往返次数:使用批量操作如MGET、MSET等减少网络通信次数。
    2. 异步处理:对于耗时的操作,如BGREWRITEAOF、BGSUBSCRIBE等,使用异步方式进行处理。
  7. 安全性

    1. 认证机制:启用密码保护,确保只有授权用户可以访问Redis实例。
    2. 网络隔离:确保Redis实例不直接暴露在公网上,最好通过防火墙或其他网络隔离手段保护。
  8. 监控与调试

    1. 性能监控:定期检查Redis的性能指标,如内存使用情况、连接数、命令执行时间等。
    2. 异常处理:实现异常处理逻辑,确保在Redis出现故障时应用仍能正常运行。

遵循上述规范可以更好地利用Redis作为缓存,提高应用的整体性能和稳定性。

常见问题及解决方案​

(一) 内存问题​

故障现象

  1. Redis实例中内存的使用超过了MaxMemory的限制以后,出现请求变慢的情况。
  2. PaaS服务实例中的内存使用超过服务提供的内存使用上限后服务不可用或访问速度变慢。
  3. info中evicted_keys大量增加:由于达到MaxMemory最大限制后,被强制回收的key的总数增加导致的速度变慢。

故障原因

  1. 淘汰数据在主线程中执行
  2. OPS(每秒操作次数)过高,也会导致Redis的性能下降
  3. Redis的LRU淘汰策略触发时也会增加耗时
  4. Bigkey的淘汰会大大降低Redis的性能

解决方案

  1. 避免存储BigKey
  2. 拆分Redis实例,分摊淘汰数据的压力(此点主要体现在Redis数据较大的情况下)
  3. 运维监控中重点监控redisinfo里面的evicted_keys的数据增量,如果出现突发的大量增加,就需要警惕了。
  4. 目前公司使用的Redis版本是5.0~7.0,建议检查Redis的PaaS或自建服务中有没有开启lazyfree机制,因为Redis的数据访问线程是单线程的,当执行Del操作时,其他的操作全部会被挂起排队等待,而Del针对越大的BigKey删除的时候需要等待的时间越久,而开启LazyFree以后,删除指令会以多线程的方式,交由后台线程进行删除,不会对Redis的主执行线程造成阻塞。
  5. Redis 6.0之前默认淘汰策略是LRU,结合业务判断,是否需要用到最近最少使用的这种淘汰策略,如果当Redis内存不足时,可以任意淘汰Key的话,建议修改为随机策略,随机策略的效率要比LRU高很多,不过这需要依赖你使用的业务场景允许的前提下。(注意:Redis 6.0以后的默认淘汰策略变成了noeviction。在这种策略下,当内存达到限制时,Redis不会删除任何键,而是返回错误。所以本条不适合6.0或之后的所有Redis版本)

(二) CPU问题​

故障现象

  1. Redis进程绑定了一个CPU的核心,1核有难多核围观
  2. CPU使用率异常升高

故障原因

  1. 子进程继承了父进程CPU的使用偏好或子进程数据持久化期间,与父进程发生CPU资源争抢
  2. 当Redis的内存使用达到持久化阈值或Redis内存不足时,会触发Redis数据刷盘操作,如果数据量非常大,则会非常占用CPU,并且此时Redis的运行速度也会降低
  3. 使用阻塞式API模糊查询大量KEY,且KEY中包含了BigKey

解决方案

  1. 将Redis的主线程与其他工作线程分别绑定到不同的CPU核心上,以确保后台线程不会出现与主线程争抢CPU资源
  2. 给上Redis足够的内存空间,并做好内存监控,尽量降低Redis持久化的频率
  3. 避免存储BigKey

(三) 缓存雪崩​

故障现象

  1. 整点发生查询延迟
  2. 固定周期/频率发生查询延迟
  3. redisinfo中expired_keys的数量突增

故障原因

  1. 清理过期的key在主线程中执行,比如通过定时任务调用del批量删除
  2. rediskey的失效时间过于集中
  3. 过期的key中包含bigkey,会导致查询延迟的现象更加明显

解决方案

  1. 针对业务处理的过期key设置一个区间过期时间,而非固定时间
  2. redis开启lazyfree功能,使用unlink异步删除
  3. 排查业务代码是否使用了expireat/pexpireat命令,以常量的形式单独设置了key的过期时间

(四) 缓存击穿​

故障现象

  1. 突发的在某一时间点数据库访问量激增,之后平稳回落或最差的情况DB宕机

故障原因

  1. 冲入数据库查询的请求量过大,导致DB负载激增,Redis没能起到该有的缓存作用

解决方案

  1. 针对查询接口,Redis增加分布式互斥锁,当出现热点Key失效的时候,通过分布式互斥锁进行数据加载,未抢到锁的线程需要等待,等锁释放以后再次尝试从缓存中获取
  2. 提前对可能的热点Key进行预热加载,并用定时任务的形式刷新其过期时间,防止KEY失效。

(五) 缓存穿透​

故障现象

  1. 突发的数据库访问量激增,并持续高负载。
  2. 服务端收到大量的查询请求,并且都是无意义的查询值

故障原因

  1. 数据库未作缓存穿透的防护,导致大量KEY失效以后,所有的请求都打入数据库
  2. 遭受到缓存穿透攻击

解决方案

  1. 使用Redis+Guava/Redission实现布隆过滤器,降低穿透的概率

(六) BigKey​

故障现象

  1. showlog出现set/del简单命令
  2. redis中存储了较大的key,并且使用redis-cli--bigkeys,检查出有bigkey存在

故障原因

  1. bigkey分配和释放内存都需要更长的耗时

解决方案

  1. 分拆bigkey,常规key建议控制在10K以内,过大的报文也请控制在60K以内

(七) 使用高复杂度的命令​

故障现象

  1. CPU使用率很高
  2. showlog有耗时的命令
  3. 业务使用复杂度过高的命令
  4. 使用O(n)时间复杂度命令,但n很大。O(n)命令包括:String:MSET、MSETNX、MGETList:LPUSH、RPUSH、LRANGE、LINDEX、LSET、LINSERTHash:HDEL、HGETALL、HKEYS/HVALSSet:SADD、SREM、SRANDMEMBER、SPOP、SMEMBERS、SUNION/SUNIONSTORE、SINTER/SINTERSTORE、SDIFF/SDIFFSTORESortedSet:ZADD、ZREM、ZRANGE/ZREVRANGE、ZRANGEBYSCORE/ZREVRANGEBYSCORE、ZREMRANGEBYRANK/ZREMRANGEBYSCORE

故障原因

  1. 复杂度高的命令执行时间更长,消耗CPU资源更多
  2. 执行耗时久的命令导致后面的请求发生阻塞

解决方案

  1. 减少复杂度高的命令
  2. 聚合数据放在代码中实现,Redis仅用作存储
  3. 一次查询尽量少的数据,分次获取,降低时间复杂度

(八) 其它问题​

频繁短连接

  1. TCP频繁建立和断开也会增加延迟

监控程序

  1. 避免监控系统频繁操作Redis
  2. 关注监控程序版本BUG导致的性能问题

(九) 开启AOF​

故障现象

  1. AOFRewrite期间访问延迟增加
  2. 磁盘IO的负载增加

故障原因

  1. appendfsyncalways家中主线程的写入负担
  2. appendfsynceverysec期间磁盘负载高,write和fsync系统调用发生阻塞

解决方案

  1. 保证充足的磁盘空间
  2. no-appendfsync-on-rewrite配置为yes,这样不会造成磁盘阻塞,但是在Redis服务宕机时有可能会丢失30秒左右的数据
  3. 磁盘性能不足,升级到速度更快速的SSD磁盘

(十) 开启内存大页​

故障现象

  1. 子进程持久化数据期间,主进程写请求变慢

故障原因

  1. 大内存页会以2MB为单位想操作系统申请内存,耗时比较久
  2. Bigkey申请内存的耗时较高

解决方案

  1. 关闭大内存页
  2. 避免Bigkey的产生

(十一) fork耗时​

故障现象

  1. 后台线程触发RDB持久化导致查询变慢
  2. 后台AOF日志触发rewrite导致查询变慢
  3. 主从节点的全量同步导致查询变慢
  4. redisinfo中latest_fork_usec耗时增加

故障原因

  1. 创建子进程会触发fork系统的调用
  2. fork拷贝内存页表导致耗时增加,拷贝结束之前redis实例会完全阻塞
  3. 自建Redis部署在虚拟机上导致的性能低下

解决方案

  1. 根据业务的特性,调整redis配置文件中的RDB备份策略
  2. 根据实际情况可以选择关闭AOF和AOFRewrite
  3. 调大repl-backlog-size,降低全量同步的概率
  4. Redis实例所在的服务器分配给Redis的内存占比尽量保持在五五开,即16G内存的服务器,Redis最大可用内存设置为8G

(十二) 使用swap​

故障现象

  1. 所有请求全部变慢
  2. 查询延迟达到150毫秒+上不封顶

故障原因

  1. Redis可用内存已不足,触发了内存数据持久化到磁盘的操作,访问磁盘会带来高延迟

解决方案

  1. 增加Redis内存
  2. 做Swap监控
  3. 释放实例的Swap

(十三) 碎片整理​

故障现象

  1. 一切服务都正常,请求量也不高,但查询速度却很慢

故障原因

  1. Redis的内存碎片整理在主线程中自动执行了

解决方案

  1. 关闭碎片自动整理,改在业务低峰期执行
  2. 合理配置碎片整理的参数

(十四) 网络带宽过载​

故障现象

  1. 稳定运行的实例,突然开始变慢,并且一直持续
  2. SSH连接到对应的Server节点速度也很慢

故障原因

  1. 网络层存在数据传输延迟、丢包
  2. 由于Bigkey被大量线程查询,导致的网络阻塞

解决方案

  1. 排查流量大的实例,通过操作系统的监控,找出流量过载的原因
  2. 拆分bigkey,降低单次查询返回的数据包大小

(十五) Redis性能降低的判断​

  1. 做基准性能测试
  2. 记录正常情况的各响应时间参数
  3. 在需要判断性能的时候,查看当时的平均效应时间是否有增高
  4. 在需要判断性能的时候,查看当时的最大响应时间是否有增高
  5. 响应延迟大于基准性能2倍以上的,基本就可以判定Redis性能低下了