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 排版