Redis数据结构,一个字牛。

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Redis数据结构,一个字牛。

redis的数据结构

redis前导知识

redis是什么?


The open source, in-memory data store used by millions of developers as a database, cache, streaming engine, and message broker.


进入官网是这么显示展示的,从这能够得到的信息就是:1:redis是开源的。2:redis是用来做数据存储的数据库。3:数百万的开发者用redis来做数据库,缓存,流式处理引擎和消息代理的。


redis的优势


  • 高性能 – Redis能读的速度是110000次/s,写的速度是81000次/s 。
  • 丰富的数据类型 – Redis支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。
  • 原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。
  • 丰富的特性 – Redis还支持 publish/subscribe, 通知, key 过期等等特性。


写文章的目的


目前的工作做中redis扮演者越来越重要的角色,但是我看挺多人都只会使用redis的字符串,当然选择也应该根据实际的业务场景来选择合适的数据结构。由此我想对此做一个总结,同时自己也做一个复习。好了废话说了这么多下面就进入我们的主题吧!


数据结构


String


单值缓存


set key value //存储
get key  //取值


对象缓存


set user:1 vale(json格式数据)  //
get user:1 
mset user:1:name qijian user:2:balance 188
mget user:1:name user:1:balance


缓存对象: key(对象+id+属性) value(值)


如果经常需要修改对象的部分属性使用 第二中方式方便一点。如果使用第一种需要获取出来使用 json转换为对象


分布式锁


**setnx当且仅当key不存在的时候才会设置成功。如果当前的key已经被其他线程设置过,这条命令不会做任何操作。**在多线程的情况下,只有第一个线程执行该条命令的线程才能执行成功,那么就可以认为该条线程获得了分布式锁。


setnx product:10001 true  //返回1代表获取锁成功
setnx product:10001 true  //返回0代表获取锁失败
...执行业务操作
del product:10001         //执行完业务释放锁
set product:10001 true ex 10 nx   //防止程序意外终止导致死锁


计数器


INCR article:readcount:{文章id}   //只要文章被打开就执行这条命令,没执行一次就加一
GET article:readcount:{文章id}      // 获取最终值



这个使用也特别多:比如用来计算浏览量,访问量等等。


hash结构


Hash常用操作
HSET  key  field  value       //存储一个哈希表key的键值 
注意:如果 key 指定的哈希集不存在,会创建一个新的哈希集并与key 关联。否则,重写覆盖
HSETNX  key  field  value     //存储一个不存在的哈希表key的键值
HMSET  key  field  value [field value ...]  //在一个哈希表key中存储多个键值对
HGET  key  field        //获取哈希表key对应的field键值
HMGET  key  field  [field ...]    //批量获取哈希表key中多个field键值
HDEL  key  field  [field ...]     //删除哈希表key中的field键值
HLEN  key       //返回哈希表key中field的数量
HGETALL  key        //返回哈希表key中所有的键值
HINCRBY  key  field  increment    //为哈希表key中field键的值加上增量increment


注意:每个 hash 可以存储 2^32 - 1 键值对(40多亿)。


对象缓存


对象缓存
HMSET  user  {userId}:name  zhuge  {userId}:balance  1888
HMSET  user  1:name  QiJian  1:balance  1888
HMGET  user  1:name  1:balance


**注意:如果存在上千万条甚至上亿条记录,就不太还是使用这种存储方式。在redis中最怕的就是大key(big key),因为大key的话会阻塞redis。当几百万条的数据时,就只有一个user ,当获取所有的值的时候。这一条命令需要执行很长的时间。big key 会阻塞redis,解决办法:**分段存储


应用场景:


购物车

c7518151123e5f5de7fce0c385015a33.png


如上面的购物车中,我们可以做如下存储:


1)以用户id为key 2)商品id为field 3)商品数量为value. 这样就可以简单的做一个购物车商品的操作。代码,如下:


购物车操作
添加商品 hset user:1001 10088 1
增加数量 hincrby user:1001 10088 1
商品总数 hlen user:1001
删除商品 hdel user:1001 10088
获取购物车所有商品 hgetall user:1001


但是需要特别注意:过期功能不能使用在field上,只能用在key上。也就是说使用hash结构时,不能对field使用过期功能。


List结构


List常用操作
LPUSH  key  value [value ...]     //将一个或多个值value插入到key列表的表头(最左边)
RPUSH  key  value [value ...]   //将一个或多个值value插入到key列表的表尾(最右边)
LPOP  key     //移除并返回key列表的头元素
RPOP  key     //移除并返回key列表的尾元素
LRANGE  key  start  stop    //返回列表key中指定区间内的元素,区间以偏移量start和stop指定
BLPOP  key  [key ...]  timeout  //从key列表表头弹出一个元素,若列表中没有元素,阻塞等待          timeout秒,如果timeout=0,一直阻塞等待
BRPOP  key  [key ...]  timeout  //从key列表表尾弹出一个元素,若列表中没有元素,阻塞等待          timeout秒,如果timeout=0,一直阻塞等待


注意:列表最多可存储 2^32 - 1 元素 (4294967295, 每个列表可存储40多亿)。


使用List结构还可以用来做栈,队列和阻塞队列


Stack(栈) = LPUSH + LPOP

Queue(队列)= LPUSH + RPOP

Blocking MQ(阻塞队列)= LPUSH + BRPOP


应用场景:微博和微信公众号消息流


显示消息时,存在时间上的先后顺序,先发消息时展示在前面,后发消息展示在后面


1)A发微博,消息ID为10018
LPUSH  msg:{user-ID}  10018
2)B微博,消息ID为10086
LPUSH  msg:{user-ID} 10086
3)查看最新微博消息
LRANGE  msg:{user-ID}  0  4 


Set


Redis的Set是string类型的无序集合。集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是O(1)。


Set常用操作
SADD  key  member  [member ...]     //往集合key中存入元素,元素存在则忽略,若key不存在则新建
SREM  key  member  [member ...]     //从集合key中删除元素
SMEMBERS  key         //获取集合key中所有元素
SCARD  key          //获取集合key的元素个数
SISMEMBER  key  member      //判断member元素是否存在于集合key中
SRANDMEMBER  key  [count]     //从集合key中选出count个元素,元素不从key中删除
SPOP  key  [count]        //从集合key中选出count个元素,元素从key中删除
Set运算操作
SINTER  key  [key ...]        //交集运算
SINTERSTORE  destination  key  [key ..]   //将交集结果存入新集合destination中
SUNION  key  [key ..]         //并集运算
SUNIONSTORE  destination  key  [key ...]    //将并集结果存入新集合destination中
SDIFF  key  [key ...]         //差集运算
SDIFFSTORE  destination  key  [key ...]   //将差集结果存入新集合destination中


运算


SINTER set1 set2 set3 -> { c }     //交集(所有共有)
SUNION set1 set2 set3 -> { a,b,c,d,e }   // 合集(所有元素)
SDIFF set1 set2 set3 -> { a }   // 差集(只看第一个集合set1)


注意:SDIFF set1 set2 set3 第一个集合减去后面两个集合的并集。(set1 - set2 n set3)

应用场景:

微信抽奖小程序
1)点击参与抽奖加入集合
SADD key {userlD}
2)查看参与抽奖所有用户
SMEMBERS key
3)抽取count名中奖者
SRANDMEMBER key [count] 或 SPOP key [count]


Zset


ZSet常用操作
ZADD key score member [[score member]…] //往有序集合key中加入带分值元素
ZREM key member [member …]    //从有序集合key中删除元素
ZSCORE key member       //返回有序集合key中元素member的分值
ZINCRBY key increment member    //为有序集合key中元素member的分值加上increment 
ZCARD key       //返回有序集合key中元素个数
ZRANGE key start stop [WITHSCORES]  //正序获取有序集合key从start下标到stop下标的元素
ZREVRANGE key start stop [WITHSCORES] //倒序获取有序集合key从start下标到stop下标的元素
Zset集合操作
ZUNIONSTORE destkey numkeys key [key ...]   //并集计算
ZINTERSTORE destkey numkeys key [key …] //交集计算


应用场景:


根据商品销售对商品进行排序显示:定义商品排行榜(sorted set集合),key为goods:sellsort,分数为商品的销售数量。


1)商品编号1001的销量是9,商品编号对的1002的销售是15


zadd goods:sellsort 9 1001 15 1002


2)客户下单2件1001商品


zincrby good:sellsort 2 1002
• 1


3)获取商品销售前十


zrange goods:sellsort 0 10 withscores


以上可以说是redis的核心数据结构,相对比较常见就不再继续深入了。但是在数据量大的时候依然不能满足我们的需求。下面还有三种新类型。


bitmap


面试:如何记录对集合中的数据进行统计。

例如;在移动应用中,需要统计每天的新增用户和第二天留存的用户数;在签到打卡中需要统计一个月内连续打卡的用户数;

在网页上统计独立访客(UV)量;京东领取金豆的签到日历;


京东领取金豆签到日历.png


这时有人就会说了,前面提到的数据结构已经能够做到上面的需求了啊,例如上面的签到日历,使用string结构,签了是1,没有签是2。别急我们接着往下看。


没错很显然,使用上面的sting结构就可做到。但是用没有想过一个问题如果数据量和大的时候呢?在现代信息化时代用户访问的级别为亿级不过分吧?比如头条,抖音,淘宝,京东等,上亿级别不为过吧。


biimap的基本思想就是使用一个bit位来标记某个元素对应的value,而key就是该元素,这样就可以大大节省存储空间。本质上位图就是一个普通的字符串,也就是bit数组,如下:


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bV3fuEEF-1664703181420)(https://qijian-1301807797.cos.ap-guangzhou.myqcloud.com/markdown/%E4%BD%8D%E5%9B%BE.png)]


说到此时我想大家应该想到了,如果使用位图来存储京东的打卡日历,签到就1,否则0.最后就会形成用0和1组成一串数字。注意这里每一天的记录只占一位,也就是一年就占365位,所占字节等于365/8,约等于46个字节。这时如果想要统计用户的签到的天数,那么只需要统计这一年里1出现的次数。会发现相比较于使用string(每一个1就需要一个字节),使用位图大大节省了空间同时也提高了效率。


一时过瘾说了bitmap的这么多。接下来我们看看常见的命令:


setbit key offset value  //用来设置或清楚某一位上的值。初始状态下所有位都是0 offset代表偏移量从0开始
getbit key offset //获取某一位上的值,不存在返回0;注意这里用string的get key是获取不到正确的数的,使用get会以16进制来显示。
bitcount key start end //统计指定位区间上,值为1的个数。可以使用负数,代表倒数第几位
strlen key //统计占多上字节数


注意:bitmap 的数组是由二进制位组成,每个二进制都对应一个偏移量。支持的最大位数是2^ 32位,这时也只需要大概512M左右。


不知不觉已经5:30 ,还有就是hayerloglog , geo。国庆假期先出去玩会 回来写,哈哈哈。

相关文章
|
2月前
|
存储 缓存 NoSQL
【📕分布式锁通关指南 12】源码剖析redisson如何利用Redis数据结构实现Semaphore和CountDownLatch
本文解析 Redisson 如何通过 Redis 实现分布式信号量(RSemaphore)与倒数闩(RCountDownLatch),利用 Lua 脚本与原子操作保障分布式环境下的同步控制,帮助开发者更好地理解其原理与应用。
159 6
|
1月前
|
消息中间件 缓存 NoSQL
Redis各类数据结构详细介绍及其在Go语言Gin框架下实践应用
这只是利用Go语言和Gin框架与Redis交互最基础部分展示;根据具体业务需求可能需要更复杂查询、事务处理或订阅发布功能实现更多高级特性应用场景。
211 86
|
3月前
|
存储 缓存 NoSQL
Redis核心数据结构与分布式锁实现详解
Redis 是高性能键值数据库,支持多种数据结构,如字符串、列表、集合、哈希、有序集合等,广泛用于缓存、消息队列和实时数据处理。本文详解其核心数据结构及分布式锁实现,帮助开发者提升系统性能与并发控制能力。
|
1月前
|
存储 消息中间件 NoSQL
Redis数据结构:别小看这5把“瑞士军刀”,用好了性能飙升!
Redis提供5种基础数据结构及多种高级结构,如String、Hash、List、Set、ZSet,底层通过SDS、跳表等实现高效操作。灵活运用可解决缓存、计数、消息队列、排行榜等问题,结合Bitmap、HyperLogLog、GEO更可应对签到、UV统计、地理位置等场景,是高性能应用的核心利器。
|
1月前
|
存储 缓存 NoSQL
Redis基础命令与数据结构概览
Redis是一个功能强大的键值存储系统,提供了丰富的数据结构以及相应的操作命令来满足现代应用程序对于高速读写和灵活数据处理的需求。通过掌握这些基础命令,开发者能够高效地对Redis进行操作,实现数据存储和管理的高性能方案。
80 12
|
1月前
|
存储 消息中间件 NoSQL
【Redis】常用数据结构之List篇:从常用命令到典型使用场景
本文将系统探讨 Redis List 的核心特性、完整命令体系、底层存储实现以及典型实践场景,为读者构建从理论到应用的完整认知框架,助力开发者在实际业务中高效运用这一数据结构解决问题。
|
1月前
|
存储 缓存 NoSQL
【Redis】 常用数据结构之String篇:从SET/GET到INCR的超全教程
无论是需要快速缓存用户信息,还是实现高并发场景下的精准计数,深入理解String的特性与最佳实践,都是提升Redis使用效率的关键。接下来,让我们从基础命令开始,逐步揭开String数据结构的神秘面纱。
|
5月前
|
存储 NoSQL 算法
Redis设计与实现——数据结构与对象
Redis 是一个高性能的键值存储系统,其数据结构设计精妙且高效。主要包括以下几种核心数据结构:SDS、链表、字典、跳跃表、整数集合、压缩列表。此外,Redis 对象通过类型和编码方式动态转换,优化内存使用,并支持引用计数、共享对象和淘汰策略(如 LRU/LFU)。这些特性共同确保 Redis 在性能与灵活性之间的平衡。
|
12月前
|
存储 消息中间件 NoSQL
Redis 数据结构与对象
【10月更文挑战第15天】在实际应用中,需要根据具体的业务需求和数据特点来选择合适的数据结构,并合理地设计数据模型,以充分发挥 Redis 的优势。
240 64
|
存储 NoSQL 算法
「Redis」数据结构与对象
Redis数据结构与对象介绍
129 0

热门文章

最新文章