Redis进阶-Redis键值设计及BigKey问题

在这里插入图片描述


键值设计

key设计

  • (1)【建议】: 可读性和可管理性

    以业务名(或数据库名)为前缀(防止key冲突),用冒号分隔,比如业务名:表名:id
    o2o:order:1


  • (2)【建议】:简洁性

    保证语义的前提下,控制key的长度,当key较多时,内存占用也不容忽视,例如:
    user:{uid}:friends:messages:{mid} 简化为 u:{uid}🇫🇷m:{mid}


  • (3)【强制】:不要包含特殊字符

    反例:包含空格、换行、单双引号以及其他转义字符


value设计

  • (1)【强制】:拒绝bigkey(防止网卡流量、慢查询)

  • (1)(2)【推荐】:选择适合的数据类型。
    例如:实体类型(要合理控制和使用数据结构,但也要注意节省内存和性能之间的平衡)

    反例:

    	set user:1:name tom
    	set user:1:age 19
    	set user:1:favor football
    

    正例:

     hmset user:1 name tom age 19 favor football
    

  • (1)3.【推荐】:控制key的生命周期,redis不是垃圾桶
    建议使用expire设置过期时间(条件允许可以打散过期时间,防止集中过期)

big key

我们知道,Redis 的一个字符串最大512M,一个二级数据结构(比如 hash、list、set 、zset)可以存储2^32-1 个元素 ,约40亿个元素。 但是不是以为着我们可以任意存储元素呢? 时刻牢记,在读写这个角度上,目前Redis还是单线程的。


定义

其实不然,按照经验来说 ,如何定义bigKey 呢?

  • 字符串类型:它的big体现在单个value值很大,一般认为超过10KB就是bigkey。
  • 非字符串类型:哈希、列表、集合、有序集合,它们的big体现在元素个数太多。

一般来说,string类型控制在10KB以内,hash、list、set、zset元素个数不要超过5000。当然了这不是绝对的,请依据场景,灵活处理。


反例

我们来看个反例: 一个hash 存储用户信息,我有100万用户,我全都放到一个key里。。。。这不管从哪个角度看 ,bigkey无疑。


bigkey的产生

一般来说,bigkey的产生都是由于程序设计不当,或者对于数据规模预料不清楚造成的,来看几个例子:

  • 社交类:粉丝列表,如果某些明星的粉丝数据,如果不精心设计下,一个明星的粉丝 百万很少了吧,你都把这百万的粉丝数据放到一个key中存储,毫无疑问是bigkey
  • 统计类:比如按天存储某项功能或者网站的用户集合,用户很少,倒是没多大问题,一旦用户多了起来,必是bigkey
  • 缓存类:将数据从数据库加载出来以后序列化放到Redis里,这个方式非常常用,但有两个地方需要注意,第一,是不是有必要把所有字段都缓存;第二,有没有相关关联的数据,不要为了图方便把相关数据都存一个key下,产生bigkey。

如何优化bigkey

核心思想: 分治 拆分


  • big list: list1、list2、…listN
    big hash:可以讲数据分段存储,比如一个大的key,假设存了1百万的用户数据,可以拆分成 200个key,每个key下面存放5000个用户数据

  • 如果bigkey不可避免,也要思考一下要不要每次把所有元素都取出来(例如有时候仅仅需要
    hmget,而不是hgetall),删除也是一样,尽量使用优雅的方式来处理

  • 【推荐】:选择适合的数据类型

    例如:实体类型(要合理控制和使用数据结构,但也要注意节省内存和性能之间的平衡)
    反例:

    set user:1:name tom
    set user:1:age 19
    set user:1:favor football
    

    正例:

     hmset user:1 name tom age 19 favor football
    
  • 【推荐】:控制key的生命周期,redis不是垃圾桶

    建议使用expire设置过期时间(条件允许可以打散过期时间,防止集中过期)。


删除bigKey的注意事项

对于非字符串的bigkey,比如 hash list set zset , 不要使用del 删除, 请使用 hscan 、sscan、zscan方式渐进式删除。

同时要注意防止bigkey过期时间自动删除问题(例如一个100万的hash设置1小时过期,会触发del操作,造成阻塞)


bigkey的危害
  • 导致redis阻塞

    这个也很好理解: 我们知道Redis 官方号称10万QPS, 我们通常打不到这个值,但是大几万的QPS还是没问题的,这也就意味着 redis 的执行速度 1秒几万条, 速度相当的快的。 假设你有个bigKey , 操作一次耗时1秒,那Redis 单线程 在这1秒钟就只能处理你这个Key, 后面堵了一堆请求。。。。 并且你的应用 序列化和反序列化这种大key , 也消耗CPU 。

  • 导致网络拥塞

    假设我们的交换机,千兆网络(小b),那么 实际带宽 1024 / 8 = 128M . 假设你的这个key的大小 500KB, 客户端并发 1000获取这个key, 那么就意味着 1000 * 500KB = 500M ,那就是每秒产生500M的流量。先不说你的Redis能不能处理的过来这个并发下的bigKey,单说你的这个千兆网络, 你说你这个网络I/O能扛得住吗? 一般服务器会采用单机多实例的方式来部署,也就是说一个bigkey可能会对其他实例也造成影响,其后果不堪设想。


  • 过期删除- Redis4.0新特性(三)-Lazy Free

    针对那种我们设置了过期时间的big key , 在redis4.0前,没有lazy free功能,我们只能通过类似scan big key,每次删除少量的元素,分多次删除;但在面对“被动”删除键的场景,这种取巧的删除就无能为力。

    举个例子:Redis Cluster大集群,业务缓慢地写入一个带有TTL的2000多万个字段的Hash键,当这个键过期时,redis开始被动清理它时,导致redis被阻塞20多秒,结果发生了fail over ,造成故障。

    Redis 4.0提供了过期异步删除(lazyfree-lazyexpire yes)

    lazy free 惰性删除或延迟释放: 当删除键的时候,redis提供异步延时释放key内存的功能,把key释放操作放在Background I/O单独的子线程处理中,减少删除big key对redis主线程的阻塞,有效地避免删除big key带来的性能和可用性问题。

    redis4.0有lazy free功能后,这类主动或被动的删除big key时,时间复杂度O(1)。

    在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

小小工匠

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

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

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

打赏作者

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

抵扣说明:

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

余额充值