文章目录

  • Redis基础知识
  • Redis 概念
  • Redis 应用场景
  • Redis 数据结构
  • String(字符串)
  • 存储结构
  • 应用场景
  • 缓存对象
  • 常规计数
  • 分布式锁
  • List(列表)
  • 存储结构
  • 补充:ziplist(压缩列表)
  • 应用场景
  • 消息队列
  • Hash(哈希)
  • 存储结构
  • 补充:hashtable 编码
  • 应用场景
  • 缓存对象
  • 购物车数据
  • Set(集合)
  • 存储结构
  • 应用场景
  • 点赞
  • 共同关注
  • 抽奖活动
  • Sorted Set(有序集合)
  • 存储结构
  • 举例说明
  • 补:跳表查询
  • 应用场景
  • 排行榜
  • 电话、姓名排序
  • BitMap
  • HyperLogLog
  • Stream
  • Redis 数据结构相关命令操作
  • 数据结构注意点
  • set与zset区别
  • Redis中的watch命令
  • 说说Redis中List结构的相关操作


Redis基础知识

Redis 概念

Redis是一种基于键值对的NoSQL数据库,而键值对的值是由多种数据结构和算法组成的。Redis的数据都存储于内存中,因此它的速度惊人,读写性能可达10万/秒,远超关系型数据库。

关系型数据库是基于二维数据表来存储数据的,它的数据格式更为严谨,并支持关系查询。关系型数据库的数据存储于磁盘上,可以存放海量的数据,但性能远不如Redis。

Redis 应用场景

  1. Redis最常用来做缓存,是实现分布式缓存的首先中间件;
  2. Redis可以作为数据库,实现诸如点赞、关注、排行等对性能要求极高的互联网需求;
  3. Redis可以作为计算工具,能用很小的代价,统计诸如PV/UV、用户在线天数等数据;
  4. Redis还有很多其他的使用场景,例如:可以实现分布式锁,可以作为消息队列使用

Redis 数据结构

  1. Redis支持5种核心的数据类型,分别是字符串、哈希、列表、集合、有序集合;
  2. 另外Redis还提供了Bitmap、HyperLogLog、Geo类型,但这些类型都是基于上述核心数据类型实现的;
  3. Redis在5.0新增加了Streams数据类型,它是一个功能强大的、支持多播的、可持久化的消息队列

注意:这里的数据类型实际描述的是 value 的类型,key 都是 string,常见数据类型(value)有如下类型

  1. string(embstr、raw、int)
  2. list(quicklist,由多个 ziplist 双向链表组成)
  3. hash(ziplist,键值比较少、hashtable键值超过某些阈值或者单个键值比较大)
  4. set(intset、hashtable)
  5. sorted set(ziplist、skiplist)
  6. bitmap
  7. hyperloglog

每一种类型都用 redisObject 结构体来表示,每种类型根据情况不同,有不同的编码 encoding(即每种结构体的底层数据结构)

下面来详细介绍一下:

String(字符串)

String 是最基本的 key-value 结构,key 是唯一标识,value 是具体的值,value其实不仅是字符串, 也可以是数字(整数或浮点数),value 最多可以容纳的数据长度是 512M

存储结构

如果字符串保存的是整数值,则底层编码为 int,实际使用 long (8字节)来存储

如果字符串保存的是非整数值(浮点数字或其它字符)使用的是 SDS(简单动态字符串)来存储,又分两种情况

  1. 长度 <= 39 字节,使用 embstr 编码来保存,即将 redisObject 和 sdshdr 结构体保存在一起,分配内存只需一次
  2. 长度 > 39 字节,使用 raw 编码来保存,即 redisObject 结构体分配一次内存,sdshdr 结构体分配一次内存,用指针相连

sdshdr(SDS )称为简单动态字符串,实现上有点类似于 java 中的 StringBuilder,Redis是用C语言实现的,sdshdr 是底层代码定义的一个结构体,它有如下特性

  1. 可以单独存储字符长度,相比 char* 获取长度效率高(char* 是 C 语言原生字符串表示)
  2. 支持动态扩容,方便字符串拼接操作
  3. 预留空间,减少内存分配、释放次数(< 1M 时容量是字符串实际长度 2 倍,>= 1M 时容量是原有容量 + 1M
  4. 二进制安全,例如传统 char*\0 作为结束字符,这样就不能保存视频、图片等二进制数据,而 sds 以长度来进行读取

应用场景

缓存对象

使用 String 来缓存对象有两种方式:

  • 直接缓存整个对象的 JSON,命令例子:
SET user:1 '{"name":"xiaolin", "age":18}'
  • 采用将 key 进行分离为 user:ID:属性,采用 MSET 存储,用 MGET 获取各属性值,命令例子:
MSET user:1:name laoniu user:1:age 18 user:2:name xiaoqian user:2:age 20
常规计数

因为 Redis 处理命令是单线程,所以执行命令的过程是原子的。因此 String 数据类型适合计数场景,比如计算访问次数、点赞、转发、库存数量等等。

比如计算文章的阅读量:

# 初始化文章的阅读量
> SET aritcle:readcount:1001 0
OK
#阅读量+1
> INCR aritcle:readcount:1001
(integer) 1
#阅读量+1
> INCR aritcle:readcount:1001
(integer) 2
#阅读量+1
> INCR aritcle:readcount:1001
(integer) 3
# 获取对应文章的阅读量
> GET aritcle:readcount:1001
"3"
分布式锁

分布式锁,即分布式系统中的锁。在单体应用中我们通过锁解决的是控制共享资源访问的问题,而分布式锁,就是解决了分布式系统中控制共享资源访问的问题。与单体应用不同的是,分布式系统中竞争共享资源的最小粒度从线程升级成了进程。

我们可以基于reids实现分布式锁,如下:

SET 命令有个 NX 参数可以实现「key不存在才插入」,可以用它来实现分布式锁:

  • 如果 key 不存在,则显示插入成功,可以用来表示加锁成功;
  • 如果 key 存在,则会显示插入失败,可以用来表示加锁失败。

一般而言,还会对分布式锁加上过期时间,分布式锁的命令如下:

SET lock_key unique_value NX PX 10000
  • lock_key 就是 key 键;
  • unique_value 是客户端生成的唯一的标识;
  • NX 代表只在 lock_key 不存在时,才对 lock_key 进行设置操作;
  • PX 10000 表示设置 lock_key 的过期时间为 10s,这是为了避免客户端发生异常而无法释放锁。

而解锁的过程就是将 lock_key 键删除,但不能乱删,要保证执行操作的客户端就是加锁的客户端。所以,解锁的时候,我们要先判断锁的 unique_value 是否为加锁客户端,是的话,才将 lock_key 键删除。

可以看到,解锁是有两个操作,这时就需要 Lua 脚本来保证解锁的原子性,因为 Redis 在执行 Lua 脚本时,可以以原子性的方式执行,保证了锁释放操作的原子性。

// 释放锁时,先比较 unique_value 是否相等,避免锁的误释放
if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end

这样一来,就通过使用 SET 命令和 Lua 脚本在 Redis 单节点上完成了分布式锁的加锁和解锁。

List(列表)

List 列表是简单的字符串列表,按照插入顺序排序,可以从头部或尾部向 List 列表添加元素。

列表的最大长度为 2^32 - 1,也即每个列表支持超过 40 亿个元素。

存储结构

从 Redis 3.2 开始,Redis 采用 quicklist 作为其编码方式,它是一个双向链表,节点元素是 ziplist

quicklist 是大链表 大链表里套了多个小链表(ziplist)

List(列表)特点如下:

  1. 由于是链表,内存上不连续
  2. 操作头尾效率高,时间复杂度 O(1)
  3. 链表中 ziplist 的大小和元素个数都可以设置,其中大小默认 8kb(默认空间可以以后将他们串起来成quicklist )

补充:ziplist(压缩列表)

ziplist 用一块连续的内存存储数据,设计目标是让数据存储更紧凑,减少碎片开销,节约内存,它的结构如下

  1. zlbytes – 记录整个 ziplist 占用字节数
  2. zltail-offset – 记录尾节点偏移量:用于快速定位尾结点,倒着遍历
  3. zllength – 记录节点数量:
  4. entry – 节点,1 ~ N 个,每个 entry 记录了前一 entry 长度(也是为了倒序遍历 减去字节),本 entry 的编码、长度、实际数据,为了节省内存,根据实际数据长度不同,用于记录长度的字节数也不同,例如前一 entry 长度是 253 时,需要用 1 个字节,但超过了 253,需要用 5 个字节
  5. zlend – 结束标记

ziplist 适合存储少量元素,否则查询效率不高,并且长度可变的设计会带来连锁更新问题

应用场景

消息队列


Hash(哈希)

Hash 是一个键值对(key - value)集合,其中 value 的形式如: value=[{field1,value1},...{fieldN,valueN}]。Hash 特别适合用于存储对象。

存储结构

Hash 类型的底层数据结构是由压缩列表或哈希表实现的:

  • 如果哈希类型元素个数小于 512 个,所有值小于 64 字节的话,Redis 会使用**(ziplist)压缩列表**作为 Hash 类型的底层数据结构;
  • 如果哈希类型元素不满足上面条件,Redis 会使用hashtable 编码作为 Hash 类型的 底层数据结构。

补充:hashtable 编码

hash 函数,Redis 5.0 采用了 SipHash 算法

采用拉链法解决 key 冲突

rehash 时机
① 当元素数 < 1 * 桶个数时,不扩容
② 当元素数 > 5 * 桶个数时,一定扩容
③ 当 1 * 桶个数 <= 元素数 <= 5 * 桶个数时,如果此时没有进行 AOF 或 RDB 操作时
④ 当元素数 < 桶个数 / 10 时,缩容

rehash 要点

① 每个字典有两个哈希表,桶个数为 redis如存储表数据库 redis存储表结构数据_redis如存储表数据库,平时使用 ht[0],ht[1] 开始为 null,扩容时新数组大小为元素个数 * 2
渐进式 rehash,即不是一次将所有桶都迁移过去,每次对这张表 CRUD 仅迁移一个桶
active rehash,server 的主循环中,每 100 ms 里留出 1s 进行主动迁移
④ rehash 过程中,新增操作 ht[1] ,其它操作先操作 ht[0],若没有,再操作 ht[1]
⑤ redis 所有 CRUD 都是单线程,因此 rehash 一定是线程安全的

应用场景

缓存对象

Hash 类型的 (key,field, value) 的结构与对象的(对象id, 属性, 值)的结构相似,也可以用来存储对象。

我们以用户信息为例,它在关系型数据库中的结构是这样的:

uid

name

age

1

Meng

12

2

Qingqiu

23

我们可以使用如下命令,将用户对象的信息存储到 Hash 类型:

# 存储一个哈希表uid:1的键值
> HMSET uid:1 name Meng age 12
2
# 存储一个哈希表uid:2的键值
> HMSET uid:2 name Qingqiu age 23
2
# 获取哈希表用户id为1中所有的键值
> HGETALL uid:1
1) "name"
2) "Meng"
3) "age"
4) "12"
购物车数据

以用户 id 为 key,商品 id 为 field,商品数量为 value,恰好构成了购物车的3个要素,如下图所示。

redis如存储表数据库 redis存储表结构数据_java_02

涉及的命令如下:

  • 添加商品:HSET cart:{用户id} {商品id} 1
  • 添加数量:HINCRBY cart:{用户id} {商品id} 1
  • 商品总数:HLEN cart:{用户id}
  • 删除商品:HDEL cart:{用户id} {商品id}
  • 获取购物车所有商品:HGETALL cart:{用户id}

当前仅仅是将商品ID存储到了Redis 中,在回显商品具体信息的时候,还需要拿着商品 id 查询一次数据库,获取完整的商品的信息。

Set(集合)

Set 类型是一个无序并唯一的键值集合,它的存储顺序不会按照插入的先后顺序进行存储。

一个集合最多可以存储 2^32-1 个元素。概念和数学中个的集合基本类似,可以交集,并集,差集等等,所以 Set 类型除了支持集合内的增删改查,同时还支持多个集合取交集、并集、差集。

Set 类型和 List 类型的区别如下:

  • List 可以存储重复元素,Set 只能存储非重复元素;
  • List 是按照元素的先后顺序存储元素的,而 Set 则是无序方式存储元素的。

存储结构

Set 类型的底层数据结构是由整数集合或哈希表实现的:

  • 如果集合中的元素都是整数且元素个数小于 512 个,Redis 会使用intset编码作为 Set 类型的底层数据结构;
  • 如果集合中的元素不满足上面条件,则 Redis 使用hashtable 编码类型的底层数据结构。

应用场景

集合的主要几个特性,无序、不可重复、支持并交差等操作。

因此 Set 类型比较适合用来数据去重和保障数据的唯一性,还可以用来统计多个集合的交集、错集和并集等,当我们存储的数据是无序并且需要去重的情况下,比较适合使用集合类型进行存储。

但是要提醒你一下,这里有一个潜在的风险。Set 的差集、并集和交集的计算复杂度较高,在数据量较大的情况下,如果直接执行这些计算,会导致 Redis 实例阻塞

在主从集群中,为了避免主库因为 Set 做聚合计算(交集、差集、并集)时导致主库被阻塞,我们可以选择一个从库完成聚合统计,或者把数据返回给客户端,由客户端来完成聚合统计。

点赞

Set 类型可以保证一个用户只能点一个赞,这里举例子一个场景,key 是文章id,value 是用户id。

uid:1uid:2uid:3 三个用户分别对 article:1 文章点赞了。

# uid:1 用户对文章 article:1 点赞
> SADD article:1 uid:1
(integer) 1
# uid:2 用户对文章 article:1 点赞
> SADD article:1 uid:2
(integer) 1
# uid:3 用户对文章 article:1 点赞
> SADD article:1 uid:3
(integer) 1

uid:1 取消了对 article:1 文章点赞。

> SREM article:1 uid:1
(integer) 1

获取 article:1 文章所有点赞用户 :

> SMEMBERS article:1
1) "uid:3"
2) "uid:2"

获取 article:1 文章的点赞用户数量:

> SCARD article:1
(integer) 2

判断用户 uid:1 是否对文章 article:1 点赞了:

> SISMEMBER article:1 uid:1
(integer) 0  # 返回0说明没点赞,返回1则说明点赞了
共同关注

因为 Set 类型支持交集运算,所以可以用来计算共同关注的好友、公众号等。

key 可以是用户id,value 则是已关注的公众号的id。

uid:1 用户关注公众号 id 为 5、6、7、8、9,uid:2 用户关注公众号 id 为 7、8、9、10、11。

# uid:1 用户关注公众号 id 为 5、6、7、8、9
> SADD uid:1 5 6 7 8 9
(integer) 5
# uid:2  用户关注公众号 id 为 7、8、9、10、11
> SADD uid:2 7 8 9 10 11
(integer) 5

uid:1uid:2 共同关注的公众号:

# 获取共同关注
> SINTER uid:1 uid:2
1) "7"
2) "8"
3) "9"

uid:2 推荐 uid:1 关注的公众号:

> SDIFF uid:1 uid:2
1) "5"
2) "6"

验证某个公众号是否同时被 uid:1uid:2 关注:

> SISMEMBER uid:1 5
(integer) 1 # 返回0,说明关注了
> SISMEMBER uid:2 5
(integer) 0 # 返回0,说明没关注
抽奖活动

存储某活动中中奖的用户名 ,Set 类型因为有去重功能,可以保证同一个用户不会中奖两次

key为抽奖活动名,value为员工名称,把所有员工名称放入抽奖箱 :

>SADD lucky Tom Meng John Sean Marry Lindy Sary Yu
(integer) 5

如果允许重复中奖,可以使用 SRANDMEMBER 命令。

# 抽取 1 个一等奖:
> SRANDMEMBER lucky 1
1) "Tom"
# 抽取 2 个二等奖:
> SRANDMEMBER lucky 2
1) "Yu"
2) "Meng"
# 抽取 3 个三等奖:
> SRANDMEMBER lucky 3
1) "Sary"
2) "Tom"
3) "Meng"

如果不允许重复中奖,可以使用 SPOP 命令。

# 抽取一等奖1个
> SPOP lucky 1
1) "Sary"
# 抽取二等奖2个
> SPOP lucky 2
1) "Meng"
2) "Yu"
# 抽取三等奖3个
> SPOP lucky 3
1) "John"
2) "Sean"
3) "Lindy"

Sorted Set(有序集合)

Zset 类型(有序集合类型)相比于 Set 类型多了一个排序属性 score(分值),对于有序集合 ZSet 来说,每个存储元素相当于有两个值组成的,一个是有序结合的元素值,一个是排序值。

有序集合保留了集合不能有重复成员的特性(分值可以重复),但不同的是,有序集合中的元素可以排序。

存储结构

Zset 类型的底层数据结构是由压缩列表或跳表实现的:

  • 如果有序集合的元素个数小于 128 个,并且每个元素的值小于 64 字节时,Redis 会使用ziplist作为 Zset 类型的底层数据结构;
  • 如果有序集合的元素不满足上面的条件,Redis 会使用skiplist+hashtable作为 Zset 类型的底层数据结构;
举例说明
  1. 在数据量较小时,采用 ziplist 作为其编码,按 score 有序,当键或值长度过大(64)或个数过多(128)时,转为 skiplist + hashtable 编码,同时采用的理由是
  • 只用 hashtable,CRUD 是 O(1),但要执行有序操作,需要排序,带来额外时间空间复杂度
  • 只用 skiplist,虽然范围操作优点保留,但时间复杂度上升
  • 虽然同时采用了两种结构,但由于采用了指针,元素并不会占用双份内存
  1. skiplist 要点:多层链表、排序规则、 backward、level(span,forward)

redis如存储表数据库 redis存储表结构数据_redis如存储表数据库_03

  • score 存储分数、member 存储数据、按 score 排序,如果 score 相同再按 member 排序
  • backward 存储上一个节点指针
  • 每个节点中会存储层级信息(level),同一个节点可能会有多层,每个 level 有属性:
  • foward 同层中下一个节点指针
  • span 跨度,用于计算排名,不是所有跳表都实现了跨度,Redis 实现特有
  1. 多层链表可以加速查询,规则为,从顶层开始
  1. 大于同层右边的,继续在同层向右找
  2. 相等找到了
  3. 小于同层右边的或右边为 NULL,下一层,重复 1、2 步骤

redis如存储表数据库 redis存储表结构数据_Redis_04

  • 以查找【崔八】为例
  1. 从顶层(4)层向右找到【王五】节点,22 > 7 继续向右找,但右侧是 NULL,下一层
  2. 在【王五】节点的第 3 层向右找到【孙二】节点,22 < 37,下一层
  3. 在【王五】节点的第 2 层向右找到【赵六】节点,22 > 19,继续向右找到【孙二】节点,22 < 37,下一层
  4. 在【赵六】节点的第 1 层向右找到【崔八】节点,22 = 22,返回

注意

  • 数据量较小时,不能体现跳表的性能提升,跳表查询的时间复杂度是 redis如存储表数据库 redis存储表结构数据_redis如存储表数据库_05,与二叉树性能相当

补:跳表查询

skiplist 查找要点,从顶层开始

右边的,继续向右
= 找到了
< 右边的或右边为 NULL,下一层,重复 1、2 步骤

以查找 score = 22 为例:

redis如存储表数据库 redis存储表结构数据_redis如存储表数据库_06

按照一层链表,我们找到 22 得查 5 次

跳表:先查第一层找到7 发现 7 的右边是null了,再从7 查第二层,右边是 37 比22 大,再从7下一层,下一个元素是19 ,19小于22,19的下一个是37,还得下一层,下一层的下一个就是22了,找到了

跳表查询的时间复杂度 redis如存储表数据库 redis存储表结构数据_数据结构_07

应用场景

Zset 类型(Sorted Set,有序集合) 可以根据元素的权重来排序,我们可以自己来决定每个元素的权重值。比如说,我们可以根据元素插入 Sorted Set 的时间确定权重值,先插入的元素权重小,后插入的元素权重大。

在面对需要展示最新列表、排行榜等场景时,如果数据更新频繁或者需要分页显示,可以优先考虑使用 Sorted Set。

排行榜

有序集合比较典型的使用场景就是排行榜。例如学生成绩的排名榜、游戏积分排行榜、视频播放排名、电商系统中商品的销量排名等。

我们以博文点赞排名为例,发表了五篇博文,分别获得赞为 200、40、100、50、150。

# arcticle:1 文章获得了200个赞
> ZADD user:mengmeng:ranking 200 arcticle:1
(integer) 1
# arcticle:2 文章获得了40个赞
> ZADD user:mengmeng:ranking 40 arcticle:2
(integer) 1
# arcticle:3 文章获得了100个赞
> ZADD user:mengmeng:ranking 100 arcticle:3
(integer) 1
# arcticle:4 文章获得了50个赞
> ZADD user:mengmeng:ranking 50 arcticle:4
(integer) 1
# arcticle:5 文章获得了150个赞
> ZADD user:mengmeng:ranking 150 arcticle:5
(integer) 1

文章 arcticle:4 新增一个赞,可以使用 ZINCRBY 命令(为有序集合key中元素member的分值加上increment):

> ZINCRBY user:mengmeng:ranking 1 arcticle:4
"51"

查看某篇文章的赞数,可以使用 ZSCORE 命令(返回有序集合key中元素个数):

> ZSCORE user:mengmeng:ranking arcticle:4
"50"

获取文章赞数最多的 3 篇文章,可以使用 ZREVRANGE 命令(倒序获取有序集合 key 从start下标到stop下标的元素):

# WITHSCORES 表示把 score 也显示出来
> ZREVRANGE user:mengmeng:ranking 0 2 WITHSCORES
1) "arcticle:1"
2) "200"
3) "arcticle:5"
4) "150"
5) "arcticle:3"
6) "100"

获取 100 赞到 200 赞的文章,可以使用 ZRANGEBYSCORE 命令(返回有序集合中指定分数区间内的成员,分数由低到高排序):

> ZRANGEBYSCORE user:mengmeng:ranking 100 200 WITHSCORES
1) "arcticle:3"
2) "100"
3) "arcticle:5"
4) "150"
5) "arcticle:1"
6) "200"
电话、姓名排序

使用有序集合的 ZRANGEBYLEXZREVRANGEBYLEX 可以帮助我们实现电话号码或姓名的排序,我们以 ZRANGEBYLEX (返回指定成员区间内的成员,按 key 正序排列,分数必须相同)为例。

注意:不要在分数不一致的 SortSet 集合中去使用 ZRANGEBYLEX和 ZREVRANGEBYLEX 指令,因为获取的结果会不准确。

1、电话排序

我们可以将电话号码存储到 SortSet 中,然后根据需要来获取号段:

> ZADD phone 0 13100111100 0 13110114300 0 13132110901 
(integer) 3
> ZADD phone 0 13200111100 0 13210414300 0 13252110901 
(integer) 3
> ZADD phone 0 13300111100 0 13310414300 0 13352110901 
(integer) 3

获取所有号码:

> ZRANGEBYLEX phone - +
1) "13100111100"
2) "13110114300"
3) "13132110901"
4) "13200111100"
5) "13210414300"
6) "13252110901"
7) "13300111100"
8) "13310414300"
9) "13352110901"

获取 132 号段的号码:

> ZRANGEBYLEX phone [132 (133
1) "13200111100"
2) "13210414300"
3) "13252110901"

获取132、133号段的号码:

> ZRANGEBYLEX phone [132 (134
1) "13200111100"
2) "13210414300"
3) "13252110901"
4) "13300111100"
5) "13310414300"
6) "13352110901"

2、姓名排序

> zadd names 0 Toumas 0 Jake 0 Bluetuo 0 Gaodeng 0 Aimini 0 Aidehua 
(integer) 6

获取所有人的名字:

> ZRANGEBYLEX names - +
1) "Aidehua"
2) "Aimini"
3) "Bluetuo"
4) "Gaodeng"
5) "Jake"
6) "Toumas"

获取名字中大写字母A开头的所有人:

> ZRANGEBYLEX names [A (B
1) "Aidehua"
2) "Aimini"

获取名字中大写字母 C 到 Z 的所有人:

> ZRANGEBYLEX names [C [Z
1) "Gaodeng"
2) "Jake"
3) "Toumas"

BitMap

Bitmap,即位图,是一串连续的二进制数组(0和1),可以通过偏移量(offset)定位元素。BitMap通过最小的单位bit来进行0|1的设置,表示某个元素的值或者状态,时间复杂度为O(1)。

由于 bit 是计算机中最小的单位,使用它进行储存将非常节省空间,特别适合一些数据量大且使用二值统计的场景

HyperLogLog

Redis HyperLogLog 是 Redis 2.8.9 版本新增的数据类型,是一种用于「统计基数」的数据集合类型,基数统计就是指统计一个集合中不重复的元素个数。但要注意,HyperLogLog 是统计规则是基于概率完成的,不是非常准确,标准误算率是 0.81%。

所以,简单来说 HyperLogLog 提供不精确的去重计数

HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的内存空间总是固定的、并且是很小的。

Stream

Redis Stream 是 Redis 5.0 版本新增加的数据类型,Redis 专门为消息队列设计的数据类型。

在 Redis 5.0 Stream 没出来之前,消息队列的实现方式都有着各自的缺陷,例如:

  • 发布订阅模式,不能持久化也就无法可靠的保存消息,并且对于离线重连的客户端不能读取历史消息的缺陷;
  • List 实现消息队列的方式不能重复消费,一个消息消费完就会被删除,而且生产者需要自行实现全局唯一 ID。

基于以上问题,Redis 5.0 便推出了 Stream 类型也是此版本最重要的功能,用于完美地实现消息队列,它支持消息的持久化、支持自动生成全局唯一 ID、支持 ack 确认消息的模式、支持消费组模式等,让消息队列更加的稳定和可靠。

Redis 数据结构相关命令操作

内容过多,参考我的另一篇博文:

数据结构注意点

set与zset区别

set:

  • 集合中的元素是无序、不可重复的,一个集合最多能存储232-1个元素;
  • 集合除了支持对元素的增删改查之外,还支持对多个集合取交集、并集、差集。

zset:

  • 有序集合保留了集合元素不能重复的特点;
  • 有序集合会给每个元素设置一个分数,并以此作为排序的依据;
  • 有序集合不能包含相同的元素,但是不同元素的分数可以相同。

Redis中的watch命令

很多时候,要确保事务中的数据没有被其他客户端修改才执行该事务。Redis提供了watch命令来解决这类问题,这是一种乐观锁的机制。客户端通过watch命令,要求服务器对一个或多个key进行监视,如果在客户端执行事务之前,这些key发生了变化,则服务器将拒绝执行客户端提交的事务,并向它返回一个空值.

说说Redis中List结构的相关操作

列表是线性有序的数据结构,它内部的元素是可以重复的,并且一个列表最多能存储232-1个元素。列表包含如下的常用命令:

命令

说明

lpush/rpush

从列表的左侧/右侧添加数据;

lrange

指定索引范围,并返回这个范围内的数据;

lindex

返回指定索引处的数据;

lpop/rpop

从列表的左侧/右侧弹出一个数据;

blpop/brpop

从列表的左侧/右侧弹出一个数据,若列表为空则进入阻塞状态