Redis 数据结构
一、Redis简介
Redis是一款基于key-value的高性能NoSQL数据库,开源免费,遵守BSD协议。支持string(字符串) 、 hash(哈希) 、list(列表) 、 set(集合) 、 zset(有序集合)等数据结构,除此之外还提供了键过期、发布订阅、Lua脚本、事务、流水线(Pipeline)、持久化和主从复制等功能,并通过 Redis 哨兵(Sentinel)和 Redis Cluster(集群)自动分区提供了高可用性。可用于数据库、缓存和消息队列等多种场景。
二、数据结构
Redis 是一个key-value数据库,即数据存储是以一个唯一的 key 对应一个 value,value 有 5 种基础数据结构: string (字符串)、 hash (哈希) 、 list (列表)、 set (集合)和 zset (有序集合)。
1、string(字符串)
string 的值可以是字符串(简单的、以及json、xml格式的)、数字,甚至是二进制数据(图片、音频、视频), 最大能存储512MB。Redis 的字符串是动态的,是可以修改的,当字符串长度小于 1M 时,扩容都是加倍现有的空间,如果超过 1M,扩容时一次只会多扩 1M 的空间。
相关命令
(1)设置值与获取值
set key value 设置指定 key 的值
get key 获取指定 key 的值
批量设置与获取
mset key value [key value ...] 同时设置一个或多个 key-value 对。
msetlen key value [key value ...] 同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。
mget key1 [key2..] 获取所有(一个或多个)给定 key 的值。
从 Redis 2.6.12 开始 set 命令支持一些选项修改其行为:
·ex seconds:设置 key 秒级过期时间。
·px milliseconds:设置 key 毫秒级过期时间。
·nx:只有 key 不存在时,才可以设置成功。
·xx:只有 key 存在时,才可以设置成功。
用法:set key value [ex seconds] [px milliseconds] [nx|xx]
同时redis也有相关命令实现以上行为:
setex key seconds value 将值 value 关联到 key ,并将 key 的过期时间设为 seconds (以秒为单位)。
psetex key milliseconds value 这个命令和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像 SETEX 命令那样,以秒为单位。
setnx key value 只有在 key 不存在时设置 key 的值。
由于以上三个命令可以使用 set 命令选项替代,所以可能会被废弃,并最终被删除。
(2)自增自减
incr key 将 key 中储存的数字值增一。
decr key 将 key 中储存的数字值减一。
incrby key increment 将 key 所储存的值加上给定的增量值(increment)。
decrby key decrement key 所储存的值减去给定的减量值(decrement) 。
如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行操作。
如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。
以上操作的值仅限于 64 位有符号整数。
incrbyfloat key increment 将 key 所储存的值加上给定的浮点增量值(increment) 。
(3)其他命令
strlen key 返回 key 所储存的字符串值的长度(实际是字节长度)。
getset key value 将给定 key 的值设为 value ,并返回 key 的旧值(old value)。
append key value 如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾。
setrange key offset value 用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始。
getrange key start end 返回 key 中字符串值的子字符。
内部编码
int:8个字节的长整型。
embstr:小于等于44个字节的字符串。
raw:大于44个字节的字符串。
应用场景
缓存(例如可以将用户信息序列化成json字符串存储)、锁(set nx、setnx)、计数器(incr、incrby、decr、decrby)等
2、hash (哈希)
hash 的值本身又是一个键值对结构,如下图所示,这样的结构特别适合用于存储对象。
相关命令
(1)设置与获取
hset key field value 将哈希表 key 中的字段 field 的值设为 value 。
hget key field 获取存储在哈希表中指定字段的值
hmset key field1 value1 [field2 value2 ] 同时将多个 field-value (域-值)对设置到哈希表 key 中。
hmget key field1 [field2] 获取所有给定字段的值
hgetall key 获取在哈希表中指定 key 的所有字段和值
hsetnx key field value 只有在字段 field 不存在时,设置哈希表字段的值。
使用hgetall时,如果哈希元素个数比较多,会存在阻塞Redis的可能。这时可以使用hscan命令,该命令会渐进式遍历哈希结构
hscan key cursor [MATCH pattern] [COUNT count] 迭代哈希表中的键值对。
如果要获取hash 表中所以的字段或值,可以使用如下命令:
hkeys key 获取所有哈希表中的字段
hvals key 获取哈希表中所有值
(2)自增
hincrby key field increment 为哈希表 key 中的指定字段的整数值加上增量 increment 。
hincrbyfloat key field increment 为哈希表 key 中的指定字段的浮点数值加上增量 increment 。
(3)其他命令
hlen key 获取哈希表中字段的数量
hexists key field 查看哈希表 key 中,指定的字段是否存在。
hdel key field2 [field2] 删除一个或多个哈希表字段
当 hash 移除了最后一个元素之后,该数据结构自动被删除,内存被回收。
内部编码
·ziplist(压缩列表):一块连续的内存空间,元素之间没有任何冗余空隙,比较节省内存。
当哈希类型元素个数小于hash-max-ziplist-entries配置(默认512个),且所有值都小于hash-max-ziplist-value配置(默认64字节)时使用。
·hashtable(哈希表):相对ziplist占用了更多的内存,但它的读写时间复杂度为O(1)。
应用场景
hash 结构也可以用来存储用户信息,不同于 string 结构每次都需要序列化和反序列化(存在一定开销),hash 可以对每个字段进行单独操作,但是 hash 结构使用 hashtable 编码时会消耗更多内存,请根据实际情况取舍。
3、list (列表)
list 列表是简单的字符串列表,按照插入顺序排序,每一个字符串就是一个元素,元素可以重复。你可以从两端对列表进行插入和弹出操作,还可以根据索引获取元素或者指定范围内的元素。存储结构如下图所示
相关命令
(1)添加元素
lpush key value1 [value2] 从列表左边添加一个或多个元素
rpush key value1 [value2] 从列表右边添加一个或多个元素
lpushx key value 从列表左边添加一个或多个元素,列表不存在时操作无效
rpushx key value 从列表右边添加一个或多个元素,列表不存在时操作无效
linsert key before|after pivot value 在列表中指定元素前或者后插入元素
(2)修改元素
lset key index value 通过索引设置列表元素的值
(3)查找元素
lindex key index 通过索引获取列表中的元素
lrange key start stop 获取列表指定范围内的元素
llen key 获取列表长度
(4)删除元素
lpop key 移出并获取列表左边第一个元素
rpop key 移出并获取列表右边第一个元素
blpop key1 [key2 ] timeout 移出并获取列表左边第一个元素, 如果列表没有元素会阻塞列表直到等待超时(s)或发现可弹出元素为止。
brpop key1 [key2 ] timeout 移出并获取列表右边第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
rpoplpush source destination 移除列表的右边第一个元素,并将该元素添加到另一个列表的左边并返回
brpoplpush source destination timeout 移除列表的右边第一个元素,并将该元素添加到另一个列表的左边并返回; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。
lrem key count value 根据参数 count 的值,移除列表中与参数 value 相等的元素
ltrim key start stop 对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。
补充:
blpop和brpop是lpop和rpop的阻塞版本,如果列表为空时,它会等待timeout后返回,如果timeout=0,客户端会被一直阻塞直到有新的元素添加进来。列表不为空时客户端会立即返回。
lrem命令根据count的不同有三种情况:
·count>0,从左到右,删除count个 value 元素。
·count<0,从右到左,删除count绝对值个 value 元素。
·count=0,删除所有与 value 相同的元素。
当列表弹出了最后一个元素之后,该数据结构自动被删除,内存被回收。
内部编码
·ziplist(压缩列表):当列表的元素个数小于list-max-ziplist-entries配置(默认512个) , 同时列表中每个元素的值都小于list-max-ziplist-value配置时(默认64字节) 时使用该编码。
·linkedlist(链表):普通的双向链表,带有prev和next指针,占用了更多的内存,获取某个节点的前置节点和后置节点的复杂度都是O(1)。
·quicklist(快速列表):Redis 3.2 版本提供,结合了 ziplist 和 linkedlist 两者的优势,之后 list 都采用该编码。
应用场景
普通队列(lpush+rpop)、消息队列(lpush+brpop)、分页(lrange)等。
4、set (集合)
set 是一种无序的集合,并且不允许有重复元素。支持多个集合取交集、并集、差集。结构如下
相关命令
(1)添加
sadd key member1 [member2] 向集合添加一个或多个元素,返回添加成功的个数
(2)删除
spop key 删除并返回集合中的一个随机元素,Redis从3.2版本开始支持[count]参数
srem key member1 [member2] 删除集合中一个或多个元素
smove source destination member 将 member 元素从 source 集合移动到 destination 集合
当集合中最后一个元素删除之后,数据结构自动删除,内存被回收。
(3)获取、查找
scard key 获取集合的元素个数
smembers key 返回集合中的所有元素
srandmember key [count] 返回集合中一个或多个随机数
sismember key member 判断 member 元素是否是集合 key 的元素
sscan key cursor [MATCH pattern] [COUNT count] 迭代集合中的元素
(4)差集、并集、交集
sdiff key1 [key2] 返回给定所有集合的差集
sunion key1 [key2] 返回所有给定集合的并集
sinter key1 [key2] 返回给定所有集合的交集
sdiffstore destination key1 [key2] 返回给定所有集合的差集并存储在 destination 中sinter
sunionstore destination key1 [key2] 所有给定集合的并集存储在 destination 集合中
sinterstore destination key1 [key2] 返回给定所有集合的交集并存储在 destination 中
内部编码
·intset(整数集合): 一个整数集合, 占用内存较少,只能存储整数类型的数据。当集合中的元素都是整数且元素个数小于set-maxintset-entries配置(默认512个) 时使用。
·hashtable(哈希表):当 intset 编码无法满足时使用。
应用场景
打标签、投票、抽奖、社交(例如共同的好友、微博共同的关注)等。
5、zset (有序集合)
zset 和 set 不同的是每个元素都会关联一个double类型的分数(score)。redis正是通过分数来为集合中的元素进行排序。有序集合的元素同样不允许重复,但分数(score)却可以重复。结构如下
相关命令
(1)添加、自增
zadd key score1 member1 [score2 member2] 向有序集合添加一个或多个元素,或者更新已存在元素的分数。被成功添加的新元素的数量,不包括那些被更新的、已经存在的元素。
从 Redis 3.0.2开始,zadd命令可以添加如下选项:
·nx: 元素不存在时才能设置成功。
·xx: 元素存在时才能设置成功。
·ch: 返回发生变化元素的数量(包括新添加的和被更新的元素)
·incr: 对 score 做增加, 等同于 zincrby 命令。
zincrby key increment member 有序集合中对指定元素的分数加上增量 increment
从 Redis 2.4 版本开始, zadd 命令每次可以添加多个元素
(2)统计数量
zcard key 获取有序集合的元素数量
zcount key min max 计算在有序集合中指定区间分数的元素数量
zlexcount key min max 在有序集合中计算指定字典区间内元素数量
zlexcount命令说明:
元素名称前需要加 [ 符号作为开头, [ 符号与元素之间不能有空格
可以使用 - 和 + 表示得分最小值和最大值
min 和 max 不能反, max 放前面 min放后面会导致返回结果为0
计算数量时,参数 min 和 max 的位置也计算在内。
(3)查找
zscore key member 返回有序集中指定元素的分数值
zrangebylex key min max [LIMIT offset count] 通过字典区间返回有序集合的元素
zrank key member 返回有序集合中指定元素的索引
zrevrank key member 返回有序集合(按分数从高到低排序时)中指定元素的索引
zrange key start stop [WITHSCORES] 通过索引区间返回有序集合成指定区间内的元素
zrevrange key start stop [WITHSCORES] 通过索引区间返回有序集合成指定区间内的元素,分数从高到底
zrangebyscore key min max [WITHSCORES] [LIMIT offset count] 通过分数返回有序集合指定区间内的元素
zrevrangebyscore key max min [WITHSCORES] [LIMIT offset count] 通过分数返回有序集合指定区间内的元素,分数从高到低排序
参数说明:
默认情况下,min和max区间的取值使用闭区间(小于等于或大于等于),你也可以通过给参数前增加“(”符号来使用可选的开区间(小于或大于)。min和max也可以是-inf和+inf,代表无限小和无限大。
[LIMIT offset count]:offset代表查找的起始位置,count代表返回的元素个数
zscan key cursor [MATCH pattern] [COUNT count] 迭代有序集合中的元素(包括元素成员和元素分值)
首次迭代 设置 cursor 为0,再次迭代时以上次返回的游标为参数
(4)删除
zrem key member [member ...] 删除有序集合中的一个或多个元素
zremrangebylex key min max 删除有序集合中给定的字典区间的所有元素
zremrangebyrank key start stop 删除有序集合中给定的索引区间的所有元素
zremrangebyscore key min max 删除有序集合中给定的分数区间的所有元素
zset 中最后一个元素被删除后,数据结构自动删除,内存被回收。
(5)交集
zunionstore destination numkeys key [key ...] 计算给定的一个或多个有序集的并集,并存储在新的 key 中
zinsterstore destination numkeys key [key ...] 计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中
内部编码
·ziplist(压缩列表):当有序集合的元素个数小于zset-max-ziplistentries配置(默认128个) , 同时每个元素的值都小于zset-max-ziplist-value配置(默认64字节) 时。
·skiplist(跳跃表): 当 ziplist 编码无法满足时使用。
应用场景
排行榜、时间轴、商品推荐等。
其他数据结构
Redis 除了上面五种基础结构外,还有Bitmaps,HyperLogLog、GEO、Stream四种数据结构。
6、Bitmaps
Bitmaps 实际上不是一种数据结构,它就是字符串,但是它可以对字符串的位进行操作。可以把Bitmaps 当成一个位数组来看待,以字母a为例,它的ASCII码是98,对应的二进制是01100001,Bitmaps 的结构如下:
相关命令
setbit key offset value 对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)
gitbit key offset 对 key 所储存的字符串值,获取指定偏移量上的位(bit)。
bitcount key [start][end] 计算指定范围内,被设置为 1 的比特位的数量。
bitpos key bit [start] [end] 返回位图中第一个值为 bit(0或1) 的二进制位的位置。
bitop operation destkey key [key …] 对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上
operation 可以是 and、 or、 not、 xor这四种操作中的任意一种:
bitop and destkey key [key ...] ,对一个或多个 key 求逻辑并,并将结果保存到 destkey 。
bitop or destkey key [key ...] ,对一个或多个 key 求逻辑或,并将结果保存到 destkey 。
bitop xor destkey key [key ...] ,对一个或多个 key 求逻辑异或,并将结果保存到 destkey 。
bitop not destkey key ,对给定 key 求逻辑非,并将结果保存到 destkey 。
除了 not 操作之外,其他操作都可以接受一个或多个 key 作为输入。
bitfield key [get type offset] [set type offset value] [incrby type offset increment] [overflow wrap|sat|fail]
该命令详情请查看:
https://redis.io/commands/bitfield
或http://www.redis.cn/commands/bitfield.html
应用场景
通过数据结构我们发现 bitmaps 适用于存储 bool 行为的数据,比如统计用户一年的登录天数,使用 setbit 命令记录登录(比如用户a第一天登录了 setbit a 0 1),使用 bitcount 命令统计数量(bitcount a 0 364)。时间越久用户越多,节省存储空间的效果就越明显。
7、HyperLogLog
HyperLogLog 是 Redis 2.8.9 添加的数据结构,它是一种用来做基数统计的算法,可以使用固定且很少的内存(每个 HyperLogLog 结构需要12K字节再加上key本身的几个字节)来存储集合的唯一元素。但是它统计的结果并不是一个精确值,而是一个带有 0.81% 标准错误(standard error)的近似值。HyperLogLog 具有去重功能。
相关命令
pfadd key element [element ...] 添加指定元素到 HyperLogLog 中。
pfcount key [key ...] 返回给定 HyperLogLog 的基数估算值。
pfmerge destkey sourcekey [sourcekey ...] 将多个 HyperLogLog 合并为一个 HyperLogLog
题外话:命令的开头 pf 是 HyperLogLog 数据结构发明人 Philippe Flajolet 的首字母缩写。
应用场景
统计某个网站或某个页面的UV(只统计总数,不需要绝对准确,也不用知道是谁访问的)。
8、GEO
Redis 3.2 提供了GEO(地理信息定位) 功能,它的底层实现是zset。
相关命令
geoadd key longitude latitude member [longitude latitude member ...] 将指定的地理空间位置(纬度、经度、名称)添加到指定的key中
有效的经度介于 -180 度至 180 度之间。
有效的纬度介于 -85.05112878 度至 85.05112878 度之间。
geopos key member [member ...] 从key里返回所有给定位置元素的位置(经度和纬度)
geodist key member1 member2 [unit] 返回两个给定位置之间的距离。
参数unit:
m 表示单位为米(默认)。
km 表示单位为千米。
mi 表示单位为英里。
ft 表示单位为英尺。
georadius key longitude latitude radius m|km|ft|mi [withcoord] [withdist]
[withhash] [COUNT count] [asc|desc] [store key] [storedist key]
以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。
可选参数:
ithcoord: 返回结果中包含经纬度。
withdist: 返回结果中包含离中心节点位置的距离。
withhash: 返回结果中包含geohash, 有关geohash后面介绍。
COUNT count: 指定返回结果的数量。
asc|desc: 返回结果按照离中心节点的距离做升序或者降序。
store key: 将返回结果的地理位置信息保存到指定键。
storedist key: 将返回结果离中心节点的距离保存到指定键。
georadiusbymember key member radiusm|km|ft|mi [withcoord] [withdist]
[withhash] [COUNT count] [asc|desc] [store key] [storedist key]
同上,只不过他是以指定的成员为中心。
geohash key member [member …] 返回一个或多个位置元素的 geohash 表示。
9、Stream
Stream 是 Redis 5.0 添加的数据结构,它是一个新的强大的支持多播的可持久化的消息队列,借鉴了 Kafka 的设计。我们前面说到 List 结构,它也可以做消息队列,但只支持点对点模式。Redis 有一个单独模块 PubSub(发布订阅) 支持消息多播,但是它不能持久化。Stream 不仅可以持久化,还支持消息分组、ACK(应答)等功能。由于本人没有使用过该数据结构,更多内容请参考:https://redis.io/topics/streams-intro或http://www.redis.cn/topics/streams-intro.html
到这里 Redis 的数据结构就介绍完了,更多使用请参考