【起】Redis 基础篇——基本数据结构之 List,Set

121 阅读7分钟

前言

距离过年那会闲在家更新的 MySQL 系列已经过去一段时间了,这段时间一直在忙其他的,所以博客的更新也就搁置了,但是一直在想着要更新啥内容比较好,刚好朋友给了我一本 Redis 的书籍,我就打算看完结合官方的文档总结一下,分享给大家,如果有什么不对的地方请指正。

Redis 系列,我想以“起承转合”的形式来更新,不过不一定是四篇噢,因为篇幅有限,太长怕你们没有耐心看完,可能《起》篇就分为几篇博文来叙述了,我也会对其进行规整,方便大家看完能更好的吸收,毕竟写文章的我能得到各位观看我的文章,是我的荣幸,我必须得对大家负责的嘛~

话归正题,Redis 应该很多人都有用过(没用过应该看这篇也能看得懂,但是一些基本理论就得自己上网百度啦)。至于 Redis 是什么,有什么好处,怎么用,那就继续往下看吧~本文会侧重于让大家对Redis 基本数据类型的操作命令,底层存储结构以及其应用场景得到一定的认知。

附上基础篇的脑图(上传平台有压缩,有兴趣可以到我的公众号【6曦轩】领取原图) 在这里插入图片描述

今天这一篇章给大家介绍一下 Redis 的基本数据类型(高阶的暂不介绍)。

篇幅有点长,我分了几篇来写,这篇是继前面两章节之后的,主要介绍 Zset 以及 Bitmap。

正文

Redis 基本数据类型

List 列表

存储类型

存储有序的字符串(从左到右),元素可以重复。可以充当队列和栈的角色。

操作命令

元素增减:

lpush queue a
lpush queue b c
rpush queue d e
lpop queue
rpop queue
blpop queue
brpop queue

取值

lindex queue 0
lrange queue 0 -1

在这里插入图片描述

存储(实现)原理

在早期的版本中,数据量较小时用 ziplist 存储,达到临界值时转换为 linkedlist 进行存储,分别对应 OBJ_ENCODING_ZIPLIST 和 OBJ_ENCODING_LINKEDLIST 。

3.2 版本之后,统一用 quicklist 来存储。 quicklist 存储了一个双向链表,每个节点都是一个 ziplist。

127.0.0.1:6379> object encoding queue
"quicklist"

quicklist

quicklist(快速列表)是 ziplist 和 linkedlist 的结合体。 quicklist.h,head 和 tail 指向双向列表的表头和表尾

typedef struct quicklist {
	quicklistNode *head; /* 指向双向列表的表头 */
	quicklistNode *tail; /* 指向双向列表的表尾 */
	unsigned long count;	/* 所有的 ziplist 中一共存了多少个元素 */
	unsigned long len;	/* 双向链表的长度,node 的数量 */
	int fill : 16;	/* fill factor for individual nodes */
	unsigned int compress : 16; /* 压缩深度,0:不压缩; */
} quicklist;

redis.conf 相关参数:

参数含义
list-max-ziplist-size(fill)正数表示单个 ziplist 最多所包含的 entry 个数。
负数代表单个 ziplist 的大小,默认 8k。
-1:4KB;-2:8KB;-3:16KB;-4:32KB;-5:64KB
list-compress-depth(compress)压缩深度,默认是 0。
1:首尾的 ziplist 不压缩;
2:首尾第一第二个 ziplist 不压缩,以此类推

quicklistNode 中的*zl 指向一个 ziplist,一个 ziplist 可以存放多个元素。

typedef struct quicklistNode {
	struct quicklistNode *prev; /* 前一个节点 */
	struct quicklistNode *next; /* 后一个节点 */
	unsigned char *zl; /* 指向实际的 ziplist */
	unsigned int sz; /* 当前 ziplist 占用多少字节 */
	unsigned int count : 16; /*	当前 ziplist 中存储了多少个元素,占 16bit(下同),最大 65536 个 */
	unsigned int encoding : 2; /* 是否采用了 LZF 压缩算法压缩节点,1:RAW 2:LZF */
	unsigned int container : 2; /* 2:ziplist,未来可能支持其他结构存储 */
	unsigned int recompress : 1; /* 当前 ziplist 是不是已经被解压出来作临时使用 */
	unsigned int attempted_compress : 1;	/* 测试用 */
	unsigned int extra : 10;	/* 预留给未来使用 */
} quicklistNode;

在这里插入图片描述 ziplist 的结构前面已经说过了,不再重复。

应用场景
  • 用户消息时间线 timeline

因为 List 是有序的,可以用来做用户时间线 在这里插入图片描述

  • 消息队列

List 提供了两个阻塞的弹出操作:BLPOP/BRPOP,可以设置超时时间。BLPOP:BLPOP key1 timeout 移出并获取列表的第一个元素, 如果列表没有元素的话会阻塞列表直到等待超时或发现可弹出元素为止。

BRPOP:BRPOP key1 timeout 移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。

队列:先进先出:rpush blpop,左头右尾,右边进入队列,左边出队列。 栈:先进后出:rpush brpop

Set 集合

存储类型

String 类型的无序集合,最大存储数量 2^32-1(40 亿左右)。 在这里插入图片描述

操作命令

添加一个或者多个元素

sadd myset a b c d e f g

获取所有元素

smembers myset

统计元素个数

scard myset

随机获取一个元素

srandmember key

随机弹出一个元素

spop myset

移除一个或者多个元素

srem myset d e f

查看元素是否存在

sismember myset a
存储(实现)原理

Redis 用 intset 或 hashtable 存储 set。 如果元素都是整数类型,就用 inset 存储。 如果不是整数类型,就用 hashtable(数组+链表的存来储结构)。

  • 问题:KV 怎么存储 set 的元素?

key 就是元素的值,value 为 null。
如果元素个数超过 512 个,也会用 hashtable 存储。

配置文件 redis.conf

set-max-intset-entries 512
127.0.0.1:6379> sadd iset 1 2 3 4 5 6
(integer) 6
127.0.0.1:6379> object encoding iset
"intset"
127.0.0.1:6379> sadd myset a b c d e f
(integer) 6
127.0.0.1:6379> object encoding myset
"hashtable"
应用场景
  • 抽奖 随机获取元素 spop myset

  • 点赞、签到、打卡 在这里插入图片描述 这条微博的 ID 是 t1001,用户 ID 是 u3001。 用 like:t1001 来维护 t1001 这条微博的所有点赞用户。 点赞了这条微博:sadd like:t1001 u3001 取消点赞:srem like:t1001 u3001 是否点赞:sismember like:t1001 u3001 点赞的所有用户:smembers like:t1001 点赞数:scard like:t1001 比关系型数据库简单许多。

  • 商品标签 用 tags:i5001 来维护商品所有的标签。 在这里插入图片描述 sadd tags:i5001 画面清晰细腻 sadd tags:i5001 真彩清晰显示屏 sadd tags:i5001 流畅至极

  • 商品筛选 获取差集 sdiff set1 set2 获取交集(intersection ) sinter set1 set2 获取并集 sunion set1 set2 在这里插入图片描述iPhone11 上市了。 sadd brand:apple iPhone11 sadd brand:ios iPhone11 sadd screensize:6.0-6.24 iPhone11 sadd screentype:lcd iPhone11 筛选商品,苹果的,iOS 的,屏幕在 6.0-6.24 之间的,屏幕材质是 LCD 屏幕 sinter brand:apple brand:ios screensize:6.0-6.24 screentype:lcd

  • 用户关注、推荐模型 留下悬念(留言中留下你的思考): 1)相互关注? 2)我关注的人也关注了他? 3)可能认识的人?

By the way

有问题?可以给我留言或私聊 有收获?那就顺手点个赞呗~

当然,也可以到我的公众号下「6曦轩」,

回复“学习”,即可领取一份 【Java工程师进阶架构师的视频教程】~

回复“面试”,可以获得: 【本人呕心沥血整理的 Java 面试题】

回复“MySQL脑图”,可以获得 【MySQL 知识点梳理高清脑图】

还有【阿里云】【腾讯云】的购买优惠噢~具体请联系我

曦轩我是科班出身的程序员,php,Android以及硬件方面都做过,不过最后还是选择专注于做 Java,所以有啥问题可以到公众号提问讨论(技术情感倾诉都可以哈哈哈),看到的话会尽快回复,希望可以跟大家共同学习进步,关于服务端架构,Java 核心知识解析,职业生涯,面试总结等文章会不定期坚持推送输出,欢迎大家关注~~~ 在这里插入图片描述