失蹤人口回來啦!git
有讀者問我爲何這麼久都沒有出Redis Lua中學教程,表示村頭廁所已經很久沒有紙了。其實我早就要寫這篇中學教程了,奈何最近太忙了,就一拖再拖,直到今天我終於又開始動筆了。忘記Lua相關概念的同窗能夠先回顧一下小學教程。github
中學教程主要分爲兩部分:Redis Lua的相關命令詳解和Lua的語法介紹。redis
前面咱們簡單介紹了EVAL和EVALSHA命令。可是隻有那點只是是沒辦法從中學畢業的,所以咱們須要進行更深刻的學習。json
最先可用版本:2.6.0數組
用法:EVAL script numkeys key [key ...] arg [arg ...]緩存
關於用法咱們已經演示過了,其中第一個參數是要執行的Lua腳本,第二個參數是傳入腳本的參數個數。後面則是參數的key數組和value數組。bash
在Lua中執行Redis命令的方法咱們也介紹過,就是使用redis.call()和redis.pcall()兩個函數。它們之間惟一的不一樣就是當Redis命令執行錯誤時,redis.call()會拋出這個錯誤,使EVAL命令拋出錯誤,而redis.pcall()會捕獲這個錯誤,並返回Lua的錯誤表。服務器
一般咱們約定執行命令的key都須要由參數傳入,命令必須在執行以前進行分析,以肯定它做用於哪一個key。這樣作的目的是爲了在必定程度上保證EVAL執行的Lua腳本的正確性。dom
在Redis執行EVAL命令時,若是腳本中有call()或者pcall()命令,就會涉及到Redis和Lua之間數據類型轉換的問題。轉換規則要求,一個Redis的返回值轉換成Lua數據類型後,再轉換成Redis數據類型,其結果必須和初始值相同。因此每種類型是一一對應的。轉換規則以下:異步
Redis | Lua |
---|---|
integer | number |
bulk | string |
multi bulk | table |
status | table with a single ok field |
error | table with a single err field |
Nil bulk &Nil multi bulk | false boolean type |
除此以外,Lua到Redis的轉換還有一些其餘的規則:
來個栗子驗證一下:
EVAL "return {1,2,3.3333,'foo',nil,'bar'}" 0
1) (integer) 1
2) (integer) 2
3) (integer) 3
4) "foo"
複製代碼
能夠看到bar沒有返回,而且3.333返回了3。
Redis運行全部的Lua命令都使用相同的Lua解釋器。當一個腳本正在執行時,其餘的腳本或Redis命令都不能執行。這很像Redis的事務multi/exec。這意味着咱們要儘可能避免腳本的執行時間過長。
當腳本進行傳播或者寫入AOF文件時,Redis一般會將腳本自己進行傳播或寫入AOF,而不是使用它產生的若干命令。緣由很簡單,傳播整個腳本要比傳播一大堆生成的命令的速度要快。
從Redis3.2開始,能夠只複製影響腳本執行結果的語句,而不用複製整個腳本。這個複製整個腳本的方法有如下屬性:
在這種模式下,Redis在執行腳本時會收集全部影響數據集的命令,當腳本執行完畢時,命令隊列會被放在事務中,發送給AOF文件。
Lua能夠經過執行redis.replicate_commands()函數來檢查複製模式,若是返回true表示當前是複製命令模式,若是返回false,則是複製整個腳本模式。
腳本複製模式選擇好之後,就能夠對複製到副本和AOF的方式進行更多的控制。這是一種高級特性,由於濫用會切斷主從備份,和AOF持久化。若是咱們只須要在master上執行某些命令時,這一特性就變得頗有用。例如咱們須要計算一些中間值時,只須要在master上計算就好,那麼這些命令就沒必要進行復制。
從Redis3.2開始,有一個新的命令叫作redis.set_repl(),它能夠用來控制複製方式,有以下選項(默認是REPL_ALL):
redis.set_repl(redis.REPL_ALL) -- Replicate to AOF and replicas.
redis.set_repl(redis.REPL_AOF) -- Replicate only to AOF.
redis.set_repl(redis.REPL_REPLICA) -- Replicate only to replicas (Redis >= 5)
redis.set_repl(redis.REPL_SLAVE) -- Used for backward compatibility, the same as REPL_REPLICA.
redis.set_repl(redis.REPL_NONE) -- Don't replicate at all. 複製代碼
爲了不數據泄露,Redis腳本不容許建立全局變量。若是必須有一個公共變量,可使用Redis的key來代替。在EVAL命令中建立一個全局變量會引發一個異常。
> eval 'a=10' 0
(error) ERR Error running script (call to f_933044db579a2f8fd45d8065f04a8d0249383e57): user_script:1: Script attempted to create global variable 'a 複製代碼
在Lua腳本中使用SELECT就像在正常客戶端中使用同樣。值得一提的是,在Redis2.8.12以前,Lua腳本中執行SELECT是會影響到客戶端的,而從2.8.12開始,Lua腳本中的SELECT只會在腳本執行過程當中生效。這點在Redis版本升級時須要注意,由於升級先後,命令的語義會改變。
Lua腳本中有許多庫,但並非都能在Redis中使用,其中可使用的有:
base
lib.table
lib.string
lib.math
lib.struct
lib.cjson
lib.cmsgpack
lib.bitop
lib.redis.sha1hex
function.redis.breakpoint and redis.debug
function in the context of the Redis Lua debugger.struct, CJSON and cmsgpack是外部庫,其餘的都是Lua的標準庫。
使用redis.log(loglevel,message)函數能夠在Lua腳本中打印Redis日誌。
loglevel包括:
它們與Redis的日誌等級是對應的。
腳本不該該訪問外部系統,包括文件系統和其餘系統。腳本應該只能操做Redis數據和傳入進來的參數。
腳本默認的最大執行時間是5秒(正常腳本執行時間都是毫秒級,因此5秒已經足夠長了)。能夠經過修改lua-time-limit變量來控制最大執行時間。
當腳本執行時間超過最大執行時間時,並不會被自動終止,由於這違反了腳本的原子性原則。當一個腳本執行時間過長時,Redis會有以下操做:
最先可用版本:2.6.0
用法:EVALSHA sha1 numkeys key [key ...] arg [arg ...]
該命令用來執行緩存在服務器上的腳本,sha1爲腳本的惟一標識。
使用EVAL命令必須每次都要把腳本從客戶端傳到服務器,因爲Redis的內部緩存機制,它並不會每次都從新編譯腳本,可是傳輸上仍然浪費帶寬。
另外一方面,若是使用特殊命令或者經過redis.conf來定義命令會有如下問題:
爲了不這些問題,同時避免浪費帶寬,Redis實現了EVALSHA命令。
若是服務器中沒有緩存指定的腳本,會返回給客戶端腳本不存在的錯誤信息。
最先可用版本:3.2.0
時間複雜度:O(1)
用法:SCRIPT DEBUG YES|SYNC|NO
該命令用於設置隨後執行的EVAL命令的調試模式。Redis包含一個完整的Lua調試器,代號爲LDB,可使編寫複雜腳本的任務更加簡單,在調試模式下,Redis充當遠程調試服務器,客戶端能夠逐步執行腳本,設置斷點,檢查變量等。想了解更多調試器內容的能夠查看官方文檔Redis Lua debugger。
LDB能夠設置成異步或同步模式。異步模式下,服務器會fork出一個調試會話,不會阻塞主會話,,調試會話結束後,全部數據都會回滾。同步模式則會阻塞會話,並保留調試過程當中數據的改變。
最先可用版本:2.6.0
時間複雜度:O(N),N是腳本數量
返回腳本是否存在於緩存中(存在返回1,不存在返回0)。這個命令適合在管道前執行,以保證管道中的全部腳本都已經加載到服務器端了,若是沒有,須要用SCRIPT LOAD命令進行加載。
最先可用版本:2.6.0
時間複雜度:O(N),N是緩存中的腳本數
刷新緩存中的腳本,這一命令常在雲服務上被使用。
最先可用版本:2.6.0
時間複雜度:O(1)
中止當前正在執行的Lua腳本,一般用來中止執行時間過長的腳本。中止後,被阻塞的客戶端會拋出一個錯誤。
最先可用版本:2.6.0
時間複雜度:O(N),N是腳本的字節數
該命令用於將腳本加載到服務器端的緩存中,但不會執行。加載後,服務器會一直緩存,由於良好的應用程序不太可能有太多不一樣的腳本致使內存不足。每一個腳本都像一個新命令的緩存,因此即便是大型應用程序,也就有幾百個,它們佔用的內存是微不足道的。
本文介紹了Redis Lua相關的命令。其中EVAL和EVALSHA用來執行腳本。腳本執行具備原子性。腳本的複製和傳播能夠根據須要設置。腳本中不能定義全局變量。