Redis的8种数据类型,什么场景使用?
Redis属于一种基于键值对的数据存储系统,并采用C语言作为底层编程语言实现这一特性。我们使用的键字段限定为字符串数据类型,并且每个键值对的键具有唯一性;相应的值字段包括以下几种数据类型
5种常用的
- 字符串类型的标记为String
- 列表类型的标记为List
- 集合类型的标记为Set
- 有序集合类的实现基于zset
- 哈希类型的标记为Hash
2种不常用的
- bitmap位图类型
- geo地理位置类型
1种redis5.0新增的
- stream类型
因为key被定义为字符串类型,所以它是否存在一些通行做法或建议呢?或是提供一些建议?
redis key如何设计?
- 通常会通过冒号来进行分隔
- 一般情况下,默认会以表名或其缩写作为key的前缀
- 比如:在认证系统中,用户的个人资料表中的ID字段值为001时,默认对应的键名为auth:user:001
- 命名需具备一定的识别性,在查看时即可立即了解其含义
- 尽量使key简短一些,在大多数情况下简短的键通常比长键更具效率;后面我们会进行详细讲解
1、String字符串
Redis的string能存储3种值的类型:字符串,整数,浮点数
1.1、应用场景
key和value都是字符串类型的应用场景极为广泛
- 常规的赋值操作
- 所有kv存储均可表示为value类型为字符串的数据结构,在具体实现中可采用序列化方式将其转化为字符串形式或直接将非字符串类型数据转换为其对应的字符串形式;因此这种情况最为常见。
- incr可以通过加入watch监听机制来实现乐观锁机制。
incr实现的是数字递增
- setnx支持分布式锁的实现。
- 当value不存在时,在使用分布式锁的时候。是否能够完成一次成功的设置?
1.2、String字符串类型命令
命令行罗列
| 命令 | 帮助 | 描述 |
|---|---|---|
| set | set key value | 赋值 |
| get | get key | 取值 |
| getset | getset key value | 取值赋值 |
| setnx | setnx key value | 当key不存在时才赋值成功,存在时赋值失败 set key value NX PX 3000 原子操作,可用于分布式锁,px 设置毫秒数 |
| append | append key value | 向尾部追加值 |
| strlen | strlen key | 获取字符串长度 |
| incr | incr key | 递增数字 |
| incrby | incrby key increment | 增加指定的整数 |
| decr | decr key | 递减数字 |
| decrby | decrby key decrement | 减少指定的整数 |
命令行实际操作
127.0.0.1:6379> set name zhangsan
OK
127.0.0.1:6379> get name
"zhangsan"
127.0.0.1:6379> getset age 20
(nil)
127.0.0.1:6379> get age
"20"
127.0.0.1:6379> setnx lock_01 1001
(integer) 1
127.0.0.1:6379> setnx lock_01 1001
(integer) 0
127.0.0.1:6379> setnx lock_01 1002
(integer) 0
127.0.0.1:6379> append name 1
(integer) 9
127.0.0.1:6379> get name
"zhangsan1"
127.0.0.1:6379> strlen name
(integer) 9
127.0.0.1:6379> incr myid
(integer) 1
127.0.0.1:6379> incrby myid 2
(integer) 3
127.0.0.1:6379> decr myid
(integer) 2
127.0.0.1:6379> decrby myid 2
(integer) 0
# 设置分布式锁后的值,有效期20s
127.0.0.1:6379> set lock_02 1002 NX PX 30000
OK
# 30s内重新设置值失败
127.0.0.1:6379> set lock_02 1002 NX
(nil)
# 30s内获取值成功
127.0.0.1:6379> get lock_02
"1002"
# 30s后获取值为空
127.0.0.1:6379> get lock_02
(nil)
# 重新设置值成功
127.0.0.1:6379> set lock_02 1003 NX PX 30000
OK
127.0.0.1:6379> get lock_02
"1003"
127.0.0.1:6379>
代码解释
2、list列表
2.1、应用场景
list列表可以存储有序并且可以重复的元素。
- 获取头部或尾部的数据极快
- 列表最多存储2^32 -1个元素,大概40亿
通过查看以下命令我们可以明确这是一个双向列表即为一个双向列表左进右出的操作相当于依次取出元素可被视为队列结构而左进左出则相当于按顺序放入元素可被视为栈结构实际上这正是A First In First Out(FIFO)队列与A Last In First Out(LIFO)栈的基本原理
可以存储各种各样的列表数据。比如用户列表
2.2、list列表命令
命令罗列
| 命令 | 帮助 | 描述 |
|---|---|---|
| lpush | lpush key v1 v2 n3 ... vn | 从左侧插入列表 |
| lpop | lpop key | 从列表左侧取出 |
| rpush | rpush key v1 v2 n3 ... vn | 从右侧插入列表 |
| rpop | rpop key | 从列表右侧取出 |
| lpushx | lpushx key value | 将值插入到列表头部 |
| rpushx | rpushx key value | 将值插入到列表尾部 |
| blpop | blpop key timeout | 从列表左侧取出,如果为空阻塞,timeout最大阻塞时间(s) |
| brpop | brpop key timeout | 从列表右侧取出,如果为空阻塞,timeout最大阻塞时间(s) |
| llen | llen key | 获得列表中元素个数 |
| lindex | lindex key index | 获得列表中下标为index的元素 index从0开始 |
| lrange | lrange key start end | 返回列表中指定区间的元素,区间通过start和end指定 |
| lrem | lrem key count value | 删除列表中与value相等的元素 当count>0时, lrem会从列表左边开始删除;当count<0时, lrem会从列表后边开始删除;当count=0时, lrem删除所有值为value的元素 |
| lset | lset key index value | 将列表index位置的元素设置成value的值 |
| ltrim | ltrim key start end | 对列表进行修剪,只保留start到end区间 |
| rpoplpush | rpoplpush key1 key2 | 从key1列表右侧弹出并插入到key2列表左侧 |
| brpoplpush | brpoplpush key1 key2 | 从key1列表右侧弹出并插入到key2列表左侧,会阻塞 |
| linsert | linsert key BEFORE/AFTER pivot value | 将value插入到列表,且位于值pivot之前或之后 |
命令执行
127.0.0.1:6379> lpush name2 zhangsan lisi wangwu
(integer) 3
127.0.0.1:6379> lpop name2
"wangwu"
127.0.0.1:6379> rpush name3 zhangsan lisi wangwu
(integer) 3
127.0.0.1:6379> rpop name3
"wangwu"
127.0.0.1:6379> lpushx name2 niuqi
(integer) 3
127.0.0.1:6379> lrange name2 0 5
1) "niuqi"
2) "lisi"
3) "zhangsan"
127.0.0.1:6379> rpushx name3 niuqi
(integer) 3
127.0.0.1:6379> lrange name3 0 5
1) "zhangsan"
2) "lisi"
3) "niuqi"
127.0.0.1:6379> blpop name2 1
1) "name2"
2) "niuqi"
127.0.0.1:6379> brpop name3 1
1) "name3"
2) "niuqi"
127.0.0.1:6379> llen name2
(integer) 2
127.0.0.1:6379> lindex name2 0
"lisi"
127.0.0.1:6379> lindex name2 1
"zhangsan"
代码解释
3、set集合
3.1、应用场景
Set集合,就是表示value唯一,并且无需的集合。
存储各种不需要顺序的数据集合。比如用户随机随机抽奖
3.2、Set集合命令
命令罗列
| 命令 | 帮助 | 描述 |
|---|---|---|
| sadd | sadd key v1 v2 n3 ... vn | 为集合添加新成员 |
| srem | srem key mem1 mem2 ...memn | 删除集合中指定成员 |
| smembers | rsmembers key | 从获得集合中所有元素 |
| spop | spop key | 返回集合中一个随机元素,并将该元素删除 |
| srandmember | srandmember key | 将返回集合中一个随机元素,不会删除该元素 |
| scard | scard key | 获得集合中元素的数量 |
| sismember | sismember key member | 判断元素是否在集合内 |
| sinter | sinter key1 key2 key3 | 求多集合的交集 |
| sdiff | sdiffff key1 key2 key3 | 求多集合的差集 |
| sunion | sunion key1 key2 key3 | 求多集合的并集 |
命令操作
127.0.0.1:6379> sadd name4 zhangsan lisi wangwu
(integer) 3
127.0.0.1:6379> srem zhangsan
(error) ERR wrong number of arguments for 'srem' command
127.0.0.1:6379> srem name4 zhangsan
(integer) 1
127.0.0.1:6379> smembers name4
1) "lisi"
2) "wangwu"
127.0.0.1:6379> spop name4
"lisi"
127.0.0.1:6379> srandmember name4
"wangwu"
127.0.0.1:6379> scard name4
(integer) 1
127.0.0.1:6379> sismember name4 wangwu
(integer) 1
127.0.0.1:6379> sismember name4 wangwu
(integer) 1
# 定义两个集合 求交集,差集,并集
127.0.0.1:6379> sadd name5 zhangsan lisi
(integer) 2
127.0.0.1:6379> sadd name6 lisi wangwu
(integer) 2
127.0.0.1:6379> sinter name5 name6
1) "lisi"
127.0.0.1:6379> sdiff name5 name6
1) "zhangsan"
127.0.0.1:6379> sunion name5 name6
1) "lisi"
2) "wangwu"
3) "zhangsan"
代码解释
4、sortedset有序集合(zset)
4.1、应用场景
sortedset是一种顺序分明的数据结构,在其元素中与普通集合类似且保证每个元素唯一。每个元素都被赋予了一个得分值(score),这些得分值决定了排序方式。这些得分值允许存在相同的数值。
有序列表在多个排行榜业务中被广泛应用:例如销量排名、用户点击率排名等。
4.2、sortedset命令
命令罗列
| 命令 | 帮助 | 描述 |
|---|---|---|
| zadd | zadd key score1 v1 score2 v2 score1v3 ... scoren vn | 为有序集合添加新成员 |
| zrem | zrem key mem1 mem2 ...memn | 删除有序集合中指定成员 |
| zcard | zcard key | 获得有序集合中的元素数量 |
| zcount | zcount key min max | 返回集合中score值在[min,max]区间的元素数量 |
| zincrby | zincrby key increment member | 在集合的member分值上加increment |
| zscore | zscore key member | 获得集合中member的分值 |
| zrank | zrank key member | 获得集合中member的排名(按分值从小到大) |
| zrevrank | zrevrank key member | 获得集合中member的排名(按分值从大到小) |
| zrange | zrange key start end | 获得集合中指定区间成员,按分数递增排序 |
| zrevrange | zrevrange key start end | 获得集合中指定区间成员,按分数递减排序 |
命令操作
127.0.0.1:6379> zadd test:001 20 zhangsan 50 lisi 30 wangwu 90 niuqi
(integer) 4
127.0.0.1:6379> zcount test:001 0 2
(integer) 0
127.0.0.1:6379> zcount test:001 20 30
(integer) 2
127.0.0.1:6379> zcard test:001
(integer) 4
127.0.0.1:6379> zrem test:001 wangwu
(integer) 1
127.0.0.1:6379> zincrby test:001 10 zhangsan
"30"
127.0.0.1:6379> zscore test:001 zhangsan
"30"
127.0.0.1:6379> zrank test:001 zhangsan
(integer) 0
127.0.0.1:6379> zrank test:001 niuqi
(integer) 2
127.0.0.1:6379> zrevrank test:001 niuqi
(integer) 0
127.0.0.1:6379> zrevrank test:001 zhangsan
(integer) 2
127.0.0.1:6379> zrange test:001 0 5
1) "zhangsan"
2) "lisi"
3) "niuqi"
127.0.0.1:6379> zrevrange test:001 0 5
1) "niuqi"
2) "lisi"
3) "zhangsan"
代码解释
5、hash散列表
5.1、应用场景
哈希的作用是将一个键映射到多个值。例如,在实际应用中, 我们可以将用户的ID作为键(key), 而将用户的完整信息作为值(value)。这样的数据类型非常适合通过哈希表进行存储。
5.2、hash命令
命令罗列
| 命令 | 帮助 | 描述 |
|---|---|---|
| hset | hset key field value | 赋值,不区别新增或修改 |
| hmset | hmset key field1 value1 field2 value2 | 批量赋值 |
| hsetnx | hsetnx key field value | 获得有序集合中的元素数量 |
| hexists | hexists key filed | 查看某个field是否存在 |
| hget | hget key field | 获取一个字段值 |
| hmget | hmget key field1 field2 ... | 获取所有 |
| hgetall | hgetall key | 获得集合中member的排名(按分值从小到大) |
| hdel | hdel key field1 field2 | 删除指定字段 |
| hincrby | hincrby key field increment | 指定字段自增increment |
| hlen | hlen key | 获得字段数量 |
命令操作
# 其实直接使用hset多个key value也是可以的
127.0.0.1:6379> hset user:001 id 001 name zhangsan
(integer) 2
127.0.0.1:6379> hget user:001 id
"001"
127.0.0.1:6379> hget user:001 name
"zhangsan"
127.0.0.1:6379> hmset user:001 age 20 sex 1
OK
127.0.0.1:6379> hsetnx user:001 age 30
(integer) 0
127.0.0.1:6379> hexists user:001 age
(integer) 1
127.0.0.1:6379> hget user:001 age
"20"
# 使用hget多个就会报错
127.0.0.1:6379> hget user:001 age name
(error) ERR wrong number of arguments for 'hget' command
127.0.0.1:6379> hmget user:001 age name
1) "20"
2) "zhangsan"
127.0.0.1:6379> hgetall user
(empty list or set)
127.0.0.1:6379> hgetall user:001
1) "id"
2) "001"
3) "name"
4) "zhangsan"
5) "age"
6) "20"
7) "sex"
8) "1"
代码解释
6、bitmap位图
bitmap用于执行按位运算能够极大地减少所需的空间在内存中使用少量内存时适合存储大量数据仅需1bit即可存储这些数据
比如:用户每月签到,客户以用户id为key, 日期作为偏移量,1 表示签到
命令罗列
| 命令 | 帮助 | 描述 |
|---|---|---|
| setbit | setbit key offffset value | 设置key在offffset处的bit值(只能是0或者1)。 |
| getbit | getbit key offffset | 获得key在offffset处的bit值 |
| bitcount | bitcount key | 获得key的bit位为1的个数 |
| bitpos | bitpos key value | 返回第一个被设置为bit值的索引值 |
| bitop | bitop and[or/xor/not] destkey key [key …] | 对多个key 进行逻辑运算后存入destkey中 |
命令操作
# 本月用户签到了四天
127.0.0.1:6379> setbit user:sign:001 20220801 1
(integer) 0
127.0.0.1:6379> setbit user:sign:001 20220803 1
(integer) 0
127.0.0.1:6379> setbit user:sign:001 20220809 1
(integer) 0
127.0.0.1:6379> setbit user:sign:001 20220820 1
(integer) 0
# 查看20220801是否签到了 1表示签到了 0表示没有签到
127.0.0.1:6379> getbit user:sign:001 20220801
(integer) 1
127.0.0.1:6379> getbit user:sign:001 20220802
(integer) 0
# 统计用户签到的次数
127.0.0.1:6379> bitcount user:sign:001
(integer) 4
# 查看第一次签到的时间
127.0.0.1:6379> bitpos user:sign:001 1
(integer) 20220801
代码解释
7、geo地理位置类型
7.1、应用场景
geo是Redis用来处理位置信息的。在Redis3.2中正式使用。
应用场景:
-
记录地理位置
-
计算距离
-
查找"附近的人"
7.2、geo地理位置命令
命令罗列
| 命令 | 帮助 | 描述 |
|---|---|---|
| geoadd | geoadd key 经度 纬度 成员名称1 经度1 纬度1 成员名称2 | 添加地理坐标 |
| geohash | geohash key 成员名称1 成员名称2 | 返回标准的geohash串 |
| geopos | geopos key 成员名称1 成员名称2 | 返回成员经纬度 |
| geodist | geodist key 成员1 成员2 单位 | 计算成员间距离 |
| georadiusbymember | georadiusbymember key 成员 值单位 count 数asc[desc] | 根据成员查找附近的成员 |
# 添加坐标
127.0.0.1:6379> geoadd user:addr 116.21 40.00 zhangsan 116.23 49.9 lisi
(integer) 2
# 获取坐标的hash
127.0.0.1:6379> geohash user:addr zhangsan lisi
1) "wx4es3v8ck0"
2) "y8feuuh0e60"
# 获取坐标的经纬度
127.0.0.1:6379> geopos user:addr zhangsan
1) 1) "116.21000200510025024"
2) "39.99999991084916218"
# 计算两个用户的距离 单位米
127.0.0.1:6379> geodist user:addr zhangsan lisi
"1101141.4665"
# 计算两个用户的距离 单位千米
127.0.0.1:6379> geodist user:addr zhangsan lisi km
"1101.1415"
# 计算张三1200km之内的人的经纬度,距离,由近到远排出顺序
127.0.0.1:6379> georadiusbymember user:addr zhangsan 1200 km withcoord withdist count 3 asc
1) 1) "zhangsan"
2) "0.0000"
3) 1) "116.21000200510025024"
2) "39.99999991084916218"
2) 1) "lisi"
2) "1101.1415"
3) 1) "116.23000055551528931"
2) "49.89999975254306719"
代码解释
8、stream数据流类型
8.1、应用场景
stream是Redis5.0后新增的数据结构,用于可持久化的消息队列。
在应用环境中未采用第三方消息队列的方式进行设计与实现,而是采用了Redis来模拟一个类似的消息队列功能,但这一功能体系尚未成熟,因此在这种情况下需要谨慎选择.学习起来虽然有效但存在一定的风险.
8.2、stream类型命令
命令罗列
| 命令 | 帮助 | 描述 |
|---|---|---|
| xadd | xadd key id <*> field1 value1 | 将指定消息数据追加到指定队列(key)中,*表示最新生成的id(当前时间+序列号) |
| xread | xread [COUNT count] [BLOCKmilliseconds] STREAMS key[key ...] ID [ID ...] | 从消息队列中读取,COUNT:读取条数,BLOCK:阻塞读(默认不阻塞)key:队列名称 id:消息id |
| xrange | xrange key start end [COUNT] | 读取队列中给定ID范围的消息 COUNT:返回消息条数(消息id从小到大) |
| xrevrange | xrevrange key start end [COUNT] | 读取队列中给定ID范围的消息 COUNT:返回消息条数(消息id从大小到大) |
| xdel | xdel key id | 删除队列的消息 |
| xgroup | xgroup create key groupname id | 创建一个新的消费组 |
| xgroup | xgroup destory key groupname | 删除指定消费组 |
| xgroup | xgroup delconsumer key groupname cname | 删除指定消费组中的某个消费者 |
| xgroup | xgroup setid key id | 修改指定消息的最大id |
| xreadgroup | xreadgroup group groupname consumer COUNT streams key | 从队列中的消费组中创建消费者并消费数据(consumer不存在则创建) |
命令操作
# 创建消息
127.0.0.1:6379> xadd topic:001 * name zhangsan age 20
"1661008860267-0"
127.0.0.1:6379> xadd topic:001 * name wangwu age 24 name lisi age 35
"1661008909085-0"
# 查看消息
127.0.0.1:6379> xrange topic:001 - +
1) 1) "1661008860267-0"
2) 1) "name"
2) "zhangsan"
3) "age"
4) "20"
2) 1) "1661008909085-0"
2) 1) "name"
2) "wangwu"
3) "age"
4) "24"
5) "name"
6) "lisi"
7) "age"
8) "35"
# 读取一个消息
127.0.0.1:6379> xread count 1 streams topic:001 0
1) 1) "topic:001"
2) 1) 1) "1661008860267-0"
2) 1) "name"
2) "zhangsan"
3) "age"
4) "20"
# 创建一个消费组
127.0.0.1:6379> xgroup create topic:001 group1 0
OK
# 使用消费组消费数据
127.0.0.1:6379> xreadgroup group group1 user1 count 1 streams topic:001 >
1) 1) "topic:001"
2) 1) 1) "1661008860267-0"
2) 1) "name"
2) "zhangsan"
3) "age"
4) "20"
# 使用消费组消费数据 继续消费 总共两条
127.0.0.1:6379> xreadgroup group group1 user1 count 1 streams topic:001 >
1) 1) "topic:001"
2) 1) 1) "1661008909085-0"
2) 1) "name"
2) "wangwu"
3) "age"
4) "24"
5) "name"
6) "lisi"
7) "age"
8) "35"
# 消费完了,没有啦
127.0.0.1:6379> xreadgroup group group1 user1 count 1 streams topic:001 >
(nil)
代码解释
9、小结
Redis功能之所以非常强大, 也与其丰富多样的数据类型息息相关; 在不同业务场景下, 可以选择最适合的数据类型; 这样便能够更加高效地实现所需功能.
