Redis数据结构及典型应用

 

小功能大用处!Redis是NoSQL数据库的优秀代表,除了基本的5种数据结构外,还包括bitmaps、GEO、HyperLogLog、stream等扩展结构,使Redis的应用场景更加丰富。基于Redis两大特点:纯内存+单线程,是面向快速执行场景的数据库,请开发人员在合适的应用场景,选择合适的结构类型和命令,谨慎设计和使用o(n)复杂度的命令。

常用结构类型

  • String                       字符串
  • Hash                         哈希
  • List                            列表
  • Set                            集合
  • Zset                          有序集合
  • Bitmaps                     位图
  • GEO                           地理信息定位(V4.0+)
  • HyperLogLog            基数统计
  • Streams                     多播消息队列(V5.0+)

Redis针对不同数据结构类型,都有2种以上的内部编码,底层编码不同,将直接影响key的内存占用空间和读写效率。

1. String 字符串

类型说明:

string是key-value键值对,是Redis最基础的数据结构,是其它几种数据结构类型的构建基础。

存储内容:

字符串(JSON / XML)、数字(整数 / 浮点数)、二进制(图片 / 音频 / 视频等)

最大值不能超过512MB

基本用法:

Set key value [ex seconds] [px milliseconds] [nx|xx] 

  • EX second :设置键的过期时间为 second 秒
  • PX millisecond :设置键的过期时间为 millisecond 毫秒
  • NX :只在键不存在时,才对键进行设置操作
  • XX :只在键已经存在时,才对键进行设置操作

ttl key: 查看剩余的过期时间(秒),返回-1表未设置过期时间,返回-2表示key不存在或已过期
persist key: 删除过期时间设置

setex key seconds value : 与set ex效果相同

setnx key value: 与set nx效果相同
说明:SET KEY VALUE命令会覆盖同名key的value,同时也会删除KEY的过期时间。

典型应用场景

1.通用缓存: 经典使用场景,把常用信息,字符串,图片或者视频等信息放到redis中,redis作为缓存层,mysql做持久化层,降低mysql的读写压力。

2. 计数器:用于下载量、点击量、在线人数等。

3. 共享session:分布式负载均衡web服务场景,使用Redis存储集中共享session,如用户登录信息等。

4. 限速:保护某些接口不被频繁访问,例如IP地址、手机验证码限制在每分钟内的访问频次等设计。

5. 分布式锁:参考方式 SET Key Value NX PX milliseconds+lua脚本,其基本原则:

  • 互斥性,任意时刻只能有一个客户端持有锁
  • 不能死锁,锁持有者即使意外崩溃未释放,也要保证后续其他客户端可以继续加锁
  • lua脚本判断释放锁条件,即加锁和解锁为同一客户端,避免解锁非自己创建的锁,另确保为原子操作

 

开发技巧

1. 合理使用mget、mset、pipeline等多值命令,提高批量操作的效率
    命令处理时间通常小于网络传输时间,减少网络开销的次数将会极大提高操作效率;
    批量操作的命令/KEY数量一次不易过多,否则会出现Redis处理阻塞或者网络延迟。

    优化前:

    优化网络时间:

2. 线上节点禁止使用或少用Keys遍历键,建议使用scan替代

    KEYS pattern:一次性过滤所有键,在Key数量特别多时,由于单线程架构模式会出现命令阻塞,导致主从节点切换甚至实例崩溃
    建议:从库节点扫描或者Key数量比较少的场景下使用
    SCAN cusor [match pattern] [count number]:渐进式遍历,有效解决了KEYS阻塞问题,但是如果业务有较大的写入,可能会出现新增的键未遍历到或者遍历到重复键的情况
    建议:对扫描结果一致性要求不高的场景下使用
    应用:快速查看key,执行scan 0,第二次迭代使用的是第一次迭代时返回的游标

    

注意事项: 

  • 不要将所有数据都放到Redis内存中,只放热数据和有用的数据
  • 长字符串尽量压缩存储,控制在512字节以内
  • 合理添加过期时间策略,根据业务使用满足需求的最小时间

 

2. Hash散列表

类型说明:
哈希类型所存储的键值本身又是一个键值对结构!是一个Mapmap,如 value={{field1,value1},......fieldN,valueN}}


典型应用场景:
对象信息,比如用户基础信息、商品信息、软件信息等

注意事项:

  • 单个hash类型成员个数不要过多
  • Hgetall获取所有成员谨慎使用,容易带来性能问题,IO和网卡流量是瓶颈,推荐使用hscan

3. List列表

类型说明:
列表类型是用来存储多个有序的字符串!【长度2^32 -1】value可以重复,可以通过下标取出对应的value值。

List是双向链表实现的,是按照插入顺序排序的字符串链表,可以在其头部(left)和尾部(right)添加新的元素。

典型应用场景:

  • 消息队列、粉丝/关注列表等
  • timeline:时间轴,缓存用户最近浏览商品,或发布微博,用lpush加入时间轴,展示新的列表信息
  • 通过lrange命令(从某个元素开始读取多少个元素)实现高性能分页查询,达到下拉不断分页的效果

开发技巧:

  • lpush+lpop=Stack(栈)
  • lpush+rpop=Queue(队列)
  • lpush+ltrim=Capped Collection(有限集合)
  • lpush+brpop=Message Queue(消息队列)

安全消息队列RPOPLPUSH,避免消息未正常处理就丢失 :

  1. 每个下游消费者维护一个私有的任务处理队列
  2. 每次通过rpoplpush原子命令,从主消息队里获取并写入私有队列
  3. 任务处理完成后从私有队列rpop出队列删除
  4. 独立监控程序,如果私有队列任务超时未处理,将其再放回主消息队列继续由其它下游客户端处理

注意事项:

  • 在插入时,如果该键不存在,Redis将为该键创建一个新的链表
  • 如果链表中所有的元素均被移除,该键也会被删除

4. Set集合

类型说明:
集合类型用来存储多个字符串元素,集合内部成员是无序的,且不允许有重复元素。支持集合间的操作,可以取多个集合取交集、并集、差集。


典型应用场景:
常用于社交网站、推荐业务,如:

  1. 兴趣标签(tag):给用户或者消息添加标签,实现如共同关注、共同粉丝、共同喜好等功能
  2. 好友、粉丝统计
  3. 点赞和收藏


注意事项:

  • 单个set类型成员个数不要过多
  • smembers命令获取所有成员谨慎使用,很容易带来性能问题(sscan)

5. Zset有序集合

类型说明:
Sorted-Sets 有序集合类型存储多个字符串,不允许有重复元素,但是可以排序!
每一个成员都会有一个分数(score)与之关联,通过分数来为集合中的成员进行从小到大的排序。成员是唯一的,但是分数(score)却是可以重复的。位置索引:分数最低的索引为0.

典型应用场景:

  • 排行榜:视频网站播放量、软件下载量、游戏积分排行榜
  • 社交:直播系统实时在线用户列表,各种礼物排行榜等

注意事项:

  • 单个zset类型成员个数不要过多
  • zset的命令大部分实现复杂度较高,一些高频操作慎重

6. Bitmaps位图

类型说明:
Bitmaps是一个可以对位操作的字符串!是Redis实现的BloomFilter,需要说明的是,bitmap并不是一种真实的数据结构,它本质上是String数据结构,只不过操作的粒度变成了位,即bit。

因为String类型最大长度为512MB,所以bitmap最多可以存储2^32个bit。

典型应用场景:
统计活跃用户、用户在线状态、用户标签等

注意事项:

  • 第一次初始化时,如果偏移量比较大会比较慢,会有阻塞现象发生
  • 预分配内存,如果设置值较大但统计值较少,会有很大的内存消耗

7. GEO地理位置信息

类型说明:
存储地理位置信息!【V3.2+版本新增】需要说明的是,Geo本身不是一种数据结构,它本质上还是借助于Sorted Set(ZSET),并且使用GeoHash技术进行填充。

GEO数据结构可以在Redis中存储地理坐标,并且坐标有限制,由EPSG:900913 / EPSG:3785 / OSGEO:41001 规定如下:

  • 有效的经度从-180度到180度
  • 有效的纬度从-85.05112878度到85.05112878度

Redis中处理这些地理位置坐标点的思想是:

二维平面坐标点 --> 一维整数编码值 --> zset(score为编码值) --> zrangebyrank(获取score相近的元素)、zrangebyscore --> 通过score(整数编码值)反解坐标点 --> 附近点的地理位置坐标。

典型应用场景:
附近的位置、摇一摇等

注意事项:

  • 底层实现基于zset,可以使用zrem、zrange等相关命令进行操作
  • 合理控制GEO集合的大小,各个命令操作复杂度较高,过大的集合会影响操作效率

8. HyperLogLog类型

类型说明:
HyperLogLog也不是新的数据结构,本质也是字符串类型。是一种基数算法,可以利用极小的内存空间完成大量独立数的统计!

典型应用场景:
大访问量UV统计等

注意事项:

  • 基于该算法的统计并不是精准的,官方给出的误差率是0.81%
  • 精确度和成本之间做特定的取舍

9. Streams

类型说明:  
这是Redis5.0引入的全新数据结构,Stream的消费模型借鉴了kafka的消费分组的概念,但kafka的消息可以分partition,而Stream消息分组需要在客户端配置,提供不同的Stream名称,对消息进行hash取模来选择往哪个Stream里追加。

Streams消息是持久化的,它弥补了Redis Pub/Sub不能持久化消息的缺陷。

开发技巧:

为了解决Stream消息积累太多,导致内存暴涨,Redis提供在xadd的指令提供定长长度maxlen,可以将历史消息删除,确保最多不超过指定长度。

典型应用场景:

完善的消息发布订阅功能组件:相比于现有的PUB/SUB、BLOCKED LIST,也可以在简单的场景下作为消息队列来使用。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值