Redis 的全稱是 Remote Dictionary Server,是一個使用 C 語言編寫的、開源的(BSD 許可)高效能非關係型(NoSQL)的鍵值對資料庫。
Redis 的資料是儲存在記憶體中的,所以讀寫速度非常快,被廣泛應用於快取方向,當然也有持久化資料庫的用法。
Redis 是高效能的記憶體資料庫,因此,快取是 Redis 最常用的場景。Redis 作為快取使用的時候,一般是採用先更新資料庫,再刪除快取的 Cache Aside Pattern 策略。
整體的邏輯是:業務從 Redis 中讀取資料,如果 Redis 中存取不到資料,然後讀取資料庫中的資料,將資料庫中的資料快取到 Redis 中;業務更新資料,直接修改資料庫中的資料,然後將 Redis 中的快取刪除。
這種方案需要注意的就是:避免快取擊穿,資料的實時性相對較低。
Redis 支援給 key 設定過期時間,使用者端無法存取到過期的 key,利用這一特性可以運用在限時的優惠活動、手機驗證碼等業務場景。
Redis 的 INCRBY
命令可以實現原子性的遞增,可以直接作為計數器儲存和遞增。尤其是,該命令在鍵不存在時會直接初始化值再執行 INCRBY
命令,執行成功後會直接返回計算增量之後的數值。
高並行的秒殺活動、分散式序列號的生成、限制手機傳送簡訊次數、介面限制存取次數等需要計數的功能,都涉及到計數器的概念,尤其是分散式系統會涉及到分散式計數器。
先使用 SETNX
命令來爭搶鎖,結果返回 1
表示設定成功,搶到之後再用 EXPIRE
命令給鎖加一個過期時間防止忘記釋放鎖。
從 Redis 的 2.6.12 版本開始,可以通過 SET
命令的複雜引數,將 SETNX
命令和 EXPIRE
命令合併成一條命令來使用:
EX second
: 設定鍵的過期時間為 second 秒,使用 EX
選項效果等同於 SETEX
命令。PX millisecond
: 設定鍵的過期時間為 millisecond 毫秒,使用 PX
選項效果等同於 PSETEX
命令。NX
: 只在鍵不存在時,才對鍵進行設定操作,使用 NX
選項效果等同於 SETNX
命令。XX
: 只在鍵已經存在時,才對鍵進行設定操作。同樣的,使用 SET
命令操作成功之後會返回 OK
,這樣才表示搶到了鎖。
為了避免分散式鎖被誤刪,加鎖時可以設定執行緒 ID 作為 value 值,刪除時需要執行緒的執行緒 ID 和 Redis 儲存的值一致才能夠刪除分散式鎖,否則只能等待鎖自動過期,整個刪除過程使用事務的方式保證原子性。
通過 Redis 的 SortedSet 可以實現排行榜功能。
比如說需要展示點贊排行榜,可以將使用者 ID 作為 SortedSet 的 value 值,將使用者的點贊數作為 SortedSet 的 score 值,SortedSet 提供的 ZRANGEBYSCORE
命令可以快速返回已排序的點贊排行榜。
延時佇列其實就是一個帶有延遲功能的訊息佇列,Redis 可以通過 SortedSet 實現延時佇列。
具體的實現如下:
ZADD
命令生產訊息,消費者可以使用 ZRANGEBYSCORE
命令獲取一段時間之前的資料輪詢處理;ZREM
命令是多執行緒爭搶任務的關鍵,ZREM
命令會返回被成功移除的成員數量,可以通過 ZREM
命令來決定任務的唯一屬主;ZREM
命令進行爭搶,那些沒搶到的程序都是白取了一次任務,這是浪費;ZRANGEBYSCORE
命令和 ZREM
命令一同挪到伺服器端進行原子化操作,這樣多個程序之間爭搶任務時就不會出現這種浪費了。第一種方案 是使用 List 結構作為非同步佇列,RPUSH
命令生產訊息,LPOP
命令消費訊息。當使用 LPOP
命令沒有得到訊息的時候,需要適當 sleep 一會再重試,在這裡 sleep 會導致訊息的處理延遲增加。
如果不做 sleep 重試,改進的 第二種方案 是,使用 LPOP
命令的阻塞版本 BLPOP
命令,在 List 佇列中沒有訊息的時候,它會阻塞直到訊息到來,一旦資料到來,則立刻醒過來,訊息的延遲幾乎為零。但是如果執行緒一直阻塞,Redis 的使用者端連線就成了閒置連線,閒置過久,伺服器一般會主動斷開連線,減少閒置資源佔用。這個時候 BLPOP
命令會丟擲異常來,程式碼中需要處理這樣的異常。
除了 List 佇列之外,第三種方案 是使用 Pub/Sub 訂閱者模式實現一對多的訊息佇列。但是 Redis 的釋出訂閱功能是無狀態的,對於釋出者來說,無法知道釋出的訊息是否被訂閱者接收到,在消費者下線的情況下,生產的訊息會丟失。