项目开发中如何定义Redis 使用规范

2022-08-01,,,,

从个人实践经验,从SDK选择,键值设计,命令使用方面总结以下几点

一. SDK选择

目前Java平台Redis的使用一般为以下三种客户端

  1.    Jedis

    1. 使用Redis原生数据结构,比较全面的提供了Redis的操作特性
    2. 数据结构过于简单,需根据实际情况自行设计所需数据结构
    3. 轻量,简洁,便于集成和改造
    4. 非线程安全,需要使用连接池
    5. 支持pipelining、事务、LUA Scripting、Redis Sentinel、Redis Cluster
    6. 使用RedisCluster时不支持pipelining
    7. 不支持读写分离,不支持异步,需要自己实现
    8. 缺乏完善的文档
    9. Springboot2之前作为spring-data-redis组件的底层连接组件,之后被Lettuce取代
  2. Redisson
    1. 基于Netty实现,采用非阻塞IO,系统资源利用率高
    2. 对Redis原生数据结构进行了封装,实现了java中的Set,List,Map,Multimap,Queue,BlockingQueue等数据结构
    3. 实现了各类分布式锁,实现了基于Redis的简易MQ队列
    4. 提供了底层Redis客户端用以实现目前未封装的原生命令
    5. 支持Redis异步操作
    6. RedissonClient单例模式下线程安全,支持连接池
    7. 支持pipelining、LUA Scripting、Redis Sentinel、Redis Cluster
    8. 支持在Redis Cluster架构下使用pipelining
    9. 支持读写分离,支持读负载均衡,在主从复制和Redis Cluster架构下都可以使用
    10. 支持API级别的事务操作
    11. 支持与Springboot的集成,提供了spring-session-redisson等组件
    12. 文档较丰富,有官方中文文档
  3. Lettuce
    1. 基于Netty实现,采用非阻塞IO,系统资源利用率高
    2. 高级Redis客户端,用于线程安全同步,异步和响应使用,支持集群,Sentinel,pipelining和编码器。
    3. springboot2集成spring-data-redis的官方首选客户端
    4. 文档资料较少

二 . 键值设计

  1. Key名设计
    1. 可读性和可管理性【强制】
      1. 以系统名:业务名(或数据库名)为前缀(防止key冲突),用冒号分隔,比如业务名:表名:id
      2. 例: LSXD:CUSTOMER:000001
    2. 简洁性
      1. 保证语义的前提下,控制key的长度,当key较多时,内存占用也不容忽视
      2. 例: user:{uid}:friends:messages:{mid}简化为u:{uid}:fr:m:{mid}。
    3. 不要包含除了 下划线_ 中划线- 之外的特殊字符【强制】
      1. 例: LSXD:CUS_INFO:000001
      2. 反例:包含空格、换行、单双引号以及其他转义字符
    4. 合适的key,便于查看,统计,排错。【强制】
      1. ":"-作为key分隔符,方便客户端工具作为目录分级
  2. Value设计
    1. 拒绝bigkey(即一个key下的value过大,防止网卡流量、慢查询)【强制】
      1. 对于访问量较大的数据,Value长度控制在2KB以内,其他数据大小尽量控制在10KB以内,hash、list、set、zset元素个数不要超过5000,对于过大的数据集合,按实际情况考虑分割等方式存储
      2. 反例:一个包含200万个元素的list。
      3. 非字符串的bigkey,不要使用del删除,使用hscan、sscan、zscan方式渐进式删除,同时要注意防止bigkey过期时间自动删除问题(例如一个200万的zset设置1小时过期,会触发del操作,造成阻塞,而且该操作不会不出现在慢查询中(latency可查)),查找方法和删除方法【非强制】
    2. 选择适合的数据类型
      1. 应根据所需数据结构选择合适的数据类型如List,Map,Bucket等数据结构
      2. 注意在数据结构与该数据占用内存之间取得平衡
      3. 对于java实体类型,注意缩小数据字段,减小内存消耗,提高性能【建议】
  3. 控制Key的生命周期
    1. 建议使用expire设置过期时间(条件允许可以打散过期时间,防止集中过期),不过期的数据重点关注数据刷新时间
  4. 数据压缩
    1. 对于必须要存储的大文本数据(超过10KB)建议压缩后存储

三 . 命令使用

  1. O(N)命令关注N的数量
     

    1. 例如hgetalllrangesmemberszrangesinter等并非不能使用,但是需要明确N的值。有遍历的需求可以使用hscansscanzscan代替
       
  2. 禁止使用Keys正则(模糊)匹配操作【强制】
     

    1. Redis是单线程处理,在线上Keys数量较多时,操作效率极低【时间复杂度为O(N)】,该命令一旦执行会严重阻塞线上其它命令的正常请求,而且在高QPS情况下会直接造成Redis服务崩溃!如果有类似需求,请使用scan命令代替
       
  3. 禁用命令
    1. 禁止线上使用keys、flushall、flushdb等,通过redis的rename机制禁掉命令,或者使用scan的方式渐进式处理
  4. 合理使用select
    1. redis的多数据库较弱,使用数字进行区分,很多客户端支持较差,同时多业务用多数据库实际还是单线程处理,会有干扰
  5. 使用批量操作提高效率
    1. 批处理方式
      1. 原生命令:例如mget、mset
      2. 非原生命令:可以使用pipeline提高效率,但要注意控制一次批量操作的元素个数(例如500以内,可根据每个元素占用的内存调节批量操作允许的最大元素数)
    2. 注意原生命令和非原生命令的不同
      1. 原生命令是指原子操作,pipeline是非原子操作
      2. pipeline可以打包不同的命令,原生做不到
      3. pipeline需要客户端和服务端同时支持
    3. 灵活使用各客户端提供的批处理操作(如Redisson基于pipelining的RBatch接口)
  6. Redis事务功能较弱,不建议过多使用
    1. Redis原生的事务功能较弱(不支持回滚),而且集群版本(自研和官方)要求一次事务操作的key必须在一个slot上(可以使用hashtag功能解决)
    2. Redisson完整实现了事务的提交与回滚,但仍然无法应用于Redis集群,只能支持单节点与哨兵模式
    3. 大部分Redis的事务都只能应用于Redis操作本身,无法与其他(如数据库事务)作用于同一个事务域
      1. 注:Redisson的收费(Pro)版提供了XA事务接口,可与其他支持的数据库(如Oracle)同时开启JTA事务,但仍然不建议使用

参考资料:

书籍:Redis开发与运维(付磊)

本文地址:https://blog.csdn.net/Ronin_88/article/details/107490993

《项目开发中如何定义Redis 使用规范.doc》

下载本文的Word格式文档,以方便收藏与打印。