Redis HyperLogLog 是用来做基数统计的算法,每个 HyperLoglog 键只需要占用 12KB 内存,就可以计算接近 264 个不同的基数。HyperLogLog 的优点是在应对大量数据事可以利用极小且固定的空间完成对独立总数的统计,但缺点是它的统计并不十分准确,存在一定误差。HyperLogLog 只会根据输入的元素来统计基数,而不会存储输入的元素,因此相比于 Set 集合类型,它不会出现元素越多占用内存多大的情况,但是它也不能像 Set 类型一样返回输入的元素。
基数:数据集中不同元素的个数。例如数据集 {8, 7, 3, 1, 0, 2, 1, 0} 中,基数集为 {8, 7, 3, 1, 0 , 2},基数为 6。
> PFADD test1 a b c d
(integer) 1
> PFADD test1 a
(integer) 0
> PFCOUNT test1
(integer) 4
> PFADD test2 e f g
(integer) 1
> PFCOUNT test1 test2
(integer) 7
> PFMERGE test test1 test2
OK
> PFCOUNT test
(integer) 7
由于 HyperLogLog 可以对基数进行统计,因此我们常常用于统计独立访客(Unique Visitor, 简称 UV)。比如今天有多少个人已经进行了签到或访问过,即使一天之内多次访问,对于总数来说还是只增加 1。
由于 Redis 发布订阅机制本身的不足,实际中的消息通信并不常用 Redis 发布订阅完成,因此仅介绍一下。
为什么不用 Redis 发布订阅机制
Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。Redis 客户端可以订阅任意数量的频道。下图展示了频道 channel 以及订阅了这个频道的三个客户端 client1、client2、client3 之间的关系:
当有新消息通过 publish 命令发送给频道 channel 时,这个消息就会被发送给订阅它的三个客户端:
简单演示一下 Redis 发布订阅,首先我们打开一个客户端订阅频道 channel1:
> SUBSCRIBE channel1
Reading messages... (press Ctrl-C to quit)
1) "subscribe"
2) "channel1"
3) (integer) 1
然后,我们再打开一个客户端,并向频道 channel1 发布一条消息:
> PUBLISH channel1 "test message"
(integer) 1
此时,订阅了频道 channel1 的客户端会接受到消息,会多出以下几行:
1) "message"
2) "channel1"
3) "test message"
sub/pub
Redis 事务可以一次执行多个命令,并且带有以下三个重要的保证:
由上述三个保证我们可以看出虽然 Redis 保证单个命令的执行是原子性的,但并没有在事务上增加任何保持原子性的机制,所以 Redis 事务的执行并不是原子性的。这不像 MySQL 数据库中的事务,在 MySQL 事务中,若有一条命令执行失败,则会发生事务回滚。Redis 中事务中的某一条命令执行失败既不会造成已完成命令的回滚,也不会影响未完成命令的执行。
一个事务从开始到执行会经历以下三个阶段:
# 监视 key,事务成功执行
> WATCH name1 score1
OK
> MULTI # 开始事务
OK
> SET name1 "Jack"
QUEUED
> SET score1 80
QUEUED
> EXEC # 执行事务
1) OK
2) OK
下图演示了监视 key 但事务被打断的情况:
Redis 脚本使用 Lua 解释器来执行脚本。由于作者对 Lua 没什么了解,这里就只能网罗些有用的信息,简单介绍。
> EVAL "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 key1 key2 arg1 agr2
1) "key1"
2) "key2"
3) "arg1"
4) "arg2"
> SCRIPT LOAD "return 'hello world'"
"5332031c6b470dc5a0dd9b4bf2030dea6d65de91"
> SCRIPT LOAD "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}"
"a42059b356c875f0717db19a51f6aaca9ae659ea"
> EVALSHA "a42059b356c875f0717db19a51f6aaca9ae659ea" 2 key1 key2 arg1 arg2
1) "key1"
2) "key2"
3) "arg1"
4) "arg2"
> SCRIPT LOAD "return 'hello world'"
"5332031c6b470dc5a0dd9b4bf2030dea6d65de91"
> SCRIPT LOAD 5332031c6b470dc5a0dd9b4bf2030dea6d65de91
1) (integer) 1
> SCRIPT FLUSH
OK
> SCRIPT LOAD 5332031c6b470dc5a0dd9b4bf2030dea6d65de91 # "return 'hello world'"的SHA1校验和
1) (integer) 0
开发者可以使用 Lua 语言编写脚本传到 Redis 中执行。在 Lua 脚本中可以调用大部分 Redis 命令。使用 Redis 脚本有以下几个优势: