Redis HyperLogLog 是用来作基数统计的算法,每一个 HyperLoglog 键只须要占用 12KB 内存,就能够计算接近 264 个不一样的基数。HyperLogLog 的优势是在应对大量数据事能够利用极小且固定的空间完成对独立总数的统计,但缺点是它的统计并不十分准确,存在必定偏差。HyperLogLog 只会根据输入的元素来统计基数,而不会存储输入的元素,所以相比于 Set 集合类型,它不会出现元素越多占用内存多大的状况,可是它也不能像 Set 类型同样返回输入的元素。web
基数: 数据集中不一样元素的个数。例如数据集 {8, 7, 3, 1, 0, 2, 1, 0} 中,基数集为 {8, 7, 3, 1, 0 , 2},基数为 6。redis
PFADD key element1 [element2]:向 HyperLogLog 键 key 中添加一个或多个元素。至少有一个元素被添加返回 1,不然返回 0。算法
> PFADD test1 a b c d
(integer) 1 > PFADD test1 a (integer) 0 复制代码
PFCOUNT key1 [key2]:返回给定 HyperLogLog 的基数估算值,多个 HyperLogLog 时返回基数值之和。数据库
> PFCOUNT test1
(integer) 4 > PFADD test2 e f g (integer) 1 > PFCOUNT test1 test2 (integer) 7 复制代码
pfmerge destkey sourcekey1[sourcekey2]:将多个 HyperLogLog 合并为一个 HyperLogLog ,合并后的 HyperLogLog 的基数估算值是经过对全部给定 HyperLogLog 进行并集计算得出的。编程
> PFMERGE test test1 test2
OK > PFCOUNT test (integer) 7 复制代码
因为 HyperLogLog 能够对基数进行统计,所以咱们经常用于统计独立访客(Unique Visitor, 简称 UV)。好比今天有多少我的已经进行了签到或访问过,即便一天以内屡次访问,对于总数来讲仍是只增长 1。缓存
因为 Redis 发布订阅机制自己的不足,实际中的消息通讯并不经常使用 Redis 发布订阅完成,所以仅介绍一下。服务器
为何不用 Redis 发布订阅机制微信
数据可靠性缘由:Redis 发布订阅要求客户端在线,由 1 个客户端发布消息,n 个客户端接收消息,且消息的发布是 无状态的。好比咱们使用微信时,消息未发送成功会有红色感叹号提醒,发出去的消息在短期内仍能够撤回,对方上线后仍能够接收到消息,但 Redis 没法实现这些功能,它没法判断消息是否被接受了仍是在传输过程当中丢失了。 稳定性缘由:对于旧版的 Redis 来讲,若是一个客户端订阅了某个或者某些频道,频道推送了不少消息可是它读取消息的速度不够快,那么不断积压的消息就会使得 Redis 输出缓冲区的体积愈来愈大,这可能会致使 redis 的速度变慢,甚至直接崩溃。也可能会致使 Redis 服务被操做系统强制杀死,甚至致使操做系统自己不可用。新版的 redis 不会出现这种问题,由于它会自动断开不符合 client-output-buffer-limit pubsub 配置选项要求的订阅客户端。 资源消耗高:在 pub/sub 中发送者不须要独占一个 Redis 的连接,而订阅者则须要单独占用一个 Redis 的连接,而发布订阅通常对应多个订阅者,此时则有着太高的资源消耗。
Redis 发布订阅(pub/sub)是一种消息通讯模式:发送者(pub)发送消息,订阅者(sub)接收消息。 Redis 客户端能够订阅任意数量的频道。 下图展现了频道 channel 以及订阅了这个频道的三个客户端 client一、client二、client3 之间的关系:网络
graph BT;
B[client1]--subscribe-->A((channel))
C[client2]--subscribe-->A((channel))
D[client3]--subscribe-->A((channel))
复制代码
当有新消息经过 publish 命令发送给频道 channel 时,这个消息就会被发送给订阅它的三个客户端:编程语言
graph TB;
A[publish channel message]-.->B((channel))
B((channel))-.message.->C[client1]
B((channel))-.message.->D[client2]
B((channel))-.message.->E[client3]
复制代码
简单演示一下 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" 复制代码
Redis 事务能够一次执行多个命令,而且带有如下三个重要的保证:
由上述三个保证咱们能够看出虽然 Redis 保证单个命令的执行是原子性的,但并无在事务上增长任何保持原子性的机制,因此 Redis 事务的执行并非原子性的。这不像 MySQL 数据库中的事务,在 MySQL 事务中,如有一条命令执行失败,则会发生事务回滚。Redis 中事务中的某一条命令执行失败既不会形成已完成命令的回滚,也不会影响未完成命令的执行。
一个事务从开始到执行会经历如下三个阶段:
graph LR;
A[开始事务]-->B[命令入队]
B[命令入队]-->C[执行事务]
复制代码
# 监视 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 script numkeys key [key ...] arg [arg ...]:执行 Lua 脚本。numkeys 指定 key 和 arg 的个数。
> 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 script:将脚本 script 添加到脚本缓存中,但不当即执行,返回 SHA1 校验和。
> SCRIPT LOAD "return 'hello world'"
"5332031c6b470dc5a0dd9b4bf2030dea6d65de91" 复制代码
EVALSHA sha1 numkeys key [key ...] arg [arg ...]:根据给定的 sha1 校验码,执行缓存在服务器中的脚本。
> 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 EXISTS script [script ...]:经过 SHA1 校验和判断指定脚本是否被保存在缓存中。
> SCRIPT LOAD "return 'hello world'"
"5332031c6b470dc5a0dd9b4bf2030dea6d65de91" > SCRIPT LOAD 5332031c6b470dc5a0dd9b4bf2030dea6d65de91 1) (integer) 1 复制代码
SCRIPT FLUSH:清空缓存中的全部脚本。
> SCRIPT FLUSH
OK > SCRIPT LOAD 5332031c6b470dc5a0dd9b4bf2030dea6d65de91 # "return 'hello world'"的SHA1校验和 1) (integer) 0 复制代码
SCRIPT KILL:杀死目前正在运行的脚本。
开发者可使用 Lua 语言编写脚本传到 Redis 中执行。在 Lua 脚本中能够调用大部分 Redis 命令。使用 Redis 脚本有如下几个优点:
本文使用 mdnice 排版