Redis最佳实践

一、Redis键值设计

1.1、优雅的key结构

Redis的key,最佳实践约定:

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

好处

  • 可读性强
  • 避免key冲突
  • 方便管理
  • 更节省内存

1.2、拒绝BigKey

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

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

推荐值

  • 单个key的value小于10KB
  • 对于集合类型的key,建议元素数量小于1000
1.2.1、BigKey的危害
  • 网络阻塞
    • 对BigKey执行读请求时,少量的QPS就可能导致带宽使用率被占满,导致Redis实例,乃至所在物理机变慢;
  • 数据倾斜
    • BigKey所在的Redis实例内存使用率远超其他实例;无法使数据分片的内存资源达到均衡;
  • Redis阻塞
    • 对元素较多的hash、list、zset等做运算会耗时较旧,使主线程被阻塞;
  • CPU压力
    • 对BigKey的数据序列化和反序列化会导致CPU的使用率飙升,影响Redis实例和本机其它应用
1.2.2、如何删除BigKey

BigKey内存占用较多,即便是删除这样的Key也需要耗费很长时间,导致Redis主线程阻塞,引发一系列问题。

  • redis3.0及以下版本
    • 如果是集合类型,则遍历BigKey的元素,先逐个删除子元素,最后删除BigKey
  • redis4.0以后
    • 异步删除:unlink

1.3、恰当的数据类型

例1:比如存储一个User对象,我们有三种存储方式

①方式一:json字符串
| user:1 | {“name”: “Jack”, “age”: 21} |

  • 优点:实现简单粗暴
  • 缺点:数据耦合,不够灵活
    ②方式二:字段打散
    在这里插入图片描述
  • 优点:可以灵活访问对象任意字段
  • 缺点:占用空间大,没办法做统一控制
    ③方式三:hash(推荐)
    在这里插入图片描述
    优点:底层使用zipList,空间占用小,可以灵活访问对象的任意字段
    缺点:代码相对复杂

1.4、总结

  • Key的最佳实践
    • 固定格式:【业务名】:【数据名】:【id】
    • 足够简短:不超过44字节
    • 不包含特殊字符
  • Value的最佳实践
    • 合理的拆分数据,拒绝BigKey
    • 选择合适数据结构
    • Hash结构的entry数量不要超过1000
    • 设置合理的超时时间

2、批处理优化

2.1、Pipeline

我们的客户端与redis服务器是这样交互的

  • 单个命令的执行流程
    在这里插入图片描述
  • N条命令的执行流程
    在这里插入图片描述

redis处理指令是最快的,主要花费的时候在于网络传输。于是乎很容易想到将多条指令批量的传输给redis

在这里插入图片描述

2.1.1、Mset

Redis提供了很多Mxxx这样的命令,可以实现批量插入数据,例如:

  • mset
  • hmset
@Test
void testMxx() {
    String[] arr = new String[2000];
    int j;
    long b = System.currentTimeMillis();
    for (int i = 1; i <= 100000; i++) {
        j = (i % 1000) << 1;
        arr[j] = "test:key_" + i;
        arr[j + 1] = "value_" + i;
        if (j == 0) {
            jedis.mset(arr);
        }
    }
    long e = System.currentTimeMillis();
    System.out.println("time: " + (e - b));
}
2.1.3、Pipeline

MSET虽然可以批处理,但是却只能操作部分数据类型,因此如果有对复杂数据类型的批处理需要,建议使用Pipeline

@Test
void testPipeline() {
    // 创建管道
    Pipeline pipeline = jedis.pipelined();
    long b = System.currentTimeMillis();
    for (int i = 1; i <= 100000; i++) {
        // 放入命令到管道
        pipeline.set("test:key_" + i, "value_" + i);
        if (i % 1000 == 0) {
            // 每放入1000条命令,批量执行
            pipeline.sync();
        }
    }
    long e = System.currentTimeMillis();
    System.out.println("time: " + (e - b));
}

2.2、集群下的批处理

如MSET或Pipeline这样的批处理需要在一次请求中携带多条命令,而此时如果Redis是一个集群,那批处理命令的多个key必须落在一个插槽中,否则就会导致执行失败。大家可以想一想这样的要求其实很难实现,因为我们在批处理时,可能一次要插入很多条数据,这些数据很有可能不会都落在相同的节点上,这就会导致报错了
四种解决方案:
在这里插入图片描述

3、服务器端优化-持久化配置

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

  • 用来做缓存的Redis实例尽量不要开启持久化功能
  • 建议关闭RDB持久化功能,使用AOF持久化
  • 利用脚本定期在slave节点做RDB,实现数据备份
  • 设置合理的rewrite阈值,避免频繁的bgrewrite
  • 配置no-appendfsync-on-rewrite = yes,禁止在rewrite期间做AOF,避免因AOF引起的阻塞
  • 部署有关建议:
    • Redis实例的物理机要预留足够内存,应对fork和rewrite
    • 单个Redis实例内存上限不要太大,例如4G或8G。可以加快fork的速度,减少主从同步、数据迁移压力
    • 不要与CPU密集型应用部署在一起
    • 不要与高硬盘负载应用一起部署。比如:数据库、消息队列

4、服务器端优化-慢查询优化

4.1、什么是慢查询

在Redis执行时耗时超过某个阈值的命令,称为慢查询;
慢查询的危害:由于Redis是单线程的,所以当客户端发出指令后,他们都会进入到redis底层的queue来执行,如果此时有一些慢查询的数据,就会导致大量请求阻塞,从而引起报错,所以我们需要解决慢查询问题。
在这里插入图片描述
慢查询的阈值可以通过配置指定:

  • lowlog-log-slower-than: 慢查询阈值,单位是微秒。默认是10000,建议1000
  • slowlog-max-len: 慢查询日志(本质是一个队列)的长度。默认是128,建议1000

4.2、如何查看慢查询

  • slowlog len: 查询慢查询日志长度
  • slowlog get [n]: 读取n条慢查询日志
  • slowlog reset: 清空慢查询列表
    在这里插入图片描述

5、服务器端优化-Redis内存划分和内存配置

当Redis内存不足时,可能导致Key频繁被删除、响应时间变长、QPS不稳定等问题。当内存使用率达到90%以上时就需要我们警惕,并快速定位到内存占用的原因

有关碎片问题分析

Redis底层分配并不是这个key有多大,他就会分配多大,而是有他自己的分配策略,比如8,16,20等等,假定当前key只需要10个字节,此时分配8肯定不够,那么他就会分配16个字节,多出来的6个节点就不能被使用,这就是我们常说的碎片问题

进程内存问题分析

这片内存,通常我们都可以忽略不计

缓冲区内存问题分析

一般包括客户端缓冲区、AOF缓冲区、复制缓冲区等等。客户端缓冲区又包括输入缓冲区和输出缓冲区两种。这部分内存占用波动较大,所以这片内存也是我们需要重点分析的内存问题。
在这里插入图片描述

我们可以通过一些命令,可以查看到Redis目前的内存分配状态

  • info memory: 查看内存分配的情况
    在这里插入图片描述
  • memory xxx: 查看key的主要占用情况
    在这里插入图片描述
    接下来我们看到了这些配置,最关键的缓存区内存如何定位和解决呢?
    内存缓冲区常见的有三种:
  • 复制缓冲区:主从复制的repl_backlog_buf,如果太小可能东芝频繁的全量复制,影响性能。通过replbacklog-size来设置,默认1mb
  • AOF缓冲区:AOF刷盘之前的缓存区域,AOF执行rewrite的缓冲区。无法设置容量上限
  • 客户端缓冲区:分为输入缓冲区和输出缓冲区,输入缓冲区最大1G且不能设置。输出缓冲区可以设置

以上复制缓冲区和AOF缓冲区不会有问题,最关键就是客户但缓冲区的问题
客户端缓冲区:指的就是我们发送命令时,客户端用来缓存命令的一个缓冲区,也就是我们向redis输入数据的输入端缓冲区和redis向客户端返回数据的响应缓存区,输入缓冲区最大1G且不能设置,所以这一块我们根本不用担心,如果超过了这个空间,redis会直接断开,因为本来此时此刻就代表着redis处理不过来了,我们需要担心的就是输出端缓冲区

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值