redis五种基本数据类型

文章详细介绍了Redis中的五种基本数据类型:String、List、Hash、Set和SortedSet。对于String类型,提到了其三种内部编码(int、embstr、raw)以及SDS(SimpleDynamicString)的结构和优势。List类型通过LPUSH、RPUSH等操作展示了其列表功能。Hash类型适用于存储对象,Set提供了去重功能,而SortedSet则通过score实现成员的有序排列。文章还讨论了各种类型在内存管理和数据结构上的实现细节及应用场景。

String(字符串类型)

#加入值
redis 127.0.0.1:6379> SET www.biancheng.net "hello编程帮"
OK
# 查询 key 对应的值
redis 127.0.0.1:6379> GET www.biancheng.net
"hello编程帮"

String(字符串)是redis中最简单的数据类型。我们知道,Redis所有数据类型都是以key作为键,通过检索这个key就可以获取相应的value值。

String的内部编码有三种:

  • int : 存储可以用long类型表示的整数值
  • embstr : 存储字节长度小于等于32字节的字符串
  • raw : 存储字节长度大于32字节的字符串

raw和embstr相比:

  • 创建raw编码的字符串时需要两次内存开辟空间
  • 创建embstr编码时需要一次内存开辟空间

String的底层结构:SDS

struct sdshdr {
	// 记录buf数组已使用字节的数量
    // 等于SDS所保存字符串的长度
    int len;
    
    // 记录buf数组中未使用的字节数
    int free;
    
    // 字节数组,用于保存字符串
    char buf[];
};

redis使用标准C语言编写,但是存储字符时,Redis并未使用C语言的字符类型,而是自定义了一个属于特殊结构SDS(Simple Dynamic String),即简单动态字符串。

关于SDS和C的具体区别:

1)获取字符串长度的复杂度不同
C字符串没有记录本身的长度,如果想知道其长度,需要对整个字符串进行遍历,对遇到的每个字符进行计数,直到代表字符串结尾的空字符为止,整个操作的复杂度为O(N)。
sds在len属性中记录了自身的长度,所以获取sds长度的复杂度为O(1)
2)杜绝缓冲区溢出
C字符串由于没有记录本身的长度,因此再修改字符串时可能会由于空间不足导致缓冲区溢出,而sds不会;因为sds记录本身的长度,所以在进行修改字符串的时候会先判断容量是否足够,如果不够SDS API会自动将SDS空间修改到所需的大小;
3)减少字符串修改时带来的内存重分配次数
sds有len属性和free属性分别记录本身的长度和剩余可用空间,在增长操作时会先判断剩余可用空间是否足够,如果足够就不用内存重分配了;如果不够,进行内存分配时,也会多分配额外的空间。具体分配策略如下:
第一点:修改后字符串的大小小于1MB,字符串的大小变为多少,程序分配的未使用空间就和字符串的大小一样
第二点:修改后字符串大小大于1MB,程序分配的未使用空间为1MB,程序分配的未使用空间就为1MB。

字符串类型常用的场景有以下这些:

(1)缓存结构体信息:(缓存JSON格式的用户信息)
将结构体json序列化成字符串,然后将字符串保存在redis的value中,将结构体的业务唯一标示作为key;这种保存json的用法用的最多的场景就是缓存用户信息,将用户bean信息转成json再序列化为字符串作为value保存在redis中,将用户id作为key。从代码中获取用户缓存信息就是一个逆过程,根据userid作为key获取到结构体json,然后将json转成java bean。
(2)计数功能:
我们都知道redis是单线程模式,并且redis将很多常用的事务操作进行了封装,这里我们最常用的就是数值自增或自减,redis的作者封装了incr可以进行自增,每调用一次自增1,因为redis是单线程运行,所以就算client是多线程调用那么也是正确自增,因为incr命令中将read和write做了事务封装。同样可以设置incr的step,每次根据step进行自增,当然如果要达到自减的效果,那么只要将step设置为负数就可以了。

List(列表类型)

#从左侧头部插入元素
127.0.0.1:6379> LPUSH www.biancheng.net python
(integer) 1
127.0.0.1:6379> LPUSH www.biancheng.net java c c#
(integer) 4
#查看插入的元素
127.0.0.1:6379> LRANGE www.biancheng.net 0 4
1) "c#"
2) "c"
3) "java"
4) "python"
#从右侧尾部插入元素
127.0.0.1:6379> RPUSH www.biancheng.net "Math"
(integer) 5
127.0.0.1:6379> RPUSH www.biancheng.net "Computer Language"
(integer) 6
#查看元素
127.0.0.1:6379> LRANGE www.biancheng.net 0 6
1) "c#"
2) "c"
3) "java"
4) "python"
5) "Math"
6) "Computer Language"
#在python前面插入元素
127.0.0.1:6379> LINSERT www.biancheng.net BEFORE python Python
(integer) 7
#从左侧弹出元素
127.0.0.1:6379> LPOP www.biancheng.net
"c#"
#从右侧弹出元素
127.0.0.1:6379> RPOP www.biancheng.net
"Computer Language"

底层结构

list的底层结构由双向链表和压缩链表组成。(列表对象的编码可以是ziplist或者linkedlist)
列表对象使用ziplist编码的条件:
(1)列表对象保存的所有字符串元素的长度都小于64字节。
(2)列表对象保存的元素数量小于512个
如果不能满足这两个条件对象需要使用linkedList

使用场景

(1)list列表结构常用来做异步队列使用
将需要延后处理的任务结构体序列化成字符串塞进 Redis 的列表,另一个线程从这个列表中轮询数据进行处理。
(2)list可用于秒杀抢购场景
在商品秒杀场景最怕的就是商品超卖,为了解决超卖问题,我们经常会将库存商品缓存到类似MQ的队列中,多线程的购买请求都是从队列中取,取完了就卖完了,但是用MQ处理的化有点重,这里就可以使用redis的list数据类型来实现,在秒杀前将本场秒杀的商品放到list中,因为list的pop操作是原子性的,所以即使有多个用户同时请求,也是依次pop,list空了pop抛出异常就代表商品卖完了。

Hash

哈希对象的编码可以是ziplist(压缩列表)或者hashtable(字典)。
当存储的数据量较少时,hash采用ziplist作为底层存储结构,此时要求符合以下两个条件:
● 哈希对象保存的所有键值对(键和值)的字符串长度总和小于64个字节。
● 哈希对象保存的键值对数量要求小于512个。
当无法满足上述条件时,hash就会采用hashtable(字典)。

Redis hash(哈希散列)是由字符类型的field(字段)和value组成的哈希映射表结构(也称为散列表),在hash类型中,field与value一一对应,且不允许重复。
Redis hash特别适合于存储对象。一个filed/value可以看做是表格中一条数据记录;而一个key可以对应多条数据。

#以user为key,设置 id+序号为字段,name+名字为值
127.0.0.1:6379> HMSET user id:1 name:Cao id:2 name:Zhao
OK
# 查询 user 这个key下所有的数据,并以字符串的形式将值返回
127.0.0.1:6379> HGETALL user
1) "id:1"
2) "name:Cao"
3) "id:2"
4) "name:Zhao"

set

集合有一个非常重要的特性就是“自动去重”,这使得它可以适用于许多场景,比如过滤掉已中奖用户的 id,保证该用户不会被第二次抽中。

#创建集合并添加多个成员
127.0.0.1:6379> SADD user:1 python java mysql
(integer) 3
127.0.0.1:6379> SADD user:2 python c redis
(integer) 3
#对两个集合求交集,求出他们共同关注的编程技术
127.0.0.1:6379> SINTER user:1 user:2
1) "python"
#两个集合求并集
127.0.0.1:6379> SUNION user:1 user:2
1) "java"
2) "python"
3) "mysql"
4) "redis"
5) "c"
#查看集合所有成员
127.0.0.1:6379> SMEMBERS user:1
1) "mysql"
2) "java"
3) "python"
#两个集合求并集,并把结果保存到另外一个user:3集合中
127.0.0.1:6379> SUNIONSTORE user:3 user:1 user:2
(integer) 5
#查看集合所有成员
127.0.0.1:6379> SMEMBERS user:3
1) "java"
2) "python"
3) "mysql"
4) "redis"
5) "c"
#从集合中弹出一个元素
127.0.0.1:6379> SPOP user:1 1
1) "python"
#从集合中弹出两个元素
127.0.0.1:6379> SPOP user:1 2
1) "mysql"
2) "java"
#查看集合元素个数
127.0.0.1:6379> SCARD user:2
(integer) 3
#迭代集合中元素
127.0.0.1:6379> SSCAN user:3 0
1) "0"
2) 1) "mysql"
   2) "redis"
   3) "java"
   4) "python"
   5) "c"

Redis set(集合)遵循无序排列的规则,集合中的每一个成员(也就是元素)都是字符串类型,并且不可重复。
在这里插入图片描述

set(集合)底层使用整数集合和字典
当集合对象可以同时满足以下两个条件时,对象使用insert(整数集合)编码:
● 集合对象保存的所有元素都是整数值
● 集合对象保存的元素数量不超过512个
不能满足这两个条件的集合对象需要使用hashtable(字典)编码。

有序集合

Redis zset(有序集合)中的成员是有序排列的,它和set集合的相同之处在于,集合中的每一个成员都是字符串类型,并且不允许重复;而它们最大区别是,有序集合是有序的,set是无序的。这是因为有序集合中每个成员都会关联一个double(双精度浮点数)类型的score(分数值),Redis正是通过score实现了对集合成员的排序。
zset是Redis常用数据类型之一,它适用于排行榜类型的业务场景,比如QQ音乐排行榜、用户贡献榜。在音乐排行榜中,我们可以将歌曲的点击次数作为score值,把歌曲的名字作为value值,通过对score排序就可以得出歌曲“热度榜单”。

有序集合同样使用了两种不同的存储结构,分别是zipList(压缩列表)和skipList(跳跃列表),当zset满足以下条件时使用压缩列表:
● 成员的数量小于128个;
● 每个member(成员)的字符串长度都小于64个字节
当有序集合不满足使用压缩列表的条件时,就会使用skipList(压缩列表)结构来存储数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值