目錄
- 前言
- 1. Lua 腳本
- 1.1 Redis 創建并修改 Lua 環境的步驟
- 1.2 Lua 環境協作組件
- 1.3 EVAL 命令的實作
- 1.4 EVALSHA 命令的實作
- 1.5 腳本管理命令的實作
- 1.6 腳本復制
- 1.6.1 EVAL、SCRIPT FLUSH、SCRIPTLOAD 命令的復制
- 1.6.2 EVALSHA 命令的復制
- 2. 排序
- 2.1 SORT 命令的實作
- 2.2 SORT 命令的可選項
- 2.3 多個選項的執行順序
- 最后
前言
參考資料:《Redis設計與實作 第二版》;
第三部分為獨立功能的實作,主要由以下模塊組成:發布訂閱、事務、Lua 腳本、排序、二進制位陣列、慢查詢日志、監視器;
本篇將介紹 Redis 的Lua 腳本與排序,Redis 從 2.6 版本開始可以使用一些命令操作 Lua 腳本,引入相關支持;Redis 的 SORT 命令可以對串列鍵、集合鍵或者有序集合鍵的值進行排序,SORT 命令的一些可選性能讓我們得到想要的排序;
與本章相關的 Redis 命令總結在下篇文章,歡迎點擊收藏,本篇將不再重復:
《Redis常用命令及示例總結(API)》:https://www.cnblogs.com/dlhjw/p/15639773.html
1. Lua 腳本
- Lua 腳本的執行的原子性的;
1.1 Redis 創建并修改 Lua 環境的步驟
- 創建 Lua 環境:呼叫 Lua 的 C API 函式 lua_open;
- 載入函式庫:包括基礎庫、表格庫、字串庫、數學庫、除錯庫、Lua CJSON 庫、Struct 庫、Lua cmsgpack 庫等;
- 創建 redis 全域表格:表格包含對 Redis 進行操作的函式,如
redis.call和redis.pcall函式等; - 使用 Redis 自制的隨機函式替換 Lua 原有的隨機函式:
math.random和math.randomseed函式帶有副作用,Redis 對其進行了替換,以避免在腳本中引入副作用; - 創建排序輔助函式:有些命令在相同資料集上會產生不同結果,為了消除這些命令帶來的不確定性,創建了
redis__compare_helper排列輔助函式; - 創建 redis.pcall 函式的錯誤報告輔助函式:創建
__redis__err__handler錯誤處理函式,提供更詳細的出錯資訊; - 保護 Lua 全域環境:防止用戶在執行腳本時將額外的全域變數添加到 Lua 環境中,但可以修改已存在的全域變數;
- 將 Lua 環境保存到服務器狀態的 lua 屬性里:然后等待服務器傳來 lua 腳本;

1.2 Lua 環境協作組件
-
Redis 服務器創建兩個與 Lua 環境進行協作的組件;
-
偽客戶端:

-
lua_scripts 字典:
struct redisServer{ //... //字典,鍵為某個 Lua 腳本的 SHA1 校驗和,值為 SHA1 校驗和對應的 Lua 腳本 dict *lua_scripts; }- 所有被 EVAL 和 SCRIPT LOAD 命令執行過的腳本會被保存到
lua_scripts字典里;
- 所有被 EVAL 和 SCRIPT LOAD 命令執行過的腳本會被保存到
1.3 EVAL 命令的實作
- 執行命令:
EVAL "return 'hello world'" 0,實作步驟如下:
- 定義腳本函式:給函式命令為
f_SHA1的校驗和,函式體為腳本本身return 'hello world'; - 將腳本保存到 lua_scripts 字典:

- 執行腳本函式:先進行設定鉤子(hook)、傳入引數等準備作業,再執行函式,將執行結果保存到客戶端狀態的輸出緩沖區,執行垃圾回收等操作;
1.4 EVALSHA 命令的實作
- 每個被 EVAL 命令成功執行過的 Lua 腳本,在 Lua 環境中有一個與該腳本對應的 Lua 函式,函式名為
f_SHA1的校驗和; - 因此即使不知道腳本內容,也可以通過 EVALSHA SHA1校驗和 命令呼叫對應函式;
1.5 腳本管理命令的實作
- SCRIPT FLUSH:
- 用于清除服務器中所有和 Lua 腳本有關的資訊;
- 會釋放并重建
lua_scripts字典,關閉現有 Lua 環境并重建一個新的 Lua 環境;
- SCRIPT EXISTS:
- 根通過檢查給定校驗和 SHA1 是否在的于
lua_scripts字典的鍵,判斷對應的腳本是否存在服務器中;
- 根通過檢查給定校驗和 SHA1 是否在的于
- SCRIPT LOAD:
- 所做作業與 EVAL 命令前兩步一樣:定義腳本函式,將腳本函式保存進
lua_scripts字典;
- 所做作業與 EVAL 命令前兩步一樣:定義腳本函式,將腳本函式保存進
- SCRIPT KILL:
- 用于處理鉤子(hook)超時的情況;
- 如果腳本未執行寫入操作,客戶端可以使用該命令停止執行腳本;
- 如果腳本已經進行過寫入操作,客戶端只能使用 SHUTDOWN nosave 命令來停止服務器,防止不合法的資料被寫入到資料庫中;

1.6 腳本復制
- 當服務器運行在復制模式下時,具有寫性質的腳本命令會被復制到從服務器里,如:EVAL、EVALSHA、SCRIPT FLUSH、SCRIPTLOAD;
1.6.1 EVAL、SCRIPT FLUSH、SCRIPTLOAD 命令的復制
- 當主服務器執行完 EVAL、SCRIPT FLUSH、SCRIPTLOAD 命令時,會直接將被執行的命令傳播給所有從服務器;

1.6.2 EVALSHA 命令的復制
- EVALSHA 命令接受一個校驗和 SHA1 作為引數傳入;
- 每個服服務器間的腳本校驗和 SHA1 可能不同,導致在不同的服務器間執行 EVALSHA 命令情況可能也不同;
- Redis 復制 EVALSHA 命令的方法:
-
判斷傳播 EVALSHA 命令是否安全的方法:主服務器使用
repl_scriptcache_dict字典記錄哪些腳本傳播給了所有從服務器,跟lua_scripts字典對比即可得知哪些校驗和還未傳播;struct redisServer{ //... //字典,鍵是校驗和 SHA1,值是 NULL dict *repl_scriptcache_dict; }; -
清空 repl_scriptcache_dict 字典:每當主服務器添加一個新的從服務器時,主服務器會清空自己的
repl_scriptcache_dict字典; -
EVALSHA 命令轉換成 EVAL 命令的方法:根據 sha1 校驗和在
lua_scripts字典中查找對應 Lua腳本 script;然后改寫命令,用 script 替換 sha1;其他引數不變;- EVAL命令:
EVAL script numkeys key [key ...] arg [arg ...] - EVALSHA命令:
EVALSHA sha1 numkeys key [key ...] arg [arg ...]
- EVAL命令:
-
傳播 EVALSHA 命令的方法:當主服務器成功執行完一個 EVALSHA 命令后,會根據該命令指定的 SHA1 校驗和是否存在于
repl_scriptcache_dict字典里來決定是否向從服務器傳播 EVALSHA 命令還是 EVAL 命令;
-

2. 排序
2.1 SORT <key> 命令的實作
- 服務器執行 SORT numbers 命令的詳細步驟:
- 1)創建一個和 numbers 串列長度相同的陣列,陣列的每個項都是
redis.h/redisSortObject結構; - 2)遍歷陣列,將各個陣列項的 obj 指標指向 numbers 串列的各個項,構成 obj 指標和串列項的一對一關系;

- 3)遍歷陣列,將各個 obj 指標所指向的串列項轉換成一個 double 型別的浮點數,并將浮點數保存到相應陣列項
u.score屬性里;

- 4)根據陣列項
u.score屬性的值,對陣列進行數字值排序;

- 5)遍歷陣列,將各個陣列項的 obj 指標所指向的串列項作為排序結果回傳給客戶端;
2.2 SORT 命令的可選項
- ALPHA 選項可以對包含字串的鍵進行排列;
- ASC 選項執行升序排序;
- DESC 選項執行降序排序;
- 升序和降序都采用快速排序演算法;
- BY 選項可以指定某些字串鍵,或者某個哈希鍵說包含的某些域來作為權重,對一個鍵排序;
- LIMIT 選項可以讓結果只回傳其中部分已排序的元素;
- GET 選項可以在鍵被排序后,根據被排序的元素,以及 GET 選項指定的模式,查找并回傳某些鍵的值;
- STORE選項可以將排序結果保存在指定的鍵里面;
2.3 多個選項的執行順序
- 選項的執行順序:
- 1)排序:會先使用 ALPHA、ASC、DESC、BY 這幾個選項,對輸入鍵進行排序,并得到一個排序結果集;
- 2)限制排序結果集的長度:使用 LIMIT 選項,對排序結果集的長度進行限制;
- 3)獲取外部鍵:使用 GET 選項,根據結果集的元素,以及 GET 指定的模式,查找并獲取指定鍵的值,并用這些值構成新的結果集;
- 4)保存排序結果集:使用 STORE 選項,將排序結果集保存到指定的鍵上;
- 5)向客戶端回傳排序結果集:遍歷排序結果集,并依次向客戶端回傳排序結果集中的元素;
- 除了 GET 選項之外,改變選項的擺放順序不會影響 SORT 命令執行這些選項的順序;
最后

轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/374465.html
標籤:其他
