《Redis設計與實現》學習筆記-Lua指令碼

來源:互聯網
上載者:User

標籤:redis   高效能分布式緩衝   nosql   lua指令碼   eval   

Redis從2.6開始支援Lua指令碼,和事務的功能類似,可以通過Lua指令碼原子的執行多個Redis命令。Redis提供了EVAL和EVALSHA命令執行lua指令碼。
建立並修改Lua壞境

Redis在伺服器內嵌了一個Lua壞境,並進行了一系列的修改,從而確保這個Lua壞境可以滿足Redis伺服器的需要,通過下列步驟建立並修改Lua壞境:

  1. 建立一個基礎Lua壞境,通過調用Lua的C API函數lua_open。
  2. 載入多個函數庫到Lua壞境中,讓Lua指令碼可以使用這些函數來進行資料操作。包括Lua核心函數(assert、error、pairs、tostring、pcall等)、表格庫(比如table.concat、table.insert等)、字串庫(string.x)、數學庫(math.x)、調試庫(debug.x)、CJSON庫、Struct庫、cmsgpack庫。
  3. 建立全域表格redis,該表格包含了對Redis進行操作的函數,比如用於在Lua指令碼中執行Redis命令的redis.call和redis.pcall函數、用於記錄日誌的redis.log函數。
  4. 使用Redis自製的隨機函數替換Lua原有的副作用隨機函數,替換後的隨機函數對於相同的seed總會產生相同的隨機數序列。
  5. 建立排序輔助函數,Lua壞境使用這個輔助函數來對一部分Redis命令的結果進行排序,消除命令的不確定性,比如SMEMBERS這種命令多次執行可能會產生不同的結果,通過輔助函數來對結果進行排序讓它們每次產生同樣的結果。
  6. 建立redis.pcall函數的錯誤報表輔助函數,提供詳細的出錯資訊。
  7. 對Lua壞境中的全域變數進行保護,防止使用者在執行Lua指令碼的過程中,將額外的變數添加到Lua壞境中,不過Redis沒有禁止使用者修改已存在的全域變數,所以使用Lua指令碼的時候需要小心,以免錯誤的修改了已存在的全域變數。
  8. 將完成修改的Lua壞境儲存到伺服器狀態的lua屬性(redisServer結構的屬性)中,等待執行伺服器傳來的Lua指令碼。
Lua壞境協作組件Redis伺服器建立了兩個用於與Lua壞境進行協作的組件,它們分別是負責執行Lua指令碼中的Redis命令的偽用戶端,以及用於儲存Lua指令碼的lua_scripts字典。
偽用戶端偽用戶端負責處理Lua指令碼中包含的Redis命令,Lua指令碼使用redis.call或者redis.pcall函數執行一個Redis命令,需要以下步驟:
  1. Lua壞境將redis.call或者redis.pcall函數想要執行的命令傳給偽用戶端。
  2. 偽用戶端將指令碼想要執行的命令傳給命令執行器。
  3. 命令執行器執行偽用戶端傳給它的命令,並將命令的執行結果返回給用戶端。
  4. 偽用戶端接收命令執行器返回的命令結果,並將這個命令結果返回給Lua壞境。
  5. Lua壞境在接收到命令結果之後,將該結果返回給redis.call或者redis.pcall。
  6. 接收到結果的redis.call或redis.pcall函數將命令結果作為函數返回值返回給指令碼的調用者。
lua_scripts字典作為一個屬性存放在redisServer結構中,字典的鍵為Lua指令碼的SHA1校正和,而字典的值時SHA1校正和對應的Lua指令碼,Redis伺服器會將所有被EVAL命令執行過的Lua指令碼,以及所有被SCRIPT LOAD命令載入過的Lua指令碼都儲存到lua_scripts字典裡面。
EVAL命令EVAL命令實現分為三個步驟:
1、根據用戶端給定的Lua指令碼,在Lua壞境中定義一個Lua函數,函數名稱由f_首碼加上指令碼的SHA1校正和(四十個字元)組成,函數體為指令碼本身。
2、將用戶端給定的指令碼儲存到lua_scripts字典中。
3、執行步驟1中定義的函數,以此來執行用戶端給定的Lua指令碼,過程如下:

  • 將EVAL命令中傳入的鍵名參數和指令碼參數分別儲存到KEYS數組和ARGV數組,然後將這兩個數組作為全域變數傳入到Lua壞境裡面。
  • 為Lua壞境裝載逾時處理鉤子,這個鉤子可以在指令碼出現逾時啟動並執行情況時,讓用戶端通過SCRIPT KILL命令停止指令碼,或者通過SHUTDOWN命令直接關閉伺服器。
  • 執行指令碼函數。
  • 移除之前裝載的逾時鉤子。
  • 將執行指令碼函數所得的結果儲存到用戶端狀態的輸出緩衝區中,等待伺服器將結果返回給用戶端。
  • 對Lua壞境執行記憶體回收操作。

EVALSHA命令

每個被EVAL命令成功執行過的Lua指令碼都會產生一個SHA1校正和,為了節省頻寬資源,在下次執行相同的指令碼時可以使用EVALSHA命令,把之前產生的校正和傳到伺服器就可以了。

指令碼管理命令
  • SCRIPT FLUSH,用於清除伺服器中所有和Lua指令碼有關的資訊:釋放lua_scripts字典,關閉現有的Lua壞境並重新建立一個Lua壞境。
  • SCRIPT EXISTS,根據輸入的SHA1校正和,檢查對應的指令碼是否存在,通過檢查lua_scripts字典。
  • SCRIPT LOAD,將指令碼儲存到lua_scripts字典中。
  • SCRIPT KILL,當伺服器設定了lua-time-limit配置選項,每次執行lua指令碼之前,伺服器都會在Lua壞境中設定一個逾時鉤子,逾時鉤子在指令碼運行期間檢查,一旦指令碼的執行時間超過了lua-time-limit,鉤子將定期在指令碼啟動並執行間隙中查看是否有SCRIPT KILL或者SHUTDOWN命令。如果逾時的指令碼未執行過任何寫入操作,那麼用戶端可以通過SCRIPT KILL來指示伺服器停止執行這個指令碼,如果執行過寫入操作,用戶端只能用SHUTDOWN nosave命令停止伺服器,防止不合法的資料寫入資料庫。
指令碼複製當伺服器運行在複製模式下,EVAL命令、SCRIPT FLUSH、SCRIPT LOAD、EVALSHA命令會複製給從伺服器,除了EVALSHA命令,其它三個命令都是直接傳播,在傳播EVALSHA命令,先檢查從伺服器是否載入過這個命令,如果載入過了直接複製,如果未載入過先要把EVALSHA命令轉換成等價的EVAL命令再傳播。

《Redis設計與實現》學習筆記-Lua指令碼

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.