Redis HyperLoglog基数统计, PubSub发布订阅, Stream消息队列

HyperLoglog基数统计

Redis 经常使用的数据类型有字符串、列表、散列、集合和有序集合,但这些类型并不能满足所有的应用场景,因此,Redis 的后续版本不断的扩增其他数据类型来增强 Redis 适用能力。在 Redis 2.8.9 版本中新增了 HyperLogLog 类型。

什么是HyperLoglog

HyperLoglog 是 Redis 重要的数据类型之一,它非常适用于海量数据的计算、统计,其特点是占用空间小,计算速度快。

HyperLoglog 采用了一种基数估计算法,因此,最终得到的结果会存在一定范围的误差(标准误差为 0.81%)。每个 HyperLogLog key 只占用 12 KB 内存,所以理论上可以存储大约2^64个值,而 set(集合)则是元素越多占用的内存就越多,两者形成了鲜明的对比 。

HyperLoglog 使用起来较为方便,但是其内部原理较为复杂,不建议大家深入研究,只要会用即可。

基数定义

基数定义:一个集合中不重复的元素个数就表示该集合的基数,比如集合 {1,2,3,1,2} ,它的基数集合为 {1,2,3} ,所以基数为 3。HyperLogLog 正是通过基数估计算法来统计输入元素的基数。

HyperLoglog 不会储存元素值本身,因此,它不能像 set 那样,可以返回具体的元素值。HyperLoglog 只记录元素的数量,并使用基数估计算法,快速地计算出集合的基数是多少。

场景应用

HyperLogLog 也有一些特定的使用场景,它最典型的应用场景就是统计网站用户月活量,或者网站页面的 UV(网站独立访客)数据等。

UV 与 PV(页面浏览量) 不同,UV 需要去重,同一个用户一天之内的多次访问只能计数一次。这就要求用户的每一次访问都要带上自身的用户 ID,无论是登陆用户还是未登陆用户都需要一个唯一 ID 来标识。

当一个网站拥有巨大的用户访问量时,我们可以使用 Redis 的 HyperLogLog 来统计网站的 UV (网站独立访客)数据,它提供的去重计数方案,虽说不精确,但 0.81% 的误差足以满足 UV 统计的需求。

常用命令

HyperLoglog命令
命令说明
PFADD key element [element ...]添加指定元素到 HyperLogLog key 中。
PFCOUNT key [key ...]返回指定 HyperLogLog key 的基数估算值。
PFMERGE destkey sourcekey [sourcekey ...]将多个 HyperLogLog key 合并为一个 key。
 

基本命令

HyperLogLog 提供了三个常用命令,分别是PFADDPFCOUNTPFMERGE

下面看一组实例演示:假设有 6 个用户(user01-user06),他们分别在上午 8 与 9 点访问了www.biancheng.netC语言中文网。

#向指定的key中添加用户
127.0.0.1:6379> PFADD user:uv:2021011308 user01 user02 user03
(integer) 1
#向指定的key中添加用户
127.0.0.1:6379> PFADD user:uv:2021011309 user04 user05
(integer) 1
#统计基数值
127.0.0.1:6379> PFCOUNT user:uv:2021011308
(integer) 3
#重复元素不能添加成功,其基数仍然为3
127.0.0.1:6379> PFADD user:uv:2021011308 user01 user02
(integer) 0
127.0.0.1:6379> PFCOUNT user:uv:2021011308
(integer) 3
#添加新元素值
127.0.0.1:6379> PFADD user:uv:2021011308 user06
(integer) 1
#基数值变为4
127.0.0.1:6379> PFCOUNT user:uv:2021011308
(integer) 4
#统计两个key的基数值
127.0.0.1:6379> PFCOUNT user:uv:2021011308 user:uv:2021011309
(integer) 6
#将两个key值合并为一个
127.0.0.1:6379> PFMERGE user:uv:2021011308-09 user:uv:2021011308 user:uv:2021011309
OK
#使用合并后key统计基数值
127.0.0.1:6379> PFCOUNT user:uv:2021011308-09
(integer) 6

在线练习工具:Try Redis
查看更多命令:Command reference – Redis

Redis PubSub发布订阅

Redis PubSub 模块又称发布订阅者模式,是一种消息传递系统,实现了消息多播功能。发布者(即发送方)发送消息,订阅者(即接收方)接收消息,而用来传递消息的链路则被称为 channel。在 Redis 中,一个客户端可以订阅任意数量的 channel(可译为频道)。

消息多播:生产者生产一次消息,中间件负责将消息复制到多个消息队列中,每个消息队列由相应的消费组进行消费,这是分布式系统常用的一种解耦方式。

发布/订阅流程

下面的示例演示了“发布/订阅者”模式的工作流程,示意图如下所示:
 


图1:发布订阅模式

1) 订阅者/等待接收消息

首先打开 Redis 客户端,然后订阅了一个名为“www.biancheng.net”的 channel,使用如下命令:

#订阅channel
127.0.0.1:6379> SUBSCRIBE www.biancheng.net
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "www.biancheng.net"
3) (integer) 1 

上述示例使用SUBSCRIBE命令订阅了名为 www.biancheng.net 的 channel。命令执行后该客户端会出处于等待接收消息的阻塞状态。

2) 发布者/发送消息

下面再启动一个 Redis 客户端,输入如下命令:

127.0.0.1:6379> PUBLISH www.biancheng.net "this is website"
(integer) 1
127.0.0.1:6379> PUBLISH www.biancheng.net "hello world"
(integer) 1
127.0.0.1:6379> PUBLISH www.biancheng.net "how are you"
(integer) 1

通过上述PUBLISH命令发布了三条信息。现在两个客户端在处于同一个名为“www.biancheng.net”的频道上,前者负责接收消息,后者负责发布消息。

3) 订阅者/成功接收消息

完成了上述操作后,您会在接收消息的客户端得到如下输出结果:

127.0.0.1:6379> SUBSCRIBE www.biancheng.net
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "www.biancheng.net"
3) (integer) 1
1) "message"
2) "www.biancheng.net"
3) "this is website"
1) "message"
2) "www.biancheng.net"
3) "hello world"
1) "message"
2) "www.biancheng.net"
3) "how are you"

常用命令汇总

Redis PubSub常用命令
命令说明
PSUBSCRIBE pattern [pattern ...]订阅一个或多个符合指定模式的频道。
PUBSUB subcommand [argument [argument ...]]查看发布/订阅系统状态,可选参数
1) channel 返回在线状态的频道。
2) numpat 返回指定模式的订阅者数量。
3) numsub 返回指定频道的订阅者数量。
PUBSUB subcommand [argument [argument ...]]将信息发送到指定的频道。
PUNSUBSCRIBE [pattern [pattern ...]]退订所有指定模式的频道。
SUBSCRIBE channel [channel ...]订阅一个或者多个频道的消息。
UNSUBSCRIBE [channel [channel ...]]退订指定的频道。

基本命令应用

下面对上述常用命令做演示:

#订阅指定模式的频道,*代表通配符,会匹配所有www开头的频道
127.0.0.1:6379> PSUBSCRIBE www*
Reading messages... (press Ctrl-C to quit)
1) "psubscribe"
2) "www*"
3) (integer) 1
#按ctrl+c退出阻塞状态
^C
C:\Users\Administrator>redis-cli
#查看发布订阅系统状态,返回相应的频道
127.0.0.1:6379> PUBSUB channels
1) "www.biancheng.net"
#退订指定模式的频道
127.0.0.1:6379> PUNSUBSCRIBE www*
1) "punsubscribe"
2) "www*"
3) (integer) 0
#退订指定频道
127.0.0.1:6379> UNSUBSCRIBE www.biancheng.net
1) "unsubscribe"
2) "www.biancheng.net"
3) (integer) 0

注意:每个模式以*作为匹配符,比如 www* 匹配所有以 www 开头的频道,比如 www.baidu.com 、www.biancheng.net  等等。

在线练习工具:Try Redis
查看更多命令:Command reference – Redis

 Redis Stream消息队列

Redis Stream 是 Redis 5.0 版本引入的一种新数据类型,同时它也是 Redis 中最为复杂的数据结构,本节主要对 Stream 做相关介绍。

什么是Stream?

Stream 实际上是一个具有消息发布/订阅功能的组件,也就常说的消息队列。其实这种类似于 broker/consumer(生产者/消费者)的数据结构很常见,比如 RabbitMQ 消息中间件、Celery 消息中间件,以及 Kafka 分布式消息系统等,而 Redis Stream 正是借鉴了 Kafaka 系统。

1) 优点

Strean 除了拥有很高的性能和内存利用率外, 它最大的特点就是提供了消息的持久化存储,以及主从复制功能,从而解决了网络断开、Redis 宕机情况下,消息丢失的问题,即便是重启 Redis,存储的内容也会存在。

2) 流程

Stream 消息队列主要由四部分组成,分别是:消息本身、生产者、消费者和消费组,对于前述三者很好理解,下面了解什么是消费组。

一个 Stream 队列可以拥有多个消费组,每个消费组中又包含了多个消费者,组内消费者之间存在竞争关系。当某个消费者消费了一条消息时,同组消费者,都不会再次消费这条消息。被消费的消息 ID 会被放入等待处理的 Pending_ids 中。每消费完一条信息,消费组的游标就会向前移动一位,组内消费者就继续去争抢下消息。

Redis Stream 消息队列结构程如下图所示:
 


图1:Redis Stream流程处理图

下面对上图涉及的专有名词做简单解释:

  • Stream direction:表示数据流,它是一个消息链,将所有的消息都串起来,每个消息都有一个唯一标识 ID 和对应的消息内容(Message content)。
  • Consumer Group :表示消费组,拥有唯一的组名,使用 XGROUP CREATE 命令创建。一个 Stream 消息链上可以有多个消费组,一个消费组内拥有多个消费者,每一个消费者也有一个唯一的 ID 标识。
  • last_delivered_id :表示消费组游标,每个消费组都会有一个游标 last_delivered_id,任意一个消费者读取了消息都会使游标 last_delivered_id 往前移动。
  • pending_ids :Redis 官方称为 PEL,表示消费者的状态变量,它记录了当前已经被客户端读取的消息 ID,但是这些消息没有被 ACK(确认字符)。如果客户端没有 ACK,那么这个变量中的消息 ID 会越来越多,一旦被某个消息被 ACK,它就开始减少。

3) ACK 

ACK(Acknowledge character)即确认字符,在数据通信中,接收方传递给发送方的一种传输类控制字符。表示发来的数据已确认接收无误。在 TCP/IP 协议中,如果接收方成功的接收到数据,那么会回复一个 ACK 数据。通常 ACK 信号有自己固定的格式,长度大小,由接收方回复给发送方。

常用命令汇总

Redis Stream命令
命令说明
XADD 添加消息到末尾。
XTRIM对 Stream 流进行修剪,限制长度。
XDEL删除指定的消息。
XLEN获取流包含的元素数量,即消息长度。
XRANGE获取消息列表,会自动过滤已经删除的消息。
XREVRANGE 反向获取消息列表,ID 从大到小。
XREAD以阻塞或非阻塞方式获取消息列表。
XGROUP CREATE创建消费者组。
XREADGROUP GROUP读取消费者组中的消息。
XACK将消息标记为"已处理"。
XGROUP SETID为消费者组设置新的最后递送消息ID。
XGROUP DELCONSUMER删除消费者。
XGROUP DESTROY删除消费者组。
XPENDING显示待处理消息的相关信息。
XCLAIM 转移消息的归属权。
XINFO查看 Stream 流、消费者和消费者组的相关信息。
XINFO GROUPS查看消费者组的信息。
XINFO STREAM 查看 Stream 流信息。
XINFO CONSUMERS key group查看组内消费者流信息。

基本命令应用

下面通过一组示例演示有关 Stream 命令的使用:

<span style="color:#444444">#添加一个消息, * 表示以时间戳自动创建id
127.0.0.1:6379> XADD mystream * username www.biancheng.net age 10 c.biancheng.net age 9
"1610619132674-1"
#自定义id等于001,注意id只增不减
127.0.0.1:6379> XADD mystream1 001 name zhangsan addr hebei
"1-0"
127.0.0.1:6379> XADD mystream1 002 name lisi addr hunan
"2-0"
#如果插入重复的id号会报错
127.0.0.1:6379> XADD mystream1 001 name wangwu addr fujian
(error) ERR The ID specified in XADD is equal or smaller than the target stream top item
127.0.0.1:6379> XADD mystream1 003 name wangwu addr fujian
"3-0"
#删除id=001的数据
127.0.0.1:6379> XDEL mystream1 001
(integer) 1
#查看stream队列包含的消息数量,也就消息长度
127.0.0.1:6379> XLEN mystream1
(integer) 2
#获取消息列表,-表示最小,+表示最大
127.0.0.1:6379> XRANGE mystream - +
1) 1) "1610619132674-0"
   2) 1) "username"
      2) "www.biancheng.net"
      3) "age"
      4) "10"
2) 1) "1610619178028-0"
   2) 1) "username"
      2) "c.biancheng.net"
      3) "age"
      4) "9"
#获取消息列表
127.0.0.1:6379> XRANGE mystream1 - 003
1) 1) "2-0"
   2) 1) "name"
      2) "lisi"
      3) "addr"
      4) "hunan"
2) 1) "3-0"
   2) 1) "name"
      2) "wangwu"
      3) "addr"
      4) "fujian"
#使用count指定返回数据的数量
127.0.0.1:6379> XRANGE mystream1 - 003 count 1
1) 1) "2-0"
   2) 1) "name"
      2) "lisi"
      3) "addr"
      4) "hunan"
#删除整个Stream
127.0.0.1:6379> DEL mystream
#使用xread读取消息
127.0.0.1:6379> XREAD count 2 STREAMS mystream1 2-0
1) 1) "mystream1"
   2) 1) 1) "3-0"
         2) 1) "name"
            2) "wangwu"
            3) "addr"
            4) "fujian"
</span>

创建消息ID

上述示例中,当我们创建一个 Srteam 时, 需要创建消息 ID,该 ID 是唯一、不可重复的,并且只增不减。消息 ID 有两种创建方式,一是系统自动生成,二是自定义创建。

1) 系统自动创建

语法格式如下:

XADD key ID field value [field value ...]

参数说明如下:

  • key :指定队列名称,如果不存就创建;
  • ID :消息 id,我们使用*表示由 redis 生成,可以自定义,但是要自己保证递增性;
  • field value :消息记录。


返回值是毫秒时间戳格式的字符串。比如 1610619132674-2,它表示在该毫秒内产生的第 2 条消息。使用示例:

XADD mystream * username www.biancheng.net age 10

2) 自定义ID

自定义 ID 比较简单,但是需要注意的是 ID 的形式必须是 “整数”,并且后面加入消息的 ID 必须大于前面消息的 ID,也就是自定义 ID 也必须遵守递增的规则。示例如下:

XADD mystream1 001 name zhangsan addr hebei

创建消费组

Redis Stream通过XGROUP CREATE指令创建消费组(Consumer Group),在创建时,需要传递起始消息的 ID 用来初始化 last_delivered_id 变量。语法格式如下:

<span style="color:#444444">XGROUP [CREATE key groupname id-or-$] [SETID key groupname id-or-$] [DESTROY key groupname] [DELCONSUMER key groupname consumername]</span>

参数说明如下:

  • key :指定 Stream 队列名称,若不存在则自动创建。
  • groupname :自定义消费组的名称,不可重复。
  • $ :表示从尾部开始消费,只接受新消息,而当前 Stream 的消息则被忽略。


示例如下:

<span style="color:#444444">#创建消费组,并传递消息起始id 0-0
127.0.0.1:6379> XGROUP CREATE mystream1 ms1 0-0
OK
#从尾部开始消费信息,只接受新消息
127.0.0.1:6379> XGROUP CREATE mystream1 ms3 $
OK
#xinfo查看队列信息
127.0.0.1:6379> XINFO stream mystream1
#队列中消息的长度
1) "length"
2) (integer) 2
3) "radix-tree-keys"
4) (integer) 1
5) "radix-tree-nodes"
6) (integer) 2
#有几个消费组
7) "groups"
8) (integer) 2
9) "last-generated-id"
10) "3-0"
11) "first-entry" #第一个消息
12) 1) "2-0"
    2) 1) "name"
       2) "lisi"
       3) "addr"
       4) "hunan"
13) "last-entry" #最后一个消息
14) 1) "3-0"
    2) 1) "name"
       2) "wangwu"
       3) "addr"
       4) "fujian"
#查看消费组信息
127.0.0.1:6379> XINFO GROUPS mystream1
1) 1) "name"
   2) "ms1"
   3) "consumers"
   4) (integer) 0
   5) "pending"
   6) (integer) 0
   7) "last-delivered-id"
   8) "0-0"
2) 1) "name"
   2) "ms3"
   3) "consumers"
   4) (integer) 0
   5) "pending"
   6) (integer) 0
   7) "last-delivered-id"
   8) "3-0"
</span>

消费消息

Redis Stream 通过XREADGROUP命令使消费组消费信息,它和XREAD命令一样,都可以阻塞等待新消息。读到新消息后,对应的消息 ID 就会进入消费者的 PLE(正在处理的消息)结构里,客户端处理完毕后使用 XACK 命令通知 Redis 服务器,本条消息已经处理完毕,该消息的 ID 就会从 PEL 中移除。示意图如下:
 


图2:Redis Stream 流程示意图


XREADGROUP命令的语法格式如下所示:

XREADGROUP GROUP group consumer [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...]  

参数说明如下:

  • group :消费组名称。
  • consumer :消费者名称。
  • count : 要读取的数量。
  • milliseconds : 阻塞时间,以毫秒为单位。
  • key :  键指定的队列名称。
  • ID : 表示消息 ID。


使用示例如下:

<span style="color:#444444">#消费组中消费者读取消息,> 表示每当消费一个信息,消费组游标就前移一位
127.0.0.1:6379> XREADGROUP GROUP ms1 c1 COUNT 1 STREAMS mystream1 >
1) 1) "mystream1"
   2) 1) 1) "2-0"
         2) 1) "name"
            2) "lisi"
            3) "addr"
            4) "hunan"
#再使用ms1-c1读取一条消息
127.0.0.1:6379> XREADGROUP GROUP ms1 c1 COUNT 1 STREAMS mystream1 >
1) 1) "mystream1"
   2) 1) 1) "3-0"
         2) 1) "name"
            2) "wangwu"
            3) "addr"
            4) "fujian"
#BLOCK 1000表示等待1秒,如果没有任何消息到来,则返回nill,此时移动到了末尾
127.0.0.1:6379> XREADGROUP GROUP ms1 c1 COUNT 1 BLOCK 1000 STREAMS mystream1 >
(nil)
127.0.0.1:6379> XREADGROUP GROUP ms1 c1 COUNT 1 STREAMS mystream1 1
1) 1) "mystream1"
   2) 1) 1) "2-0"
         2) 1) "name"
            2) "lisi"
            3) "addr"
            4) "hunan"
#超出了消息id的范围
127.0.0.1:6379> XREADGROUP GROUP ms1 c1 COUNT 1 STREAMS mystream1 3
1) 1) "mystream1"
   2) (empty list or set)
#添加新的消息 ID为004
127.0.0.1:6379> XADD mystream1 004 name zhangwu age 24
#使用另外一个消费组读取消息
127.0.0.1:6379> XREADGROUP GROUP ms3 c2 COUNT 2 STREAMS mystream1 >
1) 1) "mystream1"
   2) 1) 1) "4-0"
         2) 1) "name"
            2) "zhangwu"
            3) "age"
            4) "21"
#xack将id=002消息标记为已经处理
127.0.0.1:6379> XACK mystream1 ms1 002
(integer) 1
</span>

注意:>表示每当消费者读取一条消息时,last_delivered_id 变量就会前移一位。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值