redis 数据结构 精讲

数据结构

string

string的结构
string {
int len;//实际字符串的长度
int free;//剩余可用长度
char buf[];
}

  • 有长度字段,求长度为O(1)。
  • 预分配。当len小于1M时,每次分配空间为原来的2倍。当len大于1M时,每次分配空间增加1M。预留空间较多,append效率高,分配次数减少,但占用较多内存。
  • 惰性释放空间。缩短字符串时,不立即释放因缩短而空出来的空间。
  • 二进制安全。保留原始的字符串,不会受有’\0’的影响。可以用于存储图片、音频、视频等,图片等转化为字符串可能会中间出现’\0’。
  • API安全,增长字符串时,增加了自动扩容防止缓冲区溢出。

适用

  • 计数器,例如热门文章的访问次数、分享次数、点赞次数、收藏数。

命令

  • set
  • get
  • strlen
  • exists
  • incr
  • decr
  • setex
  • expire

list

redis3.2版本之前,list底层使用ziplist或者linked list,3.2版本之后使用quicklist,quicklist底层是ziplist+linkedlist。当存储的都是小整数或短字符串时使用ziplist。
ziplist使用连续内存,是顺序存储结构,类似于数组,但每个节点的大小不一样,使用尽量小合适的编码来存储节点实际数据,以此来节省内存,但这是以时间换空间的,需要额外计算节点长度。ziplist可用于存储整数、字符串。
查询、插入、删除平均都是O(n),
插入、删除最坏情况下是O(n^2),这是因为连锁更新问题,但连锁更新的情况不多,因此不会有性能问题。

ziplist的结构

ziplist{
zlbytes #节点占用字节数
zltail #尾节点指针
zllen #节点数
entry1
entry2

zlend #ziplist结束标识,值为255
}
entry结构
entry {
previous_entry_length #上一个节点长度,用于反序遍历
encoding #根据data大小选择合适的编码,以节省内存
data #数据
}
连锁更新问题:
一个关键是previous_entry_length,当上一个节点长度小于254时,previous_entry_length使用1B。当上一个节点长度大于等于254,使用5B。
253、253、253,连续多个节点为253长度时,他们的previous_entry_length都是1B,当在前两个253、253插入一个254的节点时,第二个253要更新previous_entry_length为5B,更新后变为257, 第三个253也因此需要更新previous_entry_length为5B,这就是连锁更新。

253、254、254、254。删除第一个254, 第二个、第三个254都要连锁更新为250.

linked list

双向链表,查询为O(n)。插入、删除只需修改两个节点的前后指针为O(1)。

quicklist

一个双向链表,每个节点是ziplist,具有压缩列表特性。

适用:

  • rpush、lpop,消息队列
  • rpush、lrange,排行榜,每天定时计算。
  • rpush、lrange最新列表,如朋友圈评论列表,不适用频繁更新,不适用需要分页,更新频繁且分页可能用户会拿到重复数据。

命令

  • lpush
  • lpop
  • rpush
  • rpop
  • lrange
  • llen

hash

有两种实现,ziplist或hashtable。当键数量小于阈值且value值都小于阈值时使用ziplist,否则使用hashtable。hashtable底层是数组加链表。

适用

  • 购物车:(用户id,商品id,数量)对应(key, field, value)
  • 存储对象:存储用户信息、商品信息等。(key, field, value)对应(对象id, 字段,值)。string + json也可能存储对象,但不便于修改某个字段值,但序列化简单。hash存储对象,能方便修改某个字段值,但字段是复杂结构如一个类对象时,要转为json后存入,多个这样的字段时序列化工作就很繁琐。频繁修改的对象用hash,一般对象用string + json

命令

  • hset
  • hmset
  • hget
  • hgetall
  • hkeys
  • hvals
  • hexists
  • hlen
  • hdel

set

底层使用hashtable,类似于jdk1.8之前的HashMap,数组+链表
存储的数据无序,不重复。能方便的求两个set的并集、交集、差集。
适用

  • 数据不重复的
  • sismember、scard好友、粉丝、关注集合:将关注粉丝时,用smove将粉丝从粉丝集合移到好友集合。在集群下,sinter不适合用于求共同好友。操作多个key的如smove、sinter要求key在同一个槽中。使用sinter求共同好友,就要求大量用户的好友集合在同一个槽,这样数据就分布不均了。redis对key进行hash来确定放在那个槽,redis提供了Hash_Tag,即用{},key中出现了{},只会对花括号中的值hash,这样相同Hash_Tag的就会在同一个槽。
  • sismember黑名单、白名单
  • srandmember随机展示

命令

  • sadd
  • spop
  • scard
  • sismember
  • smembers
  • sunion
  • sinterstore

zset

有序集合,每个key都有一个score,score用于排序。
底层由压缩列表或 跳表+字典实现。
元素个数或元素最大长度超过阈值时,由ziplist压缩列表转为跳表+dict字典。使用压缩列表占用内存小,但增删效率低。字典是哈希表,用于key到分数的查询,跳表根据score范围查询。跳表是多层次的链表,也可以说是链表+索引,下一层是上一层的子集,根据score跳跃着比较,不断缩小范围,直到找到,查找效率高,增删为O(log n)。字典、跳表查询效率高,都是空间换时间的结果,需要较多的内存。

适用

  • 数据需要根据某个权重进行排序:如直播间的用户列表、礼物打赏榜。

命令

  • zadd
  • zrem
  • zcard
  • zscore
  • zrange
  • zrevrange

bitmap

1B有8位,能记录8个状态,因此能极大节省内存。
适用

  • 统计当天在线人数:用户id作为offset,在线就setbit online_key uid 1,bitcount online_key就能得到在线人数
  • 统计一段时间的活跃用户数:用bitop and|or|xor|not 多个key。
  • 统计用户行为:如用户对某篇文章点赞,setbit 文章id 用户id 1.

命令

  • setbit
  • getbit
  • bitcount
  • bitop
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值