返回
Featured image of post Redis - 常用数据类型

Redis - 常用数据类型

Redis基于C语言实现了自己的数据类型,主要包括五种:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合).

以下示例代码部分源自 [小专栏] 剖析面试最常见问题之Redis .

type name		#查看当前 key 的类型

String

string 数据结构是简单的 key-value 类型。虽然 Redis 是用 C 语言写的,但是 Redis 并没有使用 C 的字符串表示,而是自己构建了一种 简单动态字符串(simple dynamic string,SDS)。相比于 C 的原生字符串,Redis 的 SDS 不光可以保存文本数据还可以保存二进制数据,并且获取字符串长度复杂度为 O(1)(C 字符串为 O(N)),除此之外,Redis 的 SDS API 是安全的,不会造成缓冲区溢出。

  • 常用命令: set,get,strlen,exists,dect,incr,setex 等等
  • 应用场景 :大多数需要缓存的场景、需要计数的场景,比如用户的访问次数、热点文章的点赞转发数量等等
127.0.0.1:6379> set key value #设置 key-value 类型的值
OK
127.0.0.1:6379> get key # 根据 key 获得对应的 value
"value"
127.0.0.1:6379> exists key  # 判断某个 key 是否存在
(integer) 1
127.0.0.1:6379> strlen key # 返回 key 所储存的字符串值的长度。
(integer) 5
127.0.0.1:6379> del key # 删除某个 key 对应的值
(integer) 1
127.0.0.1:6379> get key
(nil)

作计数器用 (字符串的内容为整数的时候可以使用)

127.0.0.1:6379> set number 1
OK
127.0.0.1:6379> incr number # 将 key 中储存的数字值增一
(integer) 2
127.0.0.1:6379> get number
"2"
127.0.0.1:6379> decr number # 将 key 中储存的数字值减一
(integer) 1
127.0.0.1:6379> get number
"1"

设置过期(通用)

127.0.0.1:6379> expire key  60 # 数据在 60s 后过期
(integer) 1
127.0.0.1:6379> setex key 60 value # 数据在 60s 后过期 (setex:[set] + [ex]pire)
OK
127.0.0.1:6379> ttl key # 查看数据还有多久过期
(integer) 56

List 链表

当列表中存储数据量较大,列表通过双向循环链表实现。可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。

  • 常用命令: rpush,lpop,lpush,rpop,lrange、llen 等。
  • 应用场景: 发布与订阅或者说消息队列、慢查询。

实现队列

127.0.0.1:6379> rpush myList value1 # 向 list 的头部(右边)添加元素
(integer) 1
127.0.0.1:6379> rpush myList value2 value3 # 向list的头部(最右边)添加多个元素
(integer) 3
127.0.0.1:6379> lpop myList # 将 list的尾部(最左边)元素取出
"value1"
127.0.0.1:6379> lrange myList 0 1 # 查看对应下标的list列表, 0 为 start,1为 end
1) "value2"
2) "value3"
127.0.0.1:6379> lrange myList 0 -1 # 查看列表中的所有元素,-1表示倒数第一
1) "value2"
2) "value3"

实现栈

127.0.0.1:6379> rpush myList2 value1 value2 value3
(integer) 3
127.0.0.1:6379> rpop myList2 # 将 list的头部(最右边)元素取出
"value3"

通过 lrange 查看对应下标范围的列表元素:

127.0.0.1:6379> rpush myList value1 value2 value3
(integer) 3
127.0.0.1:6379> lrange myList 0 1 # 查看对应下标的list列表, 0 为 start,1为 end
1) "value1"
2) "value2"
127.0.0.1:6379> lrange myList 0 -1 # 查看列表中的所有元素,-1表示倒数第一
1) "value1"
2) "value2"
3) "value3"

通过 lrange 命令,你可以基于 list 实现分页查询,性能非常高!

通过 llen 查看链表长度:

127.0.0.1:6379> llen myList
(integer) 3

ZipList 压缩列表

压缩列表本质是字节数组,可以包含任意多个元素,每个元素可以是字节数组或整数。

编码

  1. zlbytes:字节长度,占4字节,因此ZipList最长 $2^{32} - 1$ 字节
  2. zltail:为元素相对起始地址的偏移量,占4字节
  3. zllen:列表元素数目,占2字节,当数目超过 $2^{16}-1$ 时字段无效,只能通过遍历获取数目
  4. entryX:若干元素
  5. zlend:结尾字节,0xFF

Hash 无序散列表

存储键值对。当数据量较小,使用ZipList存储,否则使用散列表(使用MurmurHash2作为哈希函数)。

当负载因子大于1,触发扩容,将散列表扩大为2倍。当负载因子小于0.1,触发缩容,缩小为实际负载的2倍大小。

  • 常用命令: hset,hmset,hexists,hget,hgetall,hkeys,hvals 等。
  • 应用场景: 系统中对象数据的存储。
127.0.0.1:6379> hset userInfoKey name "guide" description "dev" age "24"
OK
127.0.0.1:6379> hexists userInfoKey name # 查看 key 对应的 value中指定的字段是否存在。
(integer) 1
127.0.0.1:6379> hget userInfoKey name # 获取存储在哈希表中指定字段的值。
"guide"
127.0.0.1:6379> hget userInfoKey age
"24"
127.0.0.1:6379> hgetall userInfoKey # 获取在哈希表中指定 key 的所有字段和值
1) "name"
2) "guide"
3) "description"
4) "dev"
5) "age"
6) "24"
127.0.0.1:6379> hkeys userInfoKey # 获取 key 列表
1) "name"
2) "description"
3) "age"
127.0.0.1:6379> hvals userInfoKey # 获取 value 列表
1) "guide"
2) "dev"
3) "24"
127.0.0.1:6379> hset userInfoKey name "GuideGeGe" # 修改某个字段对应的值
127.0.0.1:6379> hget userInfoKey name
"GuideGeGe"

Set 无序集合

集合中的元素没有先后顺序,不允许重复数据。

  • 常用命令: sadd,spop,smembers,sismember,scard,sinterstore,sunion 等。
  • 应用场景: 需要存放的数据不能重复以及需要获取多个数据源交集和并集等场景
127.0.0.1:6379> sadd mySet value1 value2 # 添加元素进去
(integer) 2
127.0.0.1:6379> sadd mySet value1 # 不允许有重复元素
(integer) 0
127.0.0.1:6379> smembers mySet # 查看 set 中所有的元素
1) "value1"
2) "value2"
127.0.0.1:6379> scard mySet # 查看 set 的长度
(integer) 2
127.0.0.1:6379> sismember mySet value1 # 检查某个元素是否存在set 中,只能接收单个元素
(integer) 1
127.0.0.1:6379> sadd mySet2 value2 value3
(integer) 2
127.0.0.1:6379> sinterstore mySet3 mySet mySet2 # 获取 mySet 和 mySet2 的交集并存放在 mySet3 中
(integer) 1
127.0.0.1:6379> smembers mySet3
1) "value2"
127.0.0.1:6379> sunion mySet mySet2 # 获取 mySet 和 mySet2 的并集并打印
1) "value1"
2) "value2"
3) "value3"

通过以下的案例可知Set是无序的

127.0.0.1:6379> sadd myset 1 2 3 4 5 6
(integer) 6
127.0.0.1:6379> spop myset 3
1) "4"
2) "5"
3) "1"

SortedSet 有序集合

存储键值对,有序集合的值被称为分值(score),必须为浮点数。SortedSet 是唯一既可以跟据成员访问,又可以跟据分值以及分值排列顺序访问元素的结构。有点像是 Java 中 HashMap 和 TreeSet 的结合体。

  • 常用命令: zadd,zcard,zscore,zrange,zrevrange,zrem 等。
  • 应用场景: 需要对数据根据某个权重进行排序的场景。比如在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息。
127.0.0.1:6379> zadd myZset 3.0 value1 # 添加元素到 sorted set 中 3.0 为权重
(integer) 1
127.0.0.1:6379> zadd myZset 2.0 value2 1.0 value3 # 一次添加多个元素
(integer) 2
127.0.0.1:6379> zcard myZset # 查看 sorted set 中的元素数量
(integer) 3
127.0.0.1:6379> zscore myZset value1 # 查看某个 value 的权重
"3"
127.0.0.1:6379> zrange  myZset 0 -1 # 顺序输出某个范围区间的元素,0 -1 表示输出所有元素
1) "value3"
2) "value2"
3) "value1"
127.0.0.1:6379> zrange  myZset 0 1 # 顺序输出某个范围区间的元素,0 为 start  1 为 stop
1) "value3"
2) "value2"
127.0.0.1:6379> zrevrange  myZset 0 1 # 逆序输出某个范围区间的元素,0 为 start  1 为 stop
1) "value1"
2) "value2"

bitmap

适用只需要一个 bit 位来表示某个元素对应值或者状态的情况,如是否签到、是否登录等 Java 中使用 bool 的场景,加之 bitmap 可以统计设为 1 的位的数量

常用命令: setbitgetbitbitcountbitop

# SETBIT 会返回之前位的值(默认是 0)这里会生成 7 个位
127.0.0.1:6379> setbit mykey 7 1
(integer) 0
127.0.0.1:6379> setbit mykey 7 0
(integer) 1
127.0.0.1:6379> getbit mykey 7
(integer) 0
127.0.0.1:6379> setbit mykey 6 1
(integer) 0
127.0.0.1:6379> setbit mykey 8 1
(integer) 0
# 通过 bitcount 统计被被设置为 1 的位的数量。
127.0.0.1:6379> bitcount mykey
(integer) 2

用户 ID 经常可以被用来作 bitmap 上的 offset,从而可以轻松统计“…的用户个数”。

相应的,位操作 BITOP operation destkey key [key ...] ,支持 AND OR NOT XOR 四种操作中任意一种参数。

初始化数据:

127.0.0.1:6379> setbit 20210308 1 1
(integer) 0
127.0.0.1:6379> setbit 20210308 2 1
(integer) 0
127.0.0.1:6379> setbit 20210309 1 1
(integer) 0

统计 20210308~20210309 总活跃用户数: 1

127.0.0.1:6379> bitop and desk1 20210308 20210309
(integer) 1
127.0.0.1:6379> bitcount desk1
(integer) 1

统计 20210308~20210309 在线活跃用户数: 2

127.0.0.1:6379> bitop or desk2 20210308 20210309
(integer) 1
127.0.0.1:6379> bitcount desk2
(integer) 2

实际上,如果需要统计日活、月活用户这种,Redis 有一个非常对口的数据结构——HyperLoglog,原理其实类似于布隆滤波器,也是一个哈希过滤器,有一定的误警率,因此一般用于统计日活用户数量之类对精确度没有很高要求的数据。


乐观锁

对应数据库中的 version 设计

watch money		#使用 watch 当作乐观锁操作

被监视数据,如果在事务对其执行操作前被其它线程修改,则在调用 exec 时会执行失败

unwatch		# 失败,乐观锁已经失效

watch money		# 添加新的乐观锁

链接

你可以在《Redis设计与实现》的下述页码找到对应数据结构的常用命令:

  • string———–P68
  • list—————P71
  • hash————P74
  • set—————P77
  • sorted set—–P81
Licensed under CC BY-NC-SA 4.0
comments powered by Disqus