目录
简介
进入之后身份认证才能使用
优点
用途:
数据结构
string
string自动扩容
Redis中的简单动态字符串(SDS)具有以下优点:
SDS数据的编码格式
比较:
string 常用操作
分布式锁
使用情况,保证接口幂等性 ?
分布式锁的应用???
list
常用操作
早期版本
压缩列表
优点
缺点
快速链表(quiklist)
set
set常用操作
set应用???
zset有序集合
存储结构
跳跃列表(skipList)
哈希散列表
存储结构
简介
Redis是一个开源的内存中数据结构存储系统,通常用作数据库、缓存和消息代理。它支持各种数据结构,包括字符串、哈希、列表、集合等,并提供了丰富的功能,如事务、持久化、发布/订阅等。
-
内存中数据结构存储系统:Redis主要将数据存储在内存中,这使得它具有非常高的读写速度。它通过使用持久化技术来保证数据在重启后不会丢失,因此即使是存储在内存中,也能够满足持久化的需求。
-
数据库、缓存和消息代理:Redis可以用作多种用途,包括传统数据库的替代品、缓存系统和消息代理。作为数据库,它可以存储各种类型的数据,并支持复杂的数据结构操作;作为缓存,它可以提供快速的数据访问,减轻后端数据库的压力;作为消息代理,它支持发布/订阅模式,可以用于实时通信和事件驱动架构。
-
支持多种数据结构:Redis支持多种数据结构,包括字符串(String)、哈希(Hash)、列表(List)、集合(Set)、有序集合(Sorted Set)等。这些数据结构能够满足不同场景下的数据存储和操作需求。
-
丰富的功能:除了基本的数据存储和检索功能外,Redis还提供了许多高级功能,如事务(Transaction)、持久化(Persistence)、发布/订阅(Pub/Sub)等。这些功能使得Redis在各种应用场景下都能发挥重要作用,并且具有很高的灵活性和扩展性。
总的来说,Redis是一个功能强大、性能优异的内存中数据存储系统,适用于各种场景下的数据管理和应用开发。
安装
sudo apt install redis-server
进入之后身份认证才能使用
redis-cli
auth 123456
优点
-
高性能: Redis是一个内存数据库,操作在内存中进行,因此具有快速的读写速度。
-
数据多样性: 支持不同类型的数据结构,如字符串、哈希、列表、集合、有序集合等,以及Bitmap和GEO地理信息等特殊数据类型。
-
持久化: 支持持久化数据到硬盘,可以根据需求选择RDB快照或者AOF日志文件的方式进行持久化。
-
发布/订阅模式: 支持消息代理模式,可以实现发布/订阅功能,方便实现消息通信。
-
分布式缓存: 支持主从复制和高可用性,可以搭建分布式缓存系统,提高系统的可靠性和容错性。
-
事务支持: 支持事务操作,可以将多个命令打包成一个原子操作进行执行,但不支持回滚。
-
功能丰富: 提供丰富的功能,如脚本执行(通过Lua脚本),可以实现复杂的业务逻辑。
用途:
-
排行榜: 使用有序集合(Sorted Set)数据类型,将每个成员的分数作为排名依据,可以通过自动排序功能实现排行榜功能。
-
限速器/计数器: 使用INCR命令对一个键进行自增操作,可以实现计数器功能。例如,可以将某个操作的频率限制在一定的速率内。
-
推送: 使用SET命令设置键值对,然后通过PUBLISH命令将消息推送给订阅者,实现简单的消息推送功能。
-
消息队列: 使用RPUSH命令向列表尾部添加消息,然后使用LPOP或者BLPOP命令从列表头部获取消息,可以实现基本的消息队列功能。LBPOP是一个阻塞命令,会在列表为空时等待直到有消息到来。
-
会话一致性: 可以使用Redis作为会话存储,将会话信息存储在Redis中,实现会话的统一管理和一致性。
MEMCATCH
数据结构
五种基础类型:
string
int类型的编码如果超过长度就会转化为string类型
SDS简单动态字符串:
基本结构结构体
struct sdshdr{//记录buf数组中已使用字符的数量,等于 SDS 保存字符串的长度int len;//记录 buf 数组中未使用的字符数量int free;//字符数组,用于保存字符串char buf[];
}
// 存储 “hello”
len:5
free:0
buf:['H','e','l','l','o','\0']
如上观察所得:
Redis 每次给 string 分配的空间都要大于字符串实际占用的空间,这样就在一定程度上提升了 Redis string 存储的效率,比如当字符串长度变大时,无需再重新申请内存空间。
string自动扩容
当字符串所占空间小于 1MB 时,Redis 对字符串存储空间的扩容是以成倍的方式增加的;而当所占空间超过 1MB 时,每次扩容只增加 1MB。Redis 字符串允许的最大值字节数是 512 MB。
Redis中的简单动态字符串(SDS)具有以下优点:
-
获取字符串长度的时间复杂度为O(1): SDS通过保存字符串的长度信息,使得获取字符串长度的操作时间复杂度为O(1),无论字符串长度多长,都能够在常数时间内完成。
-
安全的API: Redis的SDS API设计安全,函数使用是安全的,避免了由于字符串操作而引发的内存越界等问题。
-
保存二进制数据: SDS不仅可以保存文本数据,还可以保存二进制数据,因为SDS使用了类似C语言中的字符数组来存储数据,所以可以保存任意类型的数据,包括文本和二进制数据。
SDS数据的编码格式
在Redis中,简单动态字符串(SDS)可以存储文本数据,也可以存储二进制数据。在存储二进制数据时,Redis会使用 embstr(embedded string)或者 raw 编码来存储。
-
Embstr(嵌入式字符串): 当字符串长度较短(小于等于39字节,不同版本有所不同)时,Redis会选择使用 embstr 编码,将字符串直接存储在 SDS 结构体中,而不会进行额外的内存分配。这种方式节省了内存,提高了效率。
-
Raw 编码: 当字符串长度超过39字节时,Redis会使用 raw 编码,将字符串存储在单独的堆内存中,并在 SDS 结构体中保存指向堆内存的指针。这种方式适用于较长的字符串,可以避免嵌入式字符串带来的额外内存消耗。
比较:
-
embstr 编码创建字符串对象时,调用一次内存分配函数,raw调用两次
-
embstr 编码释放字符串对象时,调用一次内存释放函数,raw 调用两次
-
embstr 编码的字符串对象保存在一块连续的内存中,可以更好地利用 CPU 缓存提升性能
tips:
如果修改数据类型会怎样变换,会从embstr类型转为raw类型
string 常用操作
set | 设置字符串 |
---|---|
get | 读取字符串 |
incr | 自增 |
decr | 自减 |
exists | 判断key是否存在 |
strlen | 返回字符串长度 |
del | 删除key |
exprie | 设置过期时间 |
ttl | 查看剩余时间 |
sentnx | 不再存在就插入(成功返回(1)) |
分布式锁
在分布式系统中,当不同进程或线程一起访问共享资源时,会造成资源争抢,如果不加以控制的话,就会引发程序错乱。此时使用分布式锁能够非常有效的解决这个问题,它采用了一种互斥机制来防止线程或进程间相互干扰,从而保证了数据的一致性。
SETNX buy_lock value
setnxl buy 【接口名】 value 【time -》【过期时间】】
这样的命令来尝试获取名为 buy_lock
的锁。如果返回值是 1,表示成功获取了锁,可以执行后续的操作;如果返回值是 0,表示锁已经被其他客户端持有,需要等待或进行其他处理。
tip
设置过期时间:在设置分布式锁时,可以同时设置一个过期时间,以防止锁被持有的客户端出现异常或挂掉,导致锁无法正常释放而产生死锁问题。在 Redis 中,你可以使用 EXPIRE
命令来为锁设置一个过期时间,确保即使锁没有被主动释放,也能在一定时间后自动过期释放。
使用情况,保证接口幂等性 ?
Redis 分布式锁有很对应用场景,举个简单的例子,比如春运时,您需要在 12306 上抢购回家火车票,但 Redis 数据库中只剩一张票了,此时有多个用户来预订购买,那么这张票会被谁抢走呢?Redis 服务器又是如何处理这种情景的呢?在这个过程中就需要使用分布式锁。
分布式锁的应用???
分布式锁在各种场景下都有广泛的应用:
-
短链接服务:在短链接服务中,当多个用户同时请求生成短链接时,为了避免生成相同的短链接或者保证短链接的唯一性,可以使用分布式锁来确保同一时刻只有一个请求能够成功生成短链接。
-
会话存储:在分布式系统中,需要存储用户的会话信息,以实现用户登录状态的管理和跨服务的会话共享。使用分布式锁可以确保在写入或更新会话信息时的数据一致性和并发安全。
-
限流控制:在防止被恶意攻击或者保护服务稳定性的情况下,可以使用分布式锁来实现限流控制。例如,限制某个接口每秒的请求次数,通过分布式锁来控制并发请求的数量,避免服务被过载或者被恶意攻击。
list
常用操作
Redis中的列表(list)是一种支持双向操作的数据结构,类似于链表。它允许在列表的头部或尾部进行快速的添加和删除操作。这里是Redis列表的一些常用操作:
-
添加元素:
LPUSH key value [value ...]
:向列表头部插入一个或多个元素。RPUSH key value [value ...]
:向列表尾部插入一个或多个元素。
-
删除元素:
LPOP key
:从列表头部弹出一个元素。RPOP key
:从列表尾部弹出一个元素。LREM key count value
:从列表中删除指定数量的特定值。
-
获取元素:
LINDEX key index
:根据索引获取列表中的元素,负数索引从列表尾部开始计数。LRANGE key start stop
:获取列表指定范围的元素。
-
设置元素:
LSET key index value
:设置列表中指定索引处的值。
-
列表长度:
LLEN key
:获取列表的长度。
-
删除列表:
LTRIM key start stop
:保留列表指定范围内的元素,删除其他部分。
早期版本
如果列表元素个数小于512,采用压缩列表
列表每个元素的值都小于64字节
就会使用压缩列表ziplist
否则使用快速链表quicklist
压缩列表
- 1、zlbytes:压缩列表的字节长度,占4个字节,因此压缩列表最长(2^32)-1字节;
- 2、zltail:压缩列表尾元素相对于压缩列表起始地址的偏移量,占4个字节;
- 3、zllen:压缩列表的元素数目,占两个字节;那么当压缩列表的元素数目超过(2^16)-1怎么处理呢?此时通过zllen字段无法获得压缩列表的元素数目,必须遍历整个压缩列表才能获取到元素数目;
- 4、entryX:压缩列表存储的若干个元素,可以为字节数组或者整数;entry的编码结构后面详述;
- 5、zlend:压缩列表的结尾,占一个字节,恒为0xFF。
下面是entry每个元素的格式:
previous字段 为1Byte/5Byte
压缩列表(Compressed List)在存储和操作数据时具有一些优点,但同时也存在一些缺点。让我们更深入地探讨这些优点和缺点。
优点
-
内存紧凑: 压缩列表通过减少冗余元数据和指针来节省内存空间。这种紧凑的结构特别适用于存储小型数据和少量元素。
-
高效存储: 由于压缩列表以连续存储的方式组织数据,因此它在存储效率和内存占用上表现良好。
-
灵活性: 压缩列表可以存储不同类型的数据,且不需要为每个元素额外分配内存空间,如链表中的指针或对象引用。
缺点
-
不能保存过多元素: 压缩列表的结构在一定数量的元素下是高效的,但是当元素数量超出一定限度时,列表可能需要扩展和重组,导致性能下降。更大的数据集需要更多的内存和计算资源。
-
不能保存过大的元素: 如果压缩列表中的某个元素非常大,可能会导致整个列表需要重新分配内存空间,称为连锁更新(Chain Update)。这是因为在压缩列表中,节点是连续存储的,改变一个节点可能影响整个列表的布局。
-
不适合频繁修改: 由于压缩列表是紧凑存储的,插入和删除操作可能涉及重新排列整个列表,因此在频繁修改的场景下,效率可能低于其他数据结构,如链表。
-
搜索和索引性能受限: 压缩列表的结构可能不适合复杂的搜索和索引操作。它通常适用于简单的遍历和查找,但不适用于需要高效索引的数据集。
快速链表(quiklist)
quicklist其实就是一个分段的ziplist,为什么这么说呢?其实quicklist存储数据基本单位是quicklistNode,每个quicklistNode的内容区就是以ziplist数据结构存储的。
set
Redis set (集合)遵循无序排列的规则,集合中的每一个成员(也就是元素,叫法不同而已)都是字符串类型,并且不可重复。Redis set 是通过哈希映射表实现的,所以它的添加、删除、查找操作的时间复杂度为 O(1)。集合中最多可容纳 2^32 - 1 个成员(40 多亿个)
set常用操作
-
sadd:添加新元素
-
smembers:列出集合中所有元素
-
sismember:判断元素是否在集合中
-
scard:返回集合中元素个数
-
srem:移除集合中一个或多个元素,如果移除的不存在就不处理
-
sinter:对两个集台求交集
-
sunion:对两个集合求并集
-
sdiff:对两个集合求差集
-
setnx:向 Redis 中添加一个 key,只用当 key 不存在的时候才添加并 返回1,存在则不添加返回 0
set应用???
在Redis中,Set是一种无序、不重复的数据集合,它的应用非常广泛。
-
集合运算:Redis提供了一系列的集合运算命令,如并集(Union)、交集(Intersection)、差集(Difference)等,可以对多个Set进行操作,得到新的集合结果。
-
标签(Tagging):可以将Set用于标签系统,每个元素代表一个标签,可以方便地对对象进行分类和检索。
-
好友关系:可以使用Set来表示用户的好友关系,每个用户的好友列表可以用一个Set来存储,方便进行好友相关操作,如查找共同好友、计算两个用户的共同好友数等。
-
唯一值存储:由于Set中的元素是唯一的,可以将Set用于存储唯一值,如网站的访问IP地址、用户的行为记录等。
-
实时统计:可以利用Set来进行实时统计,比如统计网站独立访客数、活跃用户数等。
-
过滤器:可以将Set用作过滤器,比如用于去重、检查某个元素是否存在等。
-
兴趣标记:可以将Set用于存储用户的兴趣标记,比如用户喜欢的电影、音乐等,方便进行个性化推荐。
zset有序集合
Redis zset(有序集合)中的成员是有序排列的,它和 set 集合的相同之处在于,集合中的每一个成员都是字符串类型,并且不允许重复;而它们最大区别是,有序集合是有序的,set 是无序的,这是因为有序集合中每个成员都会关联一个 double(双精度浮点数)类型的 score (分数值),Redis 正是通过 score 实现了对集合成员的排序。
存储结构
有序集合(zset)同样使用了两种不同的存储结构,分别是 zipList(压缩列表)和 skipList(跳跃列表),当 zset 满足以下条件时使用压缩列表:
- 成员的数量小于128 个;
- 每个 member (成员)的字符串长度都小于 64 个字节。
当 zset 使用压缩列表保存数据时,entry 的第一个节点保存 member,第二个节点保存 score。依次类推,集合中的所有成员最终会按照 score 从小到大排列。
当不满足使用压缩列表的情况时就使用跳跃列表
跳跃列表(skipList)
typedef struct zskiplistNode {// 后退指针struct zskiplistNode *backward;// 分值 权重double score;// 成员对象robj *obj;// 层struct zskiplistLevel {// 前进指针struct zskiplistNode *forward;// 跨度unsigned int span;} leval[];
} zskiplistNode;typedef struct zskiplist {// 表头节点和表尾节点struct zskiplistNode *header, *tail;// 表中节点的数量unsigned long length;// 表中层数最大的节点的层数int leval;
} zskiplist;
-
zskiplistNode 结构体:
- backward:指向前一个节点的指针,用于向前遍历跳跃表。
- score:节点的分值或权重,用于有序集合中排序节点。
- obj:成员对象,实际存储在节点中的值。
- leval:表示节点的层级信息的数组,包括前进指针和跨度(表示跨越的节点数量)。
-
zskiplist 结构体:
- header, tail:指向跳跃表头节点和尾节点的指针,用于快速访问跳跃表的起始和结束位置。
- length:跳跃表中节点的数量,用于记录表的大小。
- leval:表中层数最大的节点的层数,表示跳跃表的最大层级。
哈希散列表
Redis hash(哈希散列)是由字符类型的 field(字段)和 value 组成的哈希映射表结构(也称散列表),它非常类似于表格结构。在 hash 类型中,field 与 value 一一对应,且不允许重复。
存储结构
hash 类型是 Redis 常用数据类型之一,其底层存储结构有两种实现方式。
第一种,当存储的数据量较少的时,hash 采用 ziplist 作为底层存储结构,此时要求符合以下两个条件:
- 哈希对象保存的所有键值对(键和值)的字符串长度总和小于 64 个字节。
- 哈希对象保存的键值对数量要小于 512 个。
当无法满足上述条件时,hash 就会采用第二种方式来存储数据,也就是 dict(字典结构),该结构类似于 Java 的 HashMap,是一个无序的字典,并采用了数组和链表相结合的方式存储数据。在 Redis 中,dict 是基于哈希表算法实现的,因此其查找性能非常高效,其时间复杂度为 O(1)。
**哈希散列表也会有哈希冲突的现象