Redis批量查詢的四種技巧,應對高并發場景的利器!
在高并發場景下,巧妙地利用緩存批量查詢技巧能夠顯著提高系統性能。
在筆者看來,熟練掌握細粒度的緩存使用是每位架構師必備的技能。因此,在本文中,我們將深入探討 Redis 中批量查詢的一些技巧,希望能夠給你帶來一些啟發。
圖片
1.為什么需要批量執行命令
下圖展示了客戶端與服務端交互的流程:
圖片
每次客戶端發送一個一個請求命令,Redis 服務端接收到命令后,將命令放在隊列內,一個一個命令執行,并將結果返回。
批量執行命令有三點優勢:
- 提高命令執行效率減少了網絡延遲,從而提高了 Redis 服務器的響應速度。批量執行減少了每個命令的單獨網絡傳輸開銷,有效降低了往返時間(RTT)。
- 簡化客戶端邏輯通過將多個命令封裝成一個操作,客戶端的處理邏輯變得更加簡潔和清晰。這使得客戶端代碼更易讀、易維護。
- 提升事務性能批量執行命令能夠確保一組命令在同一時間內執行,從而提高了事務的性能。這對于需要保持原子性的操作尤為重要,確保一組命令要么全部執行成功,要么全部失敗。
接下來,我們詳細講解批量查詢的四種方式。
- 字符串 MGET命令
- 哈希表 HMGET命令
- 管道技術
- Lua 腳本
2.字符串 MGET命令
MGET 是 Redis 中的一個命令,用于批量獲取多個字符串鍵的值。它接受一個或多個鍵作為參數,返回與這些鍵關聯的值。
以下是一個簡要的描述:
圖片
- key1, key2, ..., keyN:要獲取值的鍵列表。
- MGET 返回一個包含相應值的列表,如果鍵不存在,則對應的位置返回 nil。
該命令主要用于一次性獲取多個鍵的值,可以減少多次單獨查詢的開銷,提高效率。
圖片
接下來,我們展示 SpringBoot 項目展示如何使用 MGET 命令。
圖片
圖中,我們分別設置(key:a , b , c) 三個 key 的值,然后定義一個列表對象 keys, 列表中包含了一個不存在的鍵 "d"。
圖片
當我們使用 MULTIGET 命令查看結果時,發現返回的結果是一個列表對象,列表對象的大小是 4,同時第三個對象值為 NULL ,也就是鍵 “d” 對應的值為 nil。
3.哈希表 HMGET命令
HMGET 是 Redis 中的命令之一,用于獲取哈希表中指定字段的值。
它接受一個哈希表的鍵以及一個或多個字段名作為參數,返回與這些字段名關聯的值。以下是 HMGET 命令的基本語法:
圖片
如果給定的域不存在于哈希表,那么返回一個 nil 值。
因為不存在的 key 被當作一個空哈希表來處理,所以對一個不存在的 key 進行 HMGET 操作將返回一個只帶有 nil 值的表。
圖片
接下來,我們展示 SpringBoot 項目展示如何使用 HMGET 命令。
圖片
首先分別設置鍵為 "myhashkey" 下的三個字段(field)的值 ,然后定義需要查詢的字段集合 fields ,最后調用哈希表的 HMGET 命令。
圖片
和 MGET命令的結果類似,spring data redis 會將結果封裝成 List 對象,列表對象的大小是 4,同時第三個對象值為 NULL ,因為字段 “d” 對應的值為 nil。
4.管道技術
Redis Pipeline(管道)命令是一種優化網絡通信的技術,可以將多個命令一次性發送給 Redis 服務器,可以減少客戶端與 Redis 服務器之間的網絡通信次數。
圖片
客戶端將多個命令一次性發送給 Redis 服務器,Redis 服務器緩存這些命令,并一次性執行,最后將執行結果一次性返回給客戶端。
通過使用 Redis Pipeline,顯而易見的好處是避免了在每個命令執行時都進行一次網絡通信,從而顯著降低了時間開銷。
- 1 次 pipeline(n條命令) = 1 次網絡時間 + 執行n 條命令時間
接下來,我們展示 SpringBoot 項目展示如何使用 Pipeline 管道命令。
圖片
首先分別設置三個鍵(key:a, b, c)的值,然后分別設置鍵為 "myhashkey" 下的三個字段(field)的值, 最后調用 Pipeline 執行多個命令,并獲取結果。
圖片
需要注意的是:
- Redis Cluster 中 Pipeline 命令操作可能無法保證原子性!由于 Redis Cluster 采用的是分片機制,這些鍵無法保證所有的 key 都在同一區域的哈希槽上。因此,即使使用了 Pipeline,每個命令仍可能在不同的節點上進行處理,導致多個命令的執行不在同一時刻。
- Pipeline 能執行有依賴關系的命令嗎?不可以。如果 Pipeline 中后一個命令的執行依賴于前一個命令的執行結果,Pipeline 無法滿足這樣的需求。
- Pipeline 對發送的命令有數量限制嗎?雖然命令可以一次性發給 Redis 服務端,但是考慮到帶寬等情況,建議不要超過500個命令,或者根據實際命令的數據類型和大小進行調整。這樣可以避免潛在的性能問題。
5.Lua 腳本
Redis Lua 腳本是一種在 Redis 服務器上執行的腳本語言,基于 Lua 編程語言。
這種腳本可以包含多個 Redis 命令,而且它們在 Redis 服務器上以原子性操作的方式執行。通過使用 Lua 腳本,你可以在服務器端執行一系列的 Redis 命令,而不需要將它們一條一條地發送到服務器。
Redis 執行 Lua 腳本有兩種執行方式:Eval 和 EvalSHA 。
5.1 Eval
圖片
EVAL命令的執行過程主要可以分為三個步驟:
- 根據客戶端提供的 Lua 腳本,在 Lua 環境中定義一個 Lua 函數。Lua 函數的名稱實際上是以 "f_" 為前綴加上腳本本身計算出的 SHA1 值,例如 f_ddfsdfjgjbg33rndgj00,其中 SHA1 的長度為40字符。函數體則是腳本本身。
- 將客戶端提供的腳本保存到 lua_scripts 字典中。簡單來說,就是添加一個鍵值對,其中鍵是 Lua 腳本的 SHA1 校驗和,值是 Lua 腳本本身。這主要是為了以后能夠復用這個腳本。
- 執行第一步在 Lua 環境中定義的函數,從而執行客戶端提供的 Lua 腳本。這個過程利用了在步驟二中保存的 SHA1 校驗和來調用對應的 Lua 函數。
這個流程使得 Redis 能夠高效地處理客戶端提供的 Lua 腳本,同時通過緩存 SHA1 校驗和,可以減少重復傳輸腳本的開銷,提高效率。
在Redis中,使用了 Key 列表和參數列表來為Lua腳本提供更多的靈活性,執行 Eval 命令的格式為:
圖片
下圖演示下 Lua 如何調用 Redis 命令 ,通過redis.call()來執行了 Redis 命令 。
圖片
5.2 EvalSHA
與 EVAL 不同,EVALSHA 的主要目的是通過腳本的 SHA1 校驗和來執行預先在服務器端加載的 Lua 腳本,從而避免重復傳輸腳本的開銷。
圖片
使用步驟:
- 加載 Lua 腳本到 Redis 服務端:
首先,將 Lua 腳本加載到 Redis 服務端。這可以通過 SCRIPT LOAD 命令完成。執行 SCRIPT LOAD 后,會返回一個 SHA1 校驗和,該值唯一標識了加載的 Lua 腳本。
圖片
這會返回SHA1 校驗和: a1104f2250e5dd9fc10c3c681ddb389e7bd4a2cf。
- 執行 Lua 腳本:
一旦 Lua 腳本被加載并獲得了 SHA1 校驗和,之后就可以使用 EVALSHA 命令來執行該腳本。
圖片
5.3 springboot例子
圖片
首先分別設置三個鍵(key:a, b, c)的值,然后分別設置鍵為 "myhashkey" 下的三個字段(field)的值, 并編寫 Lua 腳本,執行 Lua 腳本并獲取結果。
6.總結
本文介紹了Redis 中批量查詢的四種技巧:
- MGET(批量獲取字符串值):
簡單直接,適用于批量獲取字符串值的場景。
- HMGET(批量獲取哈希表字段值):
適用于批量獲取哈希表中的字段值,可以在一個命令中獲取多個字段。
- Pipeline(管道):
最小化網絡開銷,一次性發送多個不同的命令,管道中的命令按照執行順序依次執行,減少往返時間。高并發場景下,可以顯著提高性能。
- Lua腳本:
使用 Lua 腳本的好處 :
a.減少網絡開銷。將多個請求通過腳本的形式一次發送,減少網絡時延。
b.原子操作。Redis會將整個腳本作為一個整體執行,中間不會被其他命令插入。
c.復用。客戶端發送的腳本會永久存在 Redis 中,其他客戶端可以復用這一腳本而不需要使用代碼完成相同的邏輯。
當然 使用 Lua 腳本也會有瑕疵,編寫和維護相對復雜。