天天看點

Redis | 第9章 Lua 腳本與排序《Redis設計與實作》

目錄

前言

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

Lua 腳本的執行的原子性的;

建立 Lua 環境:調用 Lua 的 C API 函數 lua_open;

載入函數庫:包括基礎庫、表格庫、字元串庫、數學庫、調試庫、Lua CJSON 庫、Struct 庫、Lua cmsgpack 庫等;

建立 redis 全局表格:表格包含對 Redis 進行操作的函數,如 <code>redis.call</code> 和 <code>redis.pcall</code> 函數等;

使用 Redis 自制的随機函數替換 Lua 原有的随機函數:<code>math.random</code> 和 <code>math.randomseed</code> 函數帶有副作用,Redis 對其進行了替換,以避免在腳本中引入副作用;

建立排序輔助函數:有些指令在相同資料集上會産生不同結果,為了消除這些指令帶來的不确定性,建立了 <code>redis__compare_helper</code> 排列輔助函數;

建立 redis.pcall 函數的錯誤報告輔助函數:建立 <code>__redis__err__handler</code> 錯誤處理函數,提供更詳細的出錯資訊;

保護 Lua 全局環境:防止使用者在執行腳本時将額外的全局變量添加到 Lua 環境中,但可以修改已存在的全局變量;

将 Lua 環境儲存到伺服器狀态的 lua 屬性裡:然後等待伺服器傳來 lua 腳本;

Redis | 第9章 Lua 腳本與排序《Redis設計與實作》

Redis 伺服器建立兩個與 Lua 環境進行協作的元件;

僞用戶端:

Redis | 第9章 Lua 腳本與排序《Redis設計與實作》

lua_scripts 字典:

所有被 EVAL 和 SCRIPT LOAD 指令執行過的腳本會被儲存到 <code>lua_scripts</code> 字典裡;

執行指令:<code>EVAL "return 'hello world'" 0</code>,實作步驟如下:

定義腳本函數:給函數指令為 <code>f_SHA1的校驗和</code>,函數體為腳本本身<code>return 'hello world'</code>;

将腳本儲存到 lua_scripts 字典:

Redis | 第9章 Lua 腳本與排序《Redis設計與實作》

執行腳本函數:先進行設定鈎子(hook)、傳入參數等準備工作,再執行函數,将執行結果儲存到用戶端狀态的輸出緩沖區,執行垃圾回收等操作;

每個被 EVAL 指令成功執行過的 Lua 腳本,在 Lua 環境中有一個與該腳本對應的 Lua 函數,函數名為 <code>f_SHA1的校驗和</code>;

是以即使不知道腳本内容,也可以通過 EVALSHA SHA1校驗和 指令調用對應函數;

SCRIPT FLUSH:

用于清除伺服器中所有和 Lua 腳本有關的資訊;

會釋放并重建 <code>lua_scripts</code> 字典,關閉現有 Lua 環境并重建一個新的 Lua 環境;

SCRIPT EXISTS:

根通過檢查給定校驗和 SHA1 是否在的于 <code>lua_scripts</code> 字典的鍵,判斷對應的腳本是否存在伺服器中;

SCRIPT LOAD:

所做工作與 EVAL 指令前兩步一樣:定義腳本函數,将腳本函數儲存進 <code>lua_scripts</code> 字典;

SCRIPT KILL:

用于處理鈎子(hook)逾時的情況;

如果腳本未執行寫入操作,用戶端可以使用該指令停止執行腳本;

如果腳本已經進行過寫入操作,用戶端隻能使用 SHUTDOWN nosave 指令來停止伺服器,防止不合法的資料被寫入到資料庫中;

Redis | 第9章 Lua 腳本與排序《Redis設計與實作》

當伺服器運作在複制模式下時,具有寫性質的腳本指令會被複制到從伺服器裡,如:EVAL、EVALSHA、SCRIPT FLUSH、SCRIPTLOAD;

當主伺服器執行完 EVAL、SCRIPT FLUSH、SCRIPTLOAD 指令時,會直接将被執行的指令傳播給所有從伺服器;

Redis | 第9章 Lua 腳本與排序《Redis設計與實作》

EVALSHA 指令接受一個校驗和 SHA1 作為參數傳入;

每個服伺服器間的腳本校驗和 SHA1 可能不同,導緻在不同的伺服器間執行 EVALSHA 指令情況可能也不同;

Redis 複制 EVALSHA 指令的方法:

判斷傳播 EVALSHA 指令是否安全的方法:主伺服器使用 <code>repl_scriptcache_dict</code> 字典記錄哪些腳本傳播給了所有從伺服器。跟 <code>lua_scripts</code> 字典對比即可得知哪些校驗和還未傳播;

清空 repl_scriptcache_dict 字典:每當主伺服器添加一個新的從伺服器時,主伺服器會清空自己的 <code>repl_scriptcache_dict</code> 字典;

EVALSHA 指令轉換成 EVAL 指令的方法:根據 sha1 校驗和在 <code>lua_scripts</code> 字典中查找對應 Lua腳本 script;然後改寫指令,用 script 替換 sha1;其他參數不變;

EVAL指令:<code>EVAL script numkeys key [key ...] arg [arg ...]</code>

EVALSHA指令:<code>EVALSHA sha1 numkeys key [key ...] arg [arg ...]</code>

傳播 EVALSHA 指令的方法:當主伺服器成功執行完一個 EVALSHA 指令後,會根據該指令指定的 SHA1 校驗和是否存在于 <code>repl_scriptcache_dict</code> 字典裡來決定是否向從伺服器傳播 EVALSHA 指令還是 EVAL 指令;

Redis | 第9章 Lua 腳本與排序《Redis設計與實作》

伺服器執行 SORT numbers 指令的詳細步驟:

1)建立一個和 numbers 清單長度相同的數組,數組的每個項都是 <code>redis.h/redisSortObject</code> 結構;

2)周遊數組,将各個數組項的 obj 指針指向 numbers 清單的各個項,構成 obj 指針和清單項的一對一關系;

Redis | 第9章 Lua 腳本與排序《Redis設計與實作》

3)周遊數組,将各個 obj 指針所指向的清單項轉換成一個 double 類型的浮點數,并将浮點數儲存到相應數組項 <code>u.score</code> 屬性裡;

Redis | 第9章 Lua 腳本與排序《Redis設計與實作》

4)根據數組項 <code>u.score</code> 屬性的值,對數組進行數字值排序;

Redis | 第9章 Lua 腳本與排序《Redis設計與實作》

5)周遊數組,将各個數組項的 obj 指針所指向的清單項作為排序結果傳回給用戶端;

ALPHA 選項可以對包含字元串的鍵進行排列;

ASC 選項執行升序排序;

DESC 選項執行降序排序;

升序和降序都采用快速排序算法;

BY 選項可以指定某些字元串鍵,或者某個哈希鍵說包含的某些域來作為權重,對一個鍵排序;

LIMIT 選項可以讓結果隻傳回其中部分已排序的元素;

GET 選項可以在鍵被排序後,根據被排序的元素,以及 GET 選項指定的模式,查找并傳回某些鍵的值;

STORE選項可以将排序結果儲存在指定的鍵裡面;

選項的執行順序:

1)排序:會先使用 ALPHA、ASC、DESC、BY 這幾個選項,對輸入鍵進行排序,并得到一個排序結果集;

2)限制排序結果集的長度:使用 LIMIT 選項,對排序結果集的長度進行限制;

3)擷取外部鍵:使用 GET 選項,根據結果集的元素,以及 GET 指定的模式,查找并擷取指定鍵的值,并用這些值構成新的結果集;

4)儲存排序結果集:使用 STORE 選項,将排序結果集儲存到指定的鍵上;

5)向用戶端傳回排序結果集:周遊排序結果集,并依次向用戶端傳回排序結果集中的元素;

除了 GET 選項之外,改變選項的擺放順序不會影響 SORT 指令執行這些選項的順序;

新人制作,如有錯誤,歡迎指出,感激不盡!

歡迎關注公衆号,會分享一些更日常的東西!

如需轉載,請标注出處!

Redis | 第9章 Lua 腳本與排序《Redis設計與實作》