Redis最佳实践

Redis最佳实践

键值设计

优雅的key设计

Redis的key虽然可以自定义,但最好遵循以下几个最佳实践约定:

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

优点:

  1. 可读性强
  2. 避免key冲突
  3. 方便管理
  4. 更节省内存: key是string类型,底层编码包含int,embstr和raw三种。embstr在小于44字节使用,采用连续内存空间,内存占用更小

拒绝bigkey

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

  • Key本身的数据量过大:一个String类型的Key,它的值为5MB
  • Key中的成员数过多:一个ZSET类型的Key,它的成员数量为10,000个
  • Key中成员的数据量过大:一个Hash类型的Key,它的成员数量虽然只有1,000个但这些成员的Value(值)总大小
    为100 MB

推荐值:

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

bigkey的危害

  1. 网络阻塞
    对BigKey执行读请求时,少量的QPS就可能导致带宽使用率被占满,导致Redis实例,乃至所在物理机变慢
  2. 数据倾斜
    BigKey所在的Redis实例内存使用率远超其他实例,无法使数据分片的内存资源达到均衡
  3. Redis阻塞
    对元素较多的hash、list、zset等做运算会耗时较旧,使主线程被阻塞
  4. CPU压力
    对BigKey的数据序列化和反序列化会导致CPU的使用率飙升,影响Redis实例和本机其它应用

如何发现bigkey

  1. redis-cli --bigkeys
    利用redis-cli提供的–bigkeys参数,可以遍历分析所有key,并返回Key的整体统计信息与每个数据的Top1的big key
  2. scan扫描
    自己编程,利用scan扫描Redis中的所有key,利用strlen、hlen等命令判断key的长度(此处不建议使用MEMORY USAGE)
  3. 第三方工具
    利用第三方工具,如Redis-Rdb-Tools分析RDB快照文件,全面分析内存使用情况
  4. 网络监控
    自定义工具,监控进出Redis的网络数据,超出预警值时主动告警

批处理优化

单机批处理

原生m命令操作

Pipeline批处理

这里使用redisTemplate,jedis也有pipeline操作,不做展示

SessionCallback & RedisCallback :在同一条连接下执行多个Redis命令。

  1. RedisCallback

    需要处理底层的转换规则,如果不考虑改写底层,尽量不使用

    redisTemplate.execute(new RedisCallback() {
        @Override
        public Object doInRedis(RedisConnection redisConnection) throws DataAccessException {
        	redisConnection.set("key1".getBytes(), "value1".getBytes());
        	redisConnection.hSet("hash1".getBytes(), "field".getBytes(), "test".getBytes());
        	return null;
        }
    });
    
    
  2. SessionCallback

    高级接口,支持事务

    redisTemplate.execute(new SessionCallback() {
        @Override
        public Object execute(RedisOperations redisOperations) throws DataAccessException {
            redisOperations.opsForValue().set("key2", "value2");
            redisOperations.opsForHash().put("hash2", "field", "test");
            return null;
        }
    });
    

集群下的批处理

单机mset和pipeline在集群中出现的问题

如MSET或Pipeline这样的批处理需要在一次请求中携带多条命令,而此时如果Redis是一个集群,那批处理命令的多个key必须落在一个插槽中,否则就会导致执行失败。原因是,redis集群定义了16384个插槽,不同key的slot不同,如果使用传统批处理,批处理中的命令会落在不同的节点,导致批处理失去意义。

解决方案

image-20221030102658601

故解决问题,需要避免服务端计算slot,由客户端计算slot,实现批处理

而springRedisTemplate实现的集群下的批处理操作正是采用并行slot,推荐使用

stringRedisTemplate.opsForValue( ).multiSet() ;

总结

  1. 批处理应注意一次不要携带过多命令,以免网络阻塞
  2. Pipeline多个命令之间不具备原子性
  3. 单机批处理与集群批处理不同,需采取对应的方案

服务器端优化

持久化配置

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

  • 用来做缓存的Redis实例尽量不要开启持久化功能

  • 建议关闭RDB持久化功能,使用AOF持久化

  • 利用脚本定期在slave节点做RDB,实现数据备份

  • 设置合理的rewrite阈值,避免频繁的bgrewrite

    image-20221030105913143

  • 当RDB与AOF都在进行io操作时,或者redis进行了AOF的rewrite,可能引起AOF的io阻塞,导致主线程阻塞,因此应配置no-appendfsync-on-rewrite = yes,禁止在rewrite期间做aof,避免因AOF引起的阻塞

慢查询

在redis执行时耗时超过某个阈值的命令,成为慢查询

慢查询的阈值可以通过配置指定:

  • slowlog-log-slower-than:慢查询阈值,单位是微秒。默认是10000,建议1000慢查询会被放入慢查询日志中,日志的长度有上限,可以通过配置指定:
  • slowlog-max-len:慢查询日志(本质是一个队列)的长度。默认是128,建议1000

命令及安全配置

为了避免这样的漏洞,这里给出一些建议:

  • Redis一定要设置密码
  • 禁止线上使用下面命令: keys、flushall、flushdb、config set等命令。可以利用配置文件修改rename-command禁用
  • 修改配置文件bind:限制网卡,禁止外网网卡访问
  • 开启防火墙
  • 不要使用Root账户启动Redis
  • 尽量不使用默认的端口

内存安全及配置

image-20221030115514626

  • 使用info memory,memory xxx 命令进行redis内存信息的查看

内存缓冲区常见的有三种:

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

集群优化

集群完整性问题

为了保证高可用特性,这里建议将cluster-require-full-coverage配置为false

#当此配置开启时,当集群中有一个插槽不可用(如集群某个节点宕机),那整个集群将不可用
cluster-require-full-coverage yes

集群带宽问题

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

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

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

解决途径
  • 避免大集群,集群节点数不要太多,最好少于1000,如果业务庞大,则建立多个集群
  • 避免在单个物理机中运行太多Redis实例
  • 配置合适的cluster-node-timeout值(定期进行节点间ping的时间)

集群还是主从

集群虽然具备高可用特性,能实现自动故障恢复,但是如果使用不当,也会存在一些问题:

  • 集群完整性问题
  • 集群带宽问题
  • 数据倾斜问题
  • 客户端性能问题
  • 命令的集群兼容性问题
  • lua和事务问题

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

集群模式lua问题补充

在集群下,它会将数据自动分布到不同的节点(虚拟的16384个slot)
它数据的路由分发,是通过计算key,所以只要key一样,则一定会被分到同一个slot
上面的参数keys的个数为1,肯定存储在一个slot里面,
但是如果是多个key且不存在同一个slot里面则会报错,

redis.clients.jedis.exceptions.JedisDataException: ERR 'EVAL' command keys must in same slot**

解决方案:

设置hashtag,在设置key的时候使用{xxxxx}的方式可以将该数据存在用一个slot里面,
但是,注意如果同一个slot里面的数据过多且同时操作查询频繁的数据就会影响到效率问题(数据倾斜)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Acerola-

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值