Redis最佳实践总结

1.优雅的Redis的Key结构

Redish中的key虽然可以自定义,但是我们在命名的时候最好遵守以下规范

  • 遵循基本的格式 [业务名称]:[数据名称]:[id]。
    • 优点
      • 可读性强
      • 避免key的重读
      • 方便管理

比如保存用户登录信息的key可以这样命名

login:user:32

  • 不要包含特殊的字符
  • 长度不要超过44字节

要求key的长度不小于44是因为在key的类型都是String,在redis的底层存储字符串有三种形式

  • 如果字符串是数值类型,则直接转换成int
  • 如果字符串长度小于44且不是数字,则使用embstr方式存储,否则使用raw方式存储
  • embStr方式在连续的空间中存储字符串,raw使用离散方式存储字符串,类似数组和链表的区别。因此raw占用的空间要更大一些。

2.BigKey问题

2.1 什么是BigKey

BigKey通常是值一些Key的大小以及Key对应的value的大小很大的键值对

  • Key本身的数据量过大,一个String类型的Key,value值约为5MB
  • Key中的成员数量过多,一个set集合可能包含100000个元素
  • Key中成员所占用的空间较大,虽然一个set中元素只有100个,可是每一个元素占用的空间可能很大。

推荐值

  • 单个Key的value小于10KB
  • 对于集合类型的Key建议数量小于1000

2.2 BigKey的危害

  • 导致网络阻塞,对于一些Bigkey,可能少量的网络请求就可以将服务器的带宽占满。
  • 数据倾斜:在集群环境下BigKey所在实例的内存利用率远远高于其他实例,导致内存分配不均衡
  • Redis阻塞:在元素较多的list set做运算的时候耗时较长,导致主线程被阻塞。
  • CPU压力:对BigKey数据的序列化和反序列化会导致CPU的利用率飙升。

2.3 搜索BigKey

  • 使用redis -cli --bigKeys命令:只能找到每一种类型占用空间最大的key。
  • 使用Scan扫描,这种方式一次扫描一部分
  • 第三方工具 如Redis-Rdb-Tools
  • 网络监控

3. 选择合适的数据类型

在Redis中不同的数据类型占用的内存是不同的,我们应该结合我们数据本身的特点去选择合适的数据类型。

3.1 例子

比如我们要存储一个User对象到Redis中,有3种数据类型可以选择。

  • 使用字符串存储User对象的JSON
    在这里插入图片描述
  • 将字符串打散,每一个key存储一个用户的一种信息
    在这里插入图片描述
  • 利用哈希存储
    在这里插入图片描述

3.2 举例2在这里插入图片描述

前面提到哈希存储会将数据压缩成ZipList进一步节省空间。但是这个压缩是有条件的。当哈希结构中的entry数目超过500的时候,就不在使用ziplist压缩存储了。
这里可以通过hash-max-ziplist-entries来指定entry的上限。

3.2.1 方案一 利用String存储

将hash的每一条记录全部都取出来,然后使用String类型存储。
在这里插入图片描述
存在的问题

  • String类型底层没有很多的优化,导致占用内存非常多
  • 想要批量获取这些数据变得非常困难
    在这里插入图片描述
    我们可以看到,在使用了String类型存储以后导致占用比之前使用哈希存储的空间还要大。
3.2.2 将哈希打散

在这里插入图片描述
这种的好处

  • 采用哈希存储,相比使用String来说占用的空间更小
  • 每一个哈希键里面只存储100条数据,可以利用ziplist进一步压缩大小
  • 相比String,这种方式在批量获取元素的时候比较方便。
    在这里插入图片描述
    可以看到使用这种方式内存占用仅为24M

4. Key Value最佳实践总结

4.1 Key的最佳实践

  • 使用固定格式的key [业务名] : [数据名] : [id]
  • key的长度不超过44个字节
  • 不要包含特殊字符

4.2 Value的最佳实践

  • 合理拆分数据,不要使用BigKey
  • 选择合理的数据结构,减少内存的占用
  • 哈希存储保证entry不要超过500
  • 设置合理的超时时间

5. Redis中的批处理

5.1 传统单条命令执行的问题

传统的Redis执行命令的方式是一条命令一条命令的执行,但是在需要插入海量数据的时候,这种单条命令执行的方式效率很低。

在这里插入图片描述
这里我们来测试一下向Redis中插入10w条数据的耗时

@Test
void test() {
    for (int i = 1; i <= 100000; i++) {
        jedis.set("test:key" + i, "value_" + i);
    }
}

在这里插入图片描述
耗时长达44秒。
实际上,Redis执行N条命令的耗时应该是N次网络传输的时间+N次Redis执行命令的时间。事实上,Redis执行命令的时间是非常短的,几乎可以忽略不计。因此大部分的时间都是花费在了网络传输阶段。

5.2 使用批处理方式解决

正如上面分析的,如果我们将我们要执行的命令打包,然后一次性发送给redis,那么实际上花费的时间就变成1次网络传输的时间+N次Redis执行命令的时间,时间会大大缩短。
在这里插入图片描述
这里我们使用Redis中的mset命令测试插入10w条数据的耗时。
在这里插入图片描述
可以看到耗时仅为182毫秒。对比之前的44秒,提升明显。

5.3 使用Pipeline批处理

虽然存在mset和hmset命令,但是大部分的命令都是设置一个key的多个值,而不能设置多个key多个值。为了解决上述问题,可以使用Pipeline解决。
在这里插入图片描述

Pipeline和Redis内置批处理的对比
Pipeline的执行时间要比Redis内置的批处理慢一些,原因是Redis的批处理是原子性的,执行过程中不会有其他的操作打断,而Pipeline方式只是将多个命令同时发送给Redis,命令与命令之间不一定是原子性,可能在执行过程中又有别的命令进来,导致执行的速度降低。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值