˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱
ʕ̯•͡˔•̯᷅ʔ大家好,我是xiaoxie.希望你看完之后,有不足之处请多多谅解,让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客
本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN 如需转载还请通知˶⍤⃝˶
个人主页:xiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客系列专栏: xiaoxie的redis学习系列专栏——CSDN博客●'ᴗ'σσணღ
我的目标:"团团等我💪( ◡̀_◡́ ҂)"( ⸝⸝⸝›ᴥ‹⸝⸝⸝ )欢迎各位→点赞👍 + 收藏⭐️ + 留言📝+关注(互三必回)!
目录
1.hash类型
1.hash类型表示格式
2.常用命令
1.hset vs hsetnx
2.hget
3.hexists
4.hdel
5.hkeys
6.hvals
7.hgetall
8.hmget
9.hlen
10.hincrby vs hincrbyfloat
编辑
3.内部编码
1.压缩列表(ziplist)
2.哈希表(hashtable)
4.应用场景
1.用户信息存储
2.缓存优化
1. 原生字符串类型
2. 序列化字符串类型(如JSON)
3. 哈希类型
3.购物车实现
4.统计计数:
5.对象映射:
2.list类型
1.list类型表示格式
2.list类型常用命令
1.lpush vs lpushx
2.rpush vs rpushx
3.lrange
4. lpop vs rpop
编辑
5.lindex
6.linsert
7.llen
8. lset
9.lrem
10.ltrim
11.阻塞版本命令
3.list类型内部编码
quicklist(快速列表)
4.应用场景
1.消息队列
2.秒杀系统
3.日志记录:
4.排行榜/最新动态:
5.缓存列表数据:
6.分布式工作队列:
这里友情提升:阅读这篇前,博主建议可以先阅读博主的String类型的总结博客,可以帮助你更好的学习,hash类型和list类型
1.hash类型
哈希类型,就是除了 key 和 value 是键值对的形式外,value 又是一个键值对的表示形式,形如:
Key: "key" value:"{{field1,value1}, {field2,value2}....}"这样的形式表示.
1.hash类型表示格式
这里我们和String类型来对比就能够清晰的知道hash类型的表示形式了.
例如:这里我们需要储存一个 studentID为 1 ,年龄为 20 ,姓名为 张三 的学生到redis中
注意:哈希类型的映射关系是 {field,value}这里value 不是 key 所对应的value 而是field 对应的value 并且这样命名,主要是为了和reids整体的映射关系{key, value}有所区别.
2.常用命令
1.hset vs hsetnx
hset :设置hash中指定的 field 和 value .返回值:添加的字段的个数
hsetnx:在field 和 value.不存在的情况下,设置hash中的field 和 value.返回值:1表示设置成功,0表示失败。
语法:
hset key field value [field value]....
hsetnx key field value [field value]....
2.hget
获取hash中指定字段的值
语法:
hget key field
返回值:字段对应的值或者nil。这里的的等于nil 可能是 field不存在,也可能是key不存在,这里需要注意.
3.hexists
判断hash中是否有指定的字段.
语法:
hexists key field
返回值: 0表示不存在,1表示存在
和 hget一样 field-value 存在的前提是 key 存在.
4.hdel
删除hash中指定的字段。
语法:
hdel key field [field]...
返回值:本次成功删除的字段个数.
本次操作删除的字段个数。注意这里是可以删除多个字段的,并且也建议一次删除多个,因为每一次删除,redis客户端就要向服务端通过网络发送一次请求,所以尽量一次删多个,而不是多次删一个.这个要特别注意, 并且还要注意,redis是单线程架构的所以一次删除也不要删除太多个字段,防止,redis在本次操作中花费太多时间,阻塞其他的操作.
还是一样hdel 字段 的前提是 key 存在.
5.hkeys
获取hash中的所有字段。
语法:
hkeys key
时间复杂度:O(n)这里的n表示的是字段的个数
返回值:字段列表
这里注意hkeys如果是我们在平时练习中可以偶尔使用,但是等到你工作的时候,特别是在生产环境下使用hkeys的话,可能会使redis服务被阻塞,因为我们并不知道redis中是否是有大量的field存在.所以谨慎小心对待.
6.hvals
获取hash中的所有的值。
语法:
hvals key
时间复杂度:O(n) n 为字段的个数
返回值:字段列表
和 hkeys 的注意事项一样这里就不过多的解释了.
7.hgetall
获取hash中的所有字段以及对应的值。
语法:
hgetall key
时间复杂度:O(n) n 为字段的个数
返回值:字段和对应的值
这里注意事项和hkeys 的注意事项一样,并且还更加不建议在生存环境下使用 hgetall,这里需要重点注意.
8.hmget
一次获取hash中多个字段的值。
语法:
hmget key fidle field...
时间复杂度: 只查询⼀个元素为O(1),查询多个元素为O(N),N为查询元素个数
返回值:字段对应的值或者nil。
本次操作为获取的字段对应的值。注意这里是可以获取多个字段的,并且也建议一次获取多个,因为每一次获取字段,redis客户端就要向服务端通过网络发送一次请求,所以尽量一次获取多个,而不是多次获取一个.这个要特别注意, 并且还要注意,redis是单线程架构的所以一次获取也不要获取太多个字段,防止,redis在本次操作中花费太多时间,阻塞其他的操作.
9.hlen
获取hash中的所有字段的个数。
语法:
hlen key
时间复杂度:O(1)
返回值:字段个数。
10.hincrby vs hincrbyfloat
hincrby : 将hash中字段对应的数值添加指定的值。
hincrbyfloat: hincrby 的浮点数版本
hincrby key field increment
hincrbyfloat key field increment
时间复杂度:O(1)
返回值:该字段变化之后的值。
注意:这里的 increment 如果是正数就是加如果为负就为减.
hincrby:
hincrbyfloat
3.内部编码
1.压缩列表(ziplist)
适用条件:
- 当一个哈希类型包含的键值对数量小于
hash-max-ziplist-entries
配置的限制(默认是512个)。 - 同时,每个键值对中的值的长度都小于
hash-max-ziplist-value
配置的限制(默认是64字节)。
优点:
- 内存高效:Ziplist通过连续存储的方式,减少了数据结构本身的开销,比如不需要额外的指针来指向每个条目,因此在存储小量且简单的数据集时非常节省内存。
- 紧凑性:数据紧密排列,没有冗余空间,适合存储在内存中。
缺点:
- 读写性能:随着元素数量增加或单个值的大小超过阈值,ziplist的插入、删除操作会变得更慢,因为它可能需要移动大量数据来保持元素的连续性。
2.哈希表(hashtable)
适用条件:
- 当哈希类型的数据不满足ziplist的使用条件时,Redis会自动转换为使用hashtable。
优点:
- 读写性能:哈希表提供了恒定时间复杂度O(1)的查找、插入和删除操作,非常适合大规模数据集,能够快速访问数据。
- 扩展性:Redis的哈希表还实现了动态扩容机制,可以在数据增长时自动调整大小,以保持高效的性能。
缺点:
- 内存消耗:相比于ziplist,哈希表为了实现快速访问牺牲了一定的内存效率。每个条目都需要额外的空间来存储指针等信息。综上所述,Redis通过动态选择ziplist或hashtable作为哈希类型的内部实现,旨在根据实际数据特点达到内存使用和访问性能的最佳平衡。对于小规模且简单的哈希数据,ziplist有助于节省内存;而当数据量或复杂度增长到一定程度时,hashtable则能确保高效的读写操作。同时注意这里的数字不需要去背诵,这些数字都是可以配置的,并且这些数字都是考虑普遍现象的,真正在实际开发中,不同的业务场景,所配置的值可能会不一样,所以我们并不需要去背诵它,主要是理解它的原理.
4.应用场景
哈希类型(HASH)是Redis中一种非常灵活且常用的数据结构,它允许用户将多个字段(field)及其对应的值(value)存储在一个单一的键(key)下。这种数据结构非常适合以下应用场景:
1.用户信息存储
在社交网络、在线游戏或任何需要用户管理的系统中,可以使用一个哈希来存储单个用户的全部信息,如 user:<userid>
,其中字段可以包括用户名、邮箱、密码哈希、注册日期等。这样,通过一次查询就能获取或更新用户的完整资料
关系型数据库保存用户信息:
代表:MySQL
映射关系保存用户信息:
代表:Redis的hash类型
同时需要注意的是哈希类型和关系型数据库(MySQL)有几点不同之处:
-
数据结构与灵活性:
- Redis哈希类型:提供了键值对的集合,每个键可以关联一个哈希结构,该结构内含多个字段-值对。这种结构非常灵活,可以直接操作单个字段而无需加载整个数据集,适合快速读写操作,尤其是在数据结构较为简单或数据量不大时更为高效。
- MySQL表:基于表格模型,每个表由固定的列和动态的行组成,列定义了数据的结构,每一行代表一条记录,所有的记录都遵循相同的结构。在MySQL中,必须预先定义表结构,包括所有列的名称和数据类型,之后的数据插入和查询都需遵循这一结构,这在处理复杂关系和大量结构化数据时更为强大。
-
事务和数据一致性:
- Redis:虽然Redis支持事务(multi, exec命令),但它的事务是简单且非阻塞的,不支持回滚,并且在事务执行过程中其他客户端的修改操作仍会被立即执行,这与ACID特性中的隔离性有所不同。
- MySQL:支持更强大的事务处理能力,遵循ACID原则,可以确保在多条SQL语句组成的事务中,要么全部执行成功,要么全部失败回滚,保证了数据的一致性和完整性。这对于金融、银行等对数据准确性要求极高的领域至关重要。
-
扩展性和性能:
- Redis哈希:设计上更侧重于速度和低延迟访问,特别适合缓存、计数器等场景,通过ziplist和hashtable的自动转换等机制优化内存使用和访问速度。
- MySQL:虽然也能通过索引优化查询速度,但其强项在于处理复杂的查询和连接操作,以及大数据量下的数据管理和分析。MySQL可以通过分片、主从复制等方式扩展,但相对于Redis在某些高并发读写场景下的性能可能会有所不足。
-
数据持久化与内存使用:
- Redis:虽然Redis可以配置为在内存中运行,但也可以配置数据定期持久化到磁盘,平衡了速度与数据安全性。Redis更适合存储短时间内频繁访问的数据。
- MySQL:数据默认存储在磁盘上,可以配置不同的存储引擎(如InnoDB, MyISAM)来适应不同的性能需求,虽然访问速度相对Redis较慢,但能更好地处理大规模数据存储和持久化需求。
综上,Redis哈希类型和MySQL在数据模型、事务处理、性能特性、扩展方式及数据持久化等方面各有优势,适用于不同的应用场景和需求,一切从业务的角度出发
2.缓存优化
1. 原生字符串类型
描述:每个属性对应一个独立的键值对,如user:1:name
、user:1:age
等。
优点:
- 简单直接:实现逻辑清晰简单,易于理解和维护。
- 灵活操作:单独更新或获取某个属性非常方便,不会影响其他属性。
缺点:
- 内存占用:由于每个属性都需要单独的键,导致内存使用较高。
- 分散管理:用户信息分散存储,缺乏内聚性,难以进行整体操作。
2. 序列化字符串类型(如JSON)
描述:将整个用户对象序列化为一个字符串(如JSON格式),然后作为一个键值对存储。
优点:
- 整体操作便捷:适合于经常需要整体读写的情况,减少网络往返和操作次数。
- 内存效率:如果序列化得当,可以有效减小存储空间,提高内存使用效率。
缺点:
- 序列化开销:每次读写都需要序列化和反序列化,增加了CPU负担。
- 局部更新不便:如果频繁进行单个属性的更新或查询,效率较低,需要读取整个对象再进行修改和写回。
3. 哈希类型
描述:使用HMSET
或HSET
命令,将所有属性作为一个哈希存储在单个键下。
优点:
- 高效灵活:同时具备整体和局部操作的高效性,特别是对特定属性的读写非常方便。
- 结构清晰:数据组织结构清晰,易于理解和操作,保持了数据的内聚性。
缺点:
- 编码转换:需要关注ziplist和hashtable两种内部编码的自动转换条件,不当的使用可能导致内存使用波动较大。
- 潜在的性能考量:在极端情况下(如哈希元素数量非常大或值非常大),性能可能不如预期,需根据实际使用情况调优。
综上所述,每种方法都有其最适合的应用场景。原生字符串类型适合属性频繁独立操作且数据量不大的情况;序列化字符串类型适合数据整体读写频繁且对内存效率有要求的场景;哈希类型则在需要灵活操作各个属性且数据结构相对复杂时最为合适。选择哪种方式应基于具体业务需求、数据访问模式和性能考量。
除此之外,redis还有其他的应用场景例如
3.购物车实现
在电商应用中,可以利用哈希类型来实现购物车功能,每个用户有一个唯一的购物车键,商品ID作为字段,商品数量作为值。这使得添加、删除商品或查询购物车内容都非常高效。
4.统计计数:
虽然字符串类型也常用于计数,但哈希可以在单个键下存储多个计数器,适用于需要跟踪多种指标的场景,比如网站的不同页面访问次数。
5.对象映射:
在处理具有多个属性的对象时,哈希提供了一种自然的映射方式,使得对象的属性可以被轻松地存储和检索,适用于各种数据模型的存储需求。
以上3点就不过的解释了,和用户信息存储的类似.
2.list类型
1.list类型表示格式
list类型是用来存储多个有序的字符串,如图所示,a、b、c、d、e五个元素从左到右组成了⼀个有序的列表,列表中的每个字符串称为元素(element),⼀个列表最多可以存储2^32 - 1.
个元素。在Redis中,可以对列表两端插入(push)和弹出(pop)。列表是⼀种比较灵活的数据结构,它可以充当栈和队列的角色,在实际开发上有很多应用场景。
列表类型的特点:
1.列表中的元素是有序的,这意味着可以通过索引下标获取某个元素或者某个范围的元素列表同时注意list类型的下标是可以为负的,这里的-1,可以理解为 list 的长度 - 1
2.列表里元素允许有重复的元素.
3.list类型,可以灵活使用 头和尾插入或者删除元素,就可以把它当作栈或者队列
同侧存取(lpush+lpop或者rpush+rpop)为栈
异侧存取(lpush+rpop或者rpush+lpop)为队列
2.list类型常用命令
1.lpush vs lpushx
lpush:
将⼀个或者多个元素从左侧放入(头插)到list中。
语法:
lpush key element element...
时间复杂度:只插入一个元素为O(1),插入多个元素为O(N),N为插入元素个数.
返回值:插入list的长度。
lpushx
在key存在时,将⼀个或者多个元素从左侧放入(头插)到list中。不存在,直接返回
语法:
lpushx key element element...
lpushx和lpush 的语法,时间复杂度,返回值基本上一样,除入当key不存在时,lpush 会创建一个key,
lpushx就直接返回.
2.rpush vs rpushx
rpush
将⼀个或者多个元素从右侧放入(尾插)到list中。
语法:
rpush key element element...
时间复杂度:只插入一个元素为O(1),插入多个元素为O(N),N为插入元素个数.
返回值:插入list的长度。
rpushx:
在key存在时,将⼀个或者多个元素从右侧放入(尾插)到list中。不存在,直接返回.
和 lpush 和 lpushx 一样这里就不过多的赘述了.
3.lrange
获取从start到end区间的所有元素,左闭右闭.
语法:
lrange key start stop
时间复杂度:O(N)
返回值:指定区间的元素。
注意这里的 start 是列表的下标,根据redis规定,下标可以为负数.
并且注意如果下标超过列表的下标范围,reids会尽量返回有的元素
4. lpop vs rpop
lpop:
从list左侧取出元素(即头删)。
rpop:
从list右侧取出元素(即尾删)。
时间复杂度:O(1)
返回值:取出的元素或者nil。
语法:
lpop key
rpop key
5.lindex
获取对应下标的元素
语法:
lindex key index
时间复杂度: O(N) N为元素个数
返回值:取出的元素或者nil.
6.linsert
在特定位置插入元素
语法:
linsert key (before || after) pivot element
意思即为 插入到 基准值 (pivot) 前面(before) 或者是 后面 (after);
时间复杂度:O(N)
返回值:插入后的list长度。
这里需要注意的是前文提到列表的元素可以重复,如果有多个基准值的情况, linsert 插入的元素是从左到右查找基准值,找到第一个基准值就插入到该基准值的前面或者后面.
7.llen
获取list长度。
语法:
llen key
时间复杂度:O(1)
返回值:list的长度。
8. lset
修改对应目标的值
语法:
lset key index value
index 为下标
时间复杂度 :O(n)
9.lrem
用于从列表中移除一个或多个指定值的元素
语法:
lrem key count value
注意:
cout > 0则从列表的左侧开始移除与value
相等的元素,直到移除的数量达到count
为止。
count < 0从列表的右侧开始移除与value
相等的元素,直到移除的数量达到count
的绝对值为止。
如果count = 0
,则移除列表中所有与value
相等的元素。
时间复杂度:O(n);
返回值:删除成功的个数
10.ltrim
保留列表中指定范围内的元素,移除列表中不在指定范围内的所有元素
语法:
ltrim key sart end
start
: 起始索引,0表示列表的第一个元素,1表示第二个,依此类推。负数表示从列表尾部开始计数,-1表示最后一个元素,-2表示倒数第二个,以此类推.
end: 结束索引,该位置的元素不会被保留。同样地,0和正数表示从头开始计算的位置,负数表示从列表尾部开始的位置。
11.阻塞版本命令
blpop key key... timeout
brpop key key... timeout
这里的timeout是当列表为空时,redis 会要求执行该命令的客户端会表现为阻塞状态,阻塞时间就是由timeout这里设定的,同时这里的单位为秒
由于blpop 和 brpop除了一个为头删,一个为尾删,这里就以blpop为例
1.当列为不为空时
lpop vs blpop
2.当列表为空时
3.list类型内部编码
quicklist(快速列表)
quicklist
是Redis为了解决ziplist
和linkedlist
各自存在的问题而设计的一种新的数据结构。它是一种结合了ziplist
和linkedlist
优点的双向链表,具有以下特点:
-
双向链表:
quicklist
由多个ziplist
组成,每个ziplist
是双向链表中的一个节点。这种结构使得quicklist
既可以像链表一样进行高效的元素插入和删除操作,又可以像数组一样通过索引快速访问元素。 -
压缩存储:每个
ziplist
节点中存储了一系列连续的元素,这些元素在内存中紧凑存储,减少了内存分配的开销。 -
动态扩展:
quicklist
中的每个ziplist
节点可以动态地增长和收缩,当达到一定数量的元素或达到一定大小后,会分配新的ziplist
节点。 -
内存效率:
quicklist
通过ziplist
节点的紧凑存储和链表的动态扩展,实现了内存使用和操作效率的平衡。 -
适用性:
quicklist
适用于存储大量元素的列表,特别是当列表中的元素数量和大小变化较大时,quicklist
可以提供更好的性能。
通过引入quicklist
,Redis的列表类型在保持了原有操作的丰富性的同时,也提高了对大数据量列表的存储和操作效率。这种新的内部编码方式使得Redis的列表可以更加灵活地应对不同的使用场景。
总结来说,Redis列表的内部编码方式从Redis 5.0开始主要是quicklist
,它结合了ziplist
的紧凑存储和linkedlist
的动态扩展特性,提供了一种更加高效和灵活的列表存储解决方案。
4.应用场景
1.消息队列
当然使用redis的list类型作为阻塞式生产者-消费者模型队列的功能还是不够丰富,redis有专门作为
生产者-消费者模型队列的stream类型.
2.秒杀系统
在电商的秒杀场景中,List可以用来暂存用户的请求。当商品库存有限时,可以先快速地将用户的购买请求存储到一个List中,再由后台服务根据List中的顺序进行处理,这样可以避免直接对数据库的冲击,提高系统的稳定性。
3.日志记录:
利用List的先进先出特性,可以用来存储系统日志或者访问日志。例如,可以定期将用户的操作记录(如访问的URL和时间戳)PUSH
到一个List中,然后使用LRANGE
命令来查看或分析一段时间内的日志记录。
4.排行榜/最新动态:
List可以用来维护一个有序的列表,如显示最近发布的文章、评论或者实时消息更新。通过LPUSH
新内容并限制List的长度,可以自动移除最早的数据,保持最新的条目。
5.缓存列表数据:
当需要存储一系列相关但不固定数量的数据时,如用户的历史浏览记录,List可以提供高效的数据结构来支持这种需求。
6.分布式工作队列:
利用List的多客户端阻塞弹出功能,可以实现一个分布式的任务分配系统,多个工作者进程可以从同一个List中公平地领取任务执行,实现负载均衡。
以上就是关于redis hash 类型 和 list 类型 的全部总结了,感谢你的阅读.