Redis学习专栏(基础知识):Redis数据结构和常用实现

一. Redis数据结构

String

最基本的数据类型,其值最大可存储512M,二进制安全(Redis的String可以包含任何二进制数据,包含jpg对象等)。
在这里插入图片描述
注:如果重复写入key相同的键值对,后写入的会将之前写入的覆盖。

Hash

String元素组成的字典,适用于存储对象。
在这里插入图片描述

List

列表,按照String元素插入顺序排序。其顺序为后进先出。由于其具有栈的特性,所以可以实现如“最新消息排行榜”这类的功能。
在这里插入图片描述

Set

String元素组成的无序集合,通过哈希表实现(增删改查时间复杂度为O(1)),不允许重复。
在这里插入图片描述
另外,当我们使用smembers遍历set中的元素时,其顺序也是不确定的,是通过hash运算过后的结果。Redis还对集合提供了求交集、并集、差集等操作,可以实现如同共同关注,共同好友等功能。

Sorted Set

通过分数来为集合中的成员进行从小到大的排序。
在这里插入图片描述

更高级的Redis类型

用于计数的HyperLogLog、用于支持存储地理位置信息的Geo。

二. Keys命令

KEYS [pattern]:查找所有符合给定模式pattern的key

使用 keys [pattern] 指令可以找到所有符合pattern条件的key,但是keys会一次性返回所有符合条件的key,所以会造成redis的卡顿,假设redis此时正在生产环境下,使用该命令就会造成隐患,另外如果一次性返回所有key,对内存的消耗在某些条件下也是巨大的。

例:

keys test* //返回所有以test为前缀的key
SCAN cursor [MATCH pattern] [COUNT count]

cursor:游标
MATCH pattern:查询key的条件
count:返回的条数

SCAN是一个基于游标的迭代器,需要基于上一次的游标延续之前的迭代过程。SCAN以0作为游标,开始一次新的迭代,直到命令返回游标0完成一次遍历。此命令并不保证每次执行都返回某个给定数量的元素,甚至会返回0个元素,但只要游标不是0,程序都不会认为SCAN命令结束,但是返回的元素数量大概率符合count参数。另外,SCAN支持模糊查询。

例:

SCAN 0 MATCH test* COUNT 10 //每次返回10条以test为前缀的key

三. Redis常用实现

1.Redis实现分布式锁

分布式锁是控制分布式系统之间共同访问共享资源的一种锁的实现。如果一个系统,或者不同系统的不同主机之间共享某个资源时,往往需要互斥,来排除干扰,满足数据一致性

分布式锁需要解决的问题如下:

互斥性:任意时刻只有一个客户端获取到锁,不能有两个客户端同时获取到锁。

安全性:锁只能被持有该锁的客户端删除,不能由其它客户端删除。

死锁:获取锁的客户端因为某些原因而宕机继而无法释放锁,其它客户端再也无法获取锁而导致死锁,此时需要有特殊机制来避免死锁。

容错:当各个节点,如某个redis节点宕机的时候,客户端仍然能够获取锁或释放锁。

使用SETNX实现

SETNX key value:如果key不存在,则创建并赋值。该命令时间复杂度为O(1),如果设置成功,则返回1,否则返回0。
在这里插入图片描述
由于SETNX指令操作简单,且是原子性的,所以初期的时候经常被人们作为分布式锁,我们在应用的时候,可以在某个共享资源区之前先使用SETNX指令,查看是否设置成功,如果设置成功则说明前方没有客户端正在访问该资源,如果设置失败则说明有客户端正在访问该资源,那么当前客户端就需要等待。

但是如果真的这么做,就会存在一个问题,因为SETNX是长久存在的,所以假设一个客户端正在访问资源,并且上锁,那么当这个客户端结束访问时,该锁依旧存在,后来者也无法成功获取锁,这个该如何解决呢?

由于SETNX并不支持传入EXPIRE参数,所以我们可以直接使用EXPIRE指令来对特定的key来设置过期时间。

用法:EXPIRE key seconds

在这里插入图片描述
程序:

RedisService redisService = SpringUtils.getBean(RedisService.class);
long status = redisService.setnx(key,"1");
if(status == 1){
  redisService.expire(key,expire);
  doOcuppiedWork();
}

这段程序存在的问题:假设程序运行到第二行出现异常,那么程序来不及设置过期时间就结束了,则key会一直存在,等同于锁一直被持有无法释放。出现此问题的根本原因为:原子性得不到满足

解决:从Redis2.6.12版本开始,我们就可以使用Set操作,将Setnx和expire融合在一起执行,具体做法如下。

SET KEY value [EX seconds] [PX milliseconds] [NX|XX]

EX second:设置键的过期时间为second秒。

PX millisecond:设置键的过期时间为millisecond毫秒。

NX:只在键不存在时,才对键进行设置操作。

XX:只在键已经存在时,才对键进行设置操作。

注:SET操作成功完成时才会返回OK,否则返回nil。

有了SET我们就可以在程序中使用类似下面的代码实现分布式锁了:

RedisService redisService = SpringUtils.getBean(RedisService.class);
String result = redisService.set(lockKey,requestId,SET_IF_NOT_EXIST,SET_WITH_EXPIRE_TIME,expireTime);
if("OK.equals(result)"){
  doOcuppiredWork();
}

2. 实现异步队列

  • 使用Redis中的List作为队列

使用上文所说的Redis的数据结构中的List作为队列 Rpush生产消息,LPOP消费消息。
在这里插入图片描述
此时我们可以看到,该队列是使用rpush生产队列,使用lpop消费队列。在这个生产者-消费者队列里,当lpop没有消息时,证明该队列中没有元素,并且生产者还没有来得及生产新的数据

缺点:lpop不会等待队列中有值之后再消费,而是直接进行消费。

弥补:可以通过在应用层引入Sleep机制去调用LPOP重试。

  • 使用BLPOP key [key…] timeout

BLPOP key [key …] timeout:阻塞直到队列有消息或者超时。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
缺点:按照此种方法,我们生产后的数据只能提供给各个单一消费者消费,能否实现生产一次就能让多个消费者消费呢?

  • pub/sub:主题订阅者模式

发送者(pub)发送消息,订阅者(sub)接收消息。

订阅者可以订阅任意数量的频道
在这里插入图片描述
pub/sub模式的缺点:

消息的发布是无状态的,无法保证可达。对于发布者来说,消息是“即发即失”的,此时如果某个消费者在生产者发布消息时下线,重新上线之后,是无法接收该消息的,要解决该问题需要使用专业的消息队列,如kafka…此处不再赘述。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Redis常用数据结构有六种,分别是简单动态字符串(SDS)、链表、字典、跳跃表、整数集合和压缩列表。这些数据结构被用来构建Redis的对象系统,包括字符串对象、列表对象、哈希对象、集合对象和有序集合对象。每种对象都有多种不同的数据结构实现,以适应不同的应用场景。字符串类型是Redis最基础的数据结构,它的值可以是简单的字符串、复杂的字符串(如JSON、XML)、数字(整数、浮点数)甚至二进制数据(如图片、音频、视频),但值的大小不能超过512MB。Redis中的字符串是动态字符串,可以通过预分配冗余空间的方式减少内存分配的频率。字符串的长度不超过1MB时,扩容时会加倍现有的空间;而超过1MB时,每次扩容只会增加1MB的空间。需要注意的是,Redis规定字符串的长度不能超过512MB。123 #### 引用[.reference_title] - *1* [Redis常见数据结构](https://blog.csdn.net/weixin_42348609/article/details/102996982)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}} ] [.reference_item] - *2* [Redis-常用数据结构](https://blog.csdn.net/Huangjiazhen711/article/details/127567141)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}} ] [.reference_item] - *3* [Redis的五种基础数据结构](https://blog.csdn.net/yuyuanlai/article/details/106745757)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT0_1"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

盛夏温暖流年

可以赏个鸡腿吃嘛~

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

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

打赏作者

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

抵扣说明:

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

余额充值