Redis 数据类型和应用场景
Redis 是一个 Key-Value 的存储系统,使用 ANSI C 语言编写。
Key 的类型是字符串。
Value 的数据类型常用的:string 字符串类型、list 列表类型、set 集合类型、sortedset(zset)有序集合类型、hash 类型。不常见的:bitmap 位图类型、geo 地理位置类型。Redis5.0 新增一种:stream 类型
注意:Redis 中命令是忽略大小写,(set SET),value 是不忽略大小写的 (NAME name)
Redis 的 Key 的设计
- 用:分割
- 把表名转换为 key 前缀, 比如: user:
- 第二段放置主键值
- 第三段放置列名
比如:用户表 user, 转换为 redis的 key-value 存储
userid | username | password | |
9 | zhangf | 111111 |
username 的 key:user:9:username
email 的 key user:9:email
表示明确:看key知道意思不易被覆盖
String 字符串类型
Redis 的 String 能表达3种值的类型:字符串、整数、浮点数,100.01 是个六位的串
常见操作命令如下表:
命令名称 |
| 命令描述 |
set | set key value | 赋值 |
get | get key | 取值 |
getset | getset key value | 取值并赋值 |
setnx | setnx key value | 当value不存在时采用赋值 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 | 减少指定的整数 |
应用场景:
1. key 和命令是字符串
2. 普通的赋值
3. incr 用于乐观锁,可用于实现乐观锁 watch (事务)
4. setnx 用于分布式锁,当 value 不存在时采用赋值,可用于实现分布式锁
setnx:
127.0.0.1:6379> setnx name zhangf
(integer) 1 # 如果 name 不存在赋值
127.0.0.1:6379> setnx name zhaoyun
(integer) 0 # 再次赋值失败
127.0.0.1:6379> get name
"zhangf"
set
127.0.0.1:6379> set age 18 NX PX 10000 # 如果不存在则赋值,有效期10秒
OK
127.0.0.1:6379> set age 20 NX # 已经存在,赋值失败
get age
(nil)
127.0.0.1:6379> set age 30 NX PX 10000 # 赋值成功
OK
127.0.0.1:6379> get age
"30"
list 列表类型
list 列表类型可以存储有序、可重复的元素,获取头部或尾部附近的记录是极快的,list 的元素个数最多为2^32-1个(40亿),常见操作命令如下表:
命令名称 | 命令格式 | 描述 |
lpush | lpush key v1 v2 v3 ... | 从左侧插入列表 |
lpop | lpop key | 从列表左侧取出 |
rpush | rpush key v1 v2 v3 ... | 从右侧插入列表 |
rpop | rpop key | 从列表右侧取出 |
lpushx | lpushx key value | 将值插入到列表头部 |
rpushx | rpushx key value | 将值插入到列表尾部 |
blpop | blpop key timeout | 从列表左侧取出,当列表为空时阻塞,可以设置最大阻塞时间,单位为秒 |
brpop | blpop key timeout | 从列表右侧取出,当列表为空时阻塞,可以设置最大阻塞时间,单位为秒 |
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之前或之后 |
应用场景:
1. 作为栈或队列使用,列表有序可以作为栈和队列使用
2. 可用于各种列表,比如用户列表、商品列表,评论列表等。
127.0.0.1:6379> lpush list:1 1 2 3 4 5 3
(integer) 5
127.0.0.1:6379> lrange list:1 0 -1
1) "5"
2) "4"
3) "3"
4) "2"
5) "1"
127.0.0.1:6379> lpop list:1 # 从0开始
"5"
127.0.0.1:6379> rpop list:1
"1"
127.0.0.1:6379> lindex list:1 1
"3"
127.0.0.1:6379> lrange list:1 0 -1
1) "4"
2) "3"
3) "2"
127.0.0.1:6379> lindex list:1 1
"3"
127.0.0.1:6379> rpoplpush list:1 list:2 "2"
127.0.0.1:6379> lrange list:2 0 -1
1) "2"
127.0.0.1:6379> lrange list:1 0 -1
1) "4"
2) "3"
set 集合类型
Set:无序、元素唯一,集合中最大的成员数为 2^32 - 1,常见操作命令如下表:
命令名称 | 命令格式 | 描述 |
sadd | sadd key mem1 mem2 .... | 为集合添加新成员 |
srem | srem key mem1 mem2 .... | 删除集合中指定成员 |
smembers | smembers key | 获得集合中所有元素 |
spop | spop key | 返回集合中一个随机元素,并将该元素删除 |
srandmember | srandmember key | 返回集合中一个随机元素,不会删除该元素 |
scard | scard key | 获得集合中元素的数量 |
sismember | sismember key member | 判断元素是否在集合内 |
sinter | sinter key1 key2 key3 | 求多集合的交集 |
sdiff | sdiff key1 key2 key3 | 求多集合的差集 |
sunion | sunion key1 key2 key3 | 求多集合的并集 |
应用场景:
适用于不能重复的且不需要顺序的数据结构,比如:关注的用户,还可以通过 spop 进行随机抽奖,举例:
127.0.0.1:6379> sadd set:1 a b c d
(integer) 4
127.0.0.1:6379> smembers set:1
1) "d"
2) "b"
3) "a"
4) "c"
127.0.0.1:6379> srandmember set:1
"c"
127.0.0.1:6379> srandmember set:1
"b"
127.0.0.1:6379> sadd set:2 b c r f
(integer) 4
127.0.0.1:6379> sinter set:1 set:2
1) "b"
2) "c"
127.0.0.1:6379> spop set:1
"d"
127.0.0.1:6379> smembers set:1
1) "b"
2) "a"
3) "c"
sortedset 有序集合类型
SortedSet(ZSet) 有序集合: 元素本身是无序不重复的,每个元素关联一个分数(score) 可按分数排序,分数可重复,常见操作命令如下表:
命令名称 | 命令格式 | 描述 |
zadd | zadd key score1 member1 score2 member2 ... | 为有序集合添加新成员 |
zrem | zrem key mem1 mem2 .... | 删除有序集合中指定成员 |
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 hit:1 100 item1 20 item2 45 item3
(integer) 3
127.0.0.1:6379> zcard hit:1
(integer) 3
127.0.0.1:6379> zscore hit:1 item3
"45"
127.0.0.1:6379> zrevrange hit:1 0 -1
1)"item1"
2)"item3"
3)"item2"
127.0.0.1:6379>
hash类型(散列表)
Redis hash 是一个 string 类型的 field 和 value 的映射表,它提供了字段和字段值的映射。每个 hash 可以存储 2^32 - 1 键值对(40多亿)。
常见操作命令如下表:
命令名称 | 命令格式 | 描述 |
hset | hset key field value | 赋值,不区别新增或修改 |
hmset | hmset key field1 value1 field2 value2 | 批量赋值 |
hsetnx | hsetnx key field value | 赋值,如果filed存在则不操作 |
hexists | hexists key filed | 查看某个field是否存在 |
hget | hget key field | 获取一个字段值 |
hmget | hmget key field1 field2 ... | 获取多个字段值 |
hgetall | hgetall key |
|
hdel | hdel key field1 field2... | 删除指定字段 |
hincrby | hincrby key field increment | 指定字段自增increment |
hlen | hlen key | 获得字段数量 |
应用场景:
对象的存储 ,表数据的映射举例:
127.0.0.1:6379> hmset user:001 username zhangfei password 111 age 23 sex M
OK
127.0.0.1:6379> hgetall user:001
1)"username"
2)"zhangfei"
3)"password"
4)"111"
5)"age"
6)"23"
7)"sex"
8) "M"
127.0.0.1:6379> hget user:001 username
"zhangfei"
127.0.0.1:6379> hincrby user:001 age 1
(integer) 24
127.0.0.1:6379> hlen user:001
(integer) 4
bitmap 位图类型
bitmap 是进行位操作的,通过一个 bit 位来表示某个元素对应的值或者状态,其中的 key 就是对应元素本身。
bitmap 本身会极大的节省储存空间。常见操作命令如下表:
命令名称 | 命令格式 | 描述 |
setbit | setbit key offset value | 设置key在offset处的bit值(只能是0或者1)。 |
getbit | getbit key offset | 获得key在offset处的bit值 |
bitcount | bitcount key | 获得key的bit位为1的个数 |
bitpos | bitpos key value | 返回第一个被设置为bit值的索引值 |
bitop | bitop and[or/xor/not] destkey key [key …] | 对多个key 进行逻辑运算后存入destkey 中 |
应用场景:
1、用户每月签到,用户id为key ,日期作为偏移量,1表示签到
2、统计活跃用户, 日期为key,用户id为偏移量,1表示活跃
3、查询用户在线状态, 日期为key,用户id为偏移量,1表示在线
127.0.0.1:6379> setbit user:sign:1000 20200101 1 # id为1000的用户20200101签到
(integer) 0
127.0.0.1:6379> setbit user:sign:1000 20200103 1 # id为1000的用户20200103签到
(integer) 0
127.0.0.1:6379> getbit user:sign:1000 20200101 # 获得id为1000的用户20200101签到状态 1 表示签到
(integer) 1
127.0.0.1:6379> getbit user:sign:1000 20200102 # 获得id为1000的用户20200102签到状态 0 表示未签到
(integer) 0
127.0.0.1:6379> bitcount user:sign:1000 # 获得id为1000的用户签到次数
(integer) 2
127.0.0.1:6379> bitpos user:sign:1000 1 # id为1000的用户第一次签到的日期
(integer) 20200101
127.0.0.1:6379> setbit 20200201 1000 1 # 20200201的1000号用户上线
(integer) 0
127.0.0.1:6379> setbit 20200202 1001 1 # 20200202的1000号用户上线
(integer) 0
127.0.0.1:6379> setbit 20200201 1002 1 # 20200201的1002号用户上线
(integer) 0
127.0.0.1:6379> bitcount 20200201 # 20200201的上线用户有2个
(integer) 2
127.0.0.1:6379> bitop or desk1 20200201 20200202 # 合并20200201的用户和20200202上线了的用户
(integer) 126
127.0.0.1:6379> bitcount desk1 # 统计20200201和20200202都上线的用户个数
(integer) 3
geo地理位置类型
geo 是 Redis 用来处理位置信息的。在 Redis3.2 中正式使用。主要是利用了Z阶曲线、Base32 编码和 geohash 算法
Z阶曲线
在x轴和y轴上将十进制数转化为二进制数,采用x轴和y轴对应的二进制数依次交叉后得到一个六位数编码。把数字从小到大依次连起来的曲线称为Z阶曲线,Z阶曲线是把多维转换成一维的一种方法。
Base32 编码
Base32这种数据编码机制,主要用来把二进制数据编码成可见的字符串,其编码规则是:任意给定一个二进制数据,以5个位(bit)为一组进行切分(base64以6个位(bit)为一组),对切分而成的每个组进行编码得到1个可见字符。Base32编码表字符集中的字符总数为32个(0-9、b-z去掉a、i、l、o),这也是Base32名字的由来。
geohash 算法
Gustavo在2008年2月上线了geohash.org网站。Geohash是一种地理位置信息编码方法。经过geohash映射后,地球上任意位置的经纬度坐标可以表示成一个较短的字符串。可以方便的存储在数据库中,附在邮件上,以及方便的使用在其他服务中。
以北京的坐标举例,[39.928167,116.389550]可以转换成wx4g0s8q3jf9 。
Redis中经纬度使用52位的整数进行编码,放进zset中,zset的value元素是key,score是GeoHash的52位整数值。在使用Redis进行Geo查询时,其内部对应的操作其实只是zset(skiplist)的操作。通过zset 的score进行排序就可以得到坐标附近的其它元素,通过将score还原成坐标值就可以得到元素的原始坐标。常见操作命令如下表:
命令名称 | 命令格式 | 描述 |
geoadd | geoadd key 经度 纬度 成员名称1 经度1 纬度1 成员名称2 经度2 纬度 2 ... | 添加地理坐标 |
geohash | geohash key 成员名称1 成员名称2... | 返回标准的geohash串 |
geopos | geopos key 成员名称1 成员名称2... | 返回成员经纬度 |
geodist | geodist key 成员1 成员2 单位 | 计算成员间距离 |
georadiusbymember | georadiusbymember key 成员 值单位 count 数 asc[desc] | 根据成员查找附近的成员 |
应用场景:
1. 记录地理位置
2. 计算距离
3. 查找"附近的人"
127.0.0.1:6379> geoadd user:addr 116.31 40.05 zhangf 116.38 39.88 zhaoyun 116.47 40.00 diaochan #添加用户地址 zhangf、zhaoyun、diaochan 的经纬度
(integer) 3
127.0.0.1:6379> geohash user:addr zhangf diaochan #获得 zhangf 和 diaochan 的 geohash 码
1) "wx4eydyk5m0"
2) "wx4gd3fbgs0"
127.0.0.1:6379> geopos user:addr zhaoyun #获得 zhaoyun 的经纬度
1) 1) "116.38000041246414185"
2) "39.88000114172373145"
127.0.0.1:6379> geodist user:addr zhangf diaochan #计算 zhangf 到 diaochan 的距离,单位是m
"14718.6972"
127.0.0.1:6379> geodist user:addr zhangf diaochan km #计算 zhangf 到 diaochan 的距离,单位是km
"14.7187"
127.0.0.1:6379> geodist user:addr zhangf zhaoyun km
"19.8276"
127.0.0.1:6379> georadiusbymember user:addr zhangf 20 km withcoord withdist count 3 asc
# 获得距离 zhangf 20km 以内的按由近到远的顺序排出前三名的成员名称、距离及经纬度 #withcoord:获得经纬度 withdist:获得距离 withhash:获得 geohash 码
1) 1) "zhangf"
2) "0.0000"
3) 1) "116.31000012159347534"
2) "40.04999982043828055"
2) 1) "diaochan"
2) "14.7187"
3) 1) "116.46999925374984741"
2) "39.99999991084916218"
3) 1) "zhaoyun"
2) "19.8276"
3) 1) "116.38000041246414185"
2) "39.88000114172373145"
stream 数据流类型
stream 是 Redis5.0 后新增的数据结构,用于可持久化的消息队列。几乎满足了消息队列具备的全部内容,包括:
- 消息ID的序列化生成
- 消息遍历
- 消息的阻塞和非阻塞读取
- 消息的分组消费
- 未完成消息的处理
- 消息队列监控
每个 Stream 都有唯一的名称,它就是 Redis 的 key,首次使用 xadd 指令追加消息时自动创建。常见操作命令如下表:
命令名称 | 命令格式 | 描述 |
xadd | xadd key id <*> field1 value1.... | 将指定消息数据追加到指定队列(key)中,* 表示最新生成的id(当前时间+序列号) |
xread | xread [COUNT count] [BLOCK milliseconds] 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:6379> xadd topic:001 * name zhangfei age 23
"1591151905088-0"
127.0.0.1:6379:6379> xadd topic:001 * name zhaoyun age 24 name diaochan age 16
"1591151912113-0"
127.0.0.1:6379:6379> xrange topic:001 - +
1) 1) "1591151905088-0"
2) 1) "name"
2) "zhangfei"
3) "age"
4) "23"
2) 1) "1591151912113-0"
2) 1) "name"
2) "zhaoyun"
3) "age"
4) "24"
5) "name"
6) "diaochan"
7) "age"
8) "16"
127.0.0.1:6379:6379> xread COUNT 1 streams topic:001 0
1) 1) "topic:001"
2) 1) 1) "1591151905088-0"
2) 1) "name"
2) "zhangfei"
3) "age"
4) "23"
#创建的group1
127.0.0.1:6379:6379> xgroup create topic:001 group1 0 OK
# 创建cus1加入到group1 消费 没有被消费过的消息 消费第一条
127.0.0.1:6379:6379> xreadgroup group group1 cus1 count 1 streams topic:001 >
1) 1) "topic:001"
2) 1) 1) "1591151905088-0"
2) 1) "name"
2) "zhangfei"
3) "age"
4) "23"
#继续消费 第二条
127.0.0.1:6379:6379> xreadgroup group group1 cus1 count 1 streams topic:001 >
1) 1) "topic:001"
2) 1) 1) "1591151912113-0"
2) 1) "name"
2) "zhaoyun"
3) "age"
4) "24"
5) "name"
6) "diaochan"
7) "age"
8) "16"
#没有可消费
127.0.0.1:6379> xreadgroup group group1 cus1 count 1 streams topic:001 >
(nil)
底层数据结构
RedisDB 结构
typedef struct redisDb {
int id; //id是数据库序号,为0-15(默认Redis有16个数据库)
long avg_ttl; //存储的数据库对象的平均ttl(time to live),用于统计
dict *dict; //存储数据库所有的key-value
dict *expires; //存储key的过期时间
dict *blocking_keys;//blpop 存储阻塞key和客户端对象
dict *ready_keys;//阻塞后push 响应阻塞客户端 存储阻塞后push的key和客户端对象
dict *watched_keys;//存储watch监控的的key和客户端对象
} redisDb;
id
dict
expires
RedisObject 结构
结构信息概览
typedef struct redisObject {
unsigned type:4; // 类型五种对象类型
unsigned encoding:4; // 编码
void *ptr; // 指向底层实现数据结构的指针
//...
int refcount; // 引用计数
//...
unsigned lru:LRU_BITS; // LRU_BITS 为 24bit 记录最后一次被命令程序访问的时间
//...
}robj;
4位 type
127.0.0.1:6379> type a1
string
4位 encoding
127.0.0.1:6379> object encoding a1
"int"
24位 LRU
refcount
ptr
7种 type
字符串对象
struct sdshdr{
//记录buf数组中已使用字节的数量
int len;
//记录 buf 数组中未使用字节的数量
int free;
//字符数组,用于保存字符串
char buf[];
}
SDS 的优势:
使用场景:
跳跃表(重点)
查找
插入与删除
删除
跳跃表特点:
- 每层都是一个有序链表
- 查找次数近似于层数(1/2)
- 底层包含所有元素
- 空间复杂度 O(n) 扩充了一倍
Redis 跳跃表的实现
//跳跃表节点
typedef struct zskiplistNode {
/* 存储字符串类型数据 redis3.0 版本中使用robj类型表示,但是在 redis4.0.1 中直接使用 sds 类型表示 */
sds ele;
double score;//存储排序的分值
struct zskiplistNode *backward;//后退指针,指向当前节点最底层的前一个节点
/* 层,柔性数组,随机生成1-64的值 */
struct zskiplistLevel {
struct zskiplistNode *forward; //指向本层下一个节点
unsigned int span;//本层下个节点到本节点的元素个数
} level[];
} zskiplistNode;
//链表
typedef struct zskiplist{
//表头节点和表尾节点
structz skiplistNode *header, *tail; //表中节点的数量
unsigned long length; //表中层数最大的节点的层数
int level;
}zskiplist;
跳跃表的优势:
应用场景:
字典(重点+难点)
数组
Hash 函数
Hash 冲突
Redis 字典的实现
Hash 表
typedef struct dictht {
dictEntry **table; // 哈希表数组
unsigned long size; // 哈希表数组的大小
unsigned long sizemask; // 用于映射位置的掩码,值永远等于(size-1)
unsigned long used; // 哈希表已有节点的数量,包含next单链表数据
} dictht;
Hash表节点
typedef struct dictEntry {
void *key; // 键
union { // 值v的类型可以是以下4种类型
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next; // 指向下一个哈希表节点,形成单向链表 解决 hash 冲突
} dictEntry
dict 字典
typedef struct dict {
dictType *type; // 该字典对应的特定操作函数
void *privdata; // 上述类型函数对应的可选参数
dictht ht[2]; // 两张哈希表,存储键值对数据,ht[0]为原生哈希表,ht[1]为 rehash 哈希表
long rehashidx;
/*
* rehash 标识当等于-1时表示没有在 rehash,
* 否则表示正在进行rehash操作,存储的值表示 hash 表
* ht[0]的 rehash 进行到哪个索引值 (数组下标)
*/
int iterators; // 当前运行的迭代器数量
} dict;
typedef struct dictType {
// 计算哈希值的函数
unsigned int (*hashFunction)(const void *key);
// 复制键的函数
void *(*keyDup)(void *privdata, const void *key);
// 复制值的函数
void *(*valDup)(void *privdata, const void *obj);
// 比较键的函数
int (*keyCompare)(void *privdata, const void *key1, const void *key2);
// 销毁键的函数
void (*keyDestructor)(void *privdata, void *key);
// 销毁值的函数
void (*valDestructor)(void *privdata, void *obj);
} dictType;
完整的 Redis 字典数据结构:
字典扩容
压缩列表
struct ziplist<T>{
unsigned int zlbytes; // ziplist的长度字节数,包含头部、所有entry和zipend。
unsigned int zloffset; // 从ziplist的头指针到指向最后一个entry的偏移量,用于快速反 向查询
unsigned short int zllength; // entry元素个数
T[] entry; // 元素值
unsigned char zlend; // ziplist结束符,值固定为0xFF
}
typedef struct zlentry {
unsigned int prevrawlensize; //previous_entry_length字段的长度
unsigned int prevrawlen; //previous_entry_length字段存储的内容
unsigned int lensize; //encoding字段的长度
unsigned int len; //数据内容长度
unsigned int headersize; //当前元素的首部长度,即 previous_entry_length 字段长度与 encoding 字段长度之和。
unsigned char encoding; //数据类型 unsigned char *p; //当前元素首地址
} zlentry;
应用场景:
整数集合
127.0.0.1:6379> sadd set:001 1 3 5 6 2
(integer) 5 127.0.0.1:6379> object encoding set:001
"intset"
127.0.0.1:6379> sadd set:004 1 100000000000000000000000000 9999999999
(integer) 3
127.0.0.1:6379> object encoding set:004
"hashtable"
typedef struct intset{
// 编码方式
uint32_t encoding;
// 集合包含的元素数量
uint32_t length;
// 保存元素的数组
int8_t contents[];
}intset;
应用场景:
双向列表(adlist)
双向链表优势:
快速列表(重要)
127.0.0.1:6379> lpush list:001 1 2 5 4 3
(integer) 5
127.0.0.1:6379> object encoding list:001
"quicklist"
quicklist 是一个双向链表,链表中的每个节点时一个ziplist结构。quicklist中的每个节点ziplist都能够存储多个数据元素。
quicklist 的结构定义如下:
typedef struct quicklist {
quicklistNode *head; // 指向quicklist的头部
quicklistNode *tail; // 指向quicklist的尾部
unsigned long count; // 列表中所有数据项的个数总和
unsigned int len; // quicklist节点的个数,即ziplist的个数
int fill : 16; // ziplist大小限定,由list-max-ziplist-size给定 (Redis设定)
unsigned int compress : 16; // 节点压缩深度设置,由list-compress-depth给定 (Redis设定)
} quicklist;
quicklistNode 的结构定义如下:
typedef struct quicklistNode {
struct quicklistNode *prev; // 指向上一个ziplist节点
struct quicklistNode *next; // 指向下一个ziplist节点
unsigned char *zl; // 数据指针,如果没有被压缩,就指向ziplist结构,反之指向 quicklistLZF 结构
unsigned int sz; // 表示指向ziplist结构的总长度(内存占用长度)
unsigned int count : 16; // 表示ziplist中的数据项个数
unsigned int encoding : 2; // 编码方式,1--ziplist,2--quicklistLZF
unsigned int container : 2; // 预留字段,存放数据的方式,1--NONE,2--ziplist
unsigned int recompress : 1; // 解压标记,当查看一个被压缩的数据时,需要暂时解压,标 记此参数为 1,之后再重新进行压缩
unsigned int attempted_compress : 1; // 测试相关
unsigned int extra : 10; // 扩展字段,暂时没用
} quicklistNode;
数据压缩
typedef struct quicklistLZF {
unsigned int sz; // LZF压缩后占用的字节数
char compressed[]; // 柔性数组,指向数据部分
} quicklistLZF;
应用场景
流对象
listpack
Rax树
应用场景:
10种encoding
127.0.0.1:6379> sadd set:001 1 3 5 6 2
(integer) 5
27.0.0.1:6379> object encoding set:001
"intset"
127.0.0.1:6379> sadd set:004 1 100000000000000000000000000 9999999999
(integer) 3
127.0.0.1:6379> object encoding set:004
"hashtable"
String
int
127.0.0.1:6379> set n1 123
OK
127.0.0.1:6379> object encoding n1
"int"
embstr
127.0.0.1:6379> set name:001 zhangfei
OK
127.0.0.1:6379> object encoding name:001
"embstr"
raw
127.0.0.1:6379> set address:001 asdasdasdasdasdasdsadasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdas dasdasdas
OK
127.0.0.1:6379> object encoding address:001
"raw"
list
127.0.0.1:6379> lpush list:001 1 2 5 4 3
(integer) 5
127.0.0.1:6379> object encoding list:001
"quicklist"
hash
dict
127.0.0.1:6379> hmset user:003 username111111111111111111111111111111111111111111111111111111111111111111111111 11111111111111111111111111111111 zhangfei password 111 num 2300000000000000000000000000000000000000000000000000
OK
127.0.0.1:6379> object encoding user:003
"hashtable"
ziplist
127.0.0.1:6379> hmset user:001 username zhangfei password 111 age 23 sex M
OK
127.0.0.1:6379> object encoding user:001
"ziplist"
set
127.0.0.1:6379> zadd hit:2 100 item1111111111111111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111 20 item2 45 item3
(integer) 3
127.0.0.1:6379> object encoding hit:2
"skiplist"
intset
127.0.0.1:6379> sadd set:001 1 3 5 6 2
(integer) 5
127.0.0.1:6379> object encoding set:001
"intset"
dict
127.0.0.1:6379> sadd set:004 1 100000000000000000000000000 9999999999
(integer) 3
127.0.0.1:6379> object encoding set:004
"hashtable"
zset
ziplist
127.0.0.1:6379> zadd hit:1 100 item1 20 item2 45 item3
(integer) 3
127.0.0.1:6379> object encoding hit:1
"ziplist"
skiplist + dict
127.0.0.1:6379> zadd hit:2 100 item1111111111111111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111 20 item2 45 item3
(integer) 3
127.0.0.1:6379> object encoding hit:2
"skiplist"