前言:
前面我們已經分析了如何實現分布式鎖,以及在實現的過程中分布式鎖存在的各種問題,並且提出了解決辦法,雖然我們上面看似實現了分布式鎖,但是卻存在一個致命問題,原子性問題,無論是獲取鎖還是釋放鎖,都是用多行Redis命令來實現,如果無法保證這個命令執行的原子性,則整個過程中就存在安全問題,在這里我們要引入另一門語言Lua,Lua腳本語言則可以用來解決多行Redis命令原子性問題,下面來看一下Lua腳本語言;
一、Redis中如何執行Lua腳本:
1.EVAL命令:

執行一個腳本包括參數:
script:腳本內容或者腳本地址;
numkeys:腳本中用到的key的數量,接下來的numkeys個數會作為key的參數,剩下的作為arg的參數;
arg:其他參數,會被存入腳本環境中的ARGV數組中,角標從1開始;
示例:EVAL "return 'hello world'" 0,其中
"return 'hello world'":就是腳本的內容,在這里這幾返回一個字符串;
0:也就是說沒有用到key參數,直接返回
效果:

2、SCRIPT LOAD命令:

將一個腳本編譯並且緩存起來,生成一個SHA1值並且返回,為了方便使用,參數script就是腳本內容貨地址;
一上面腳本為案例演示:

3、EVALSHA命令:

與EVAL類似,執行一段腳本,區別是通過腳本的sha1值執行,去腳本緩存中查詢,然后執行,參數:
sha1:就是腳本對應的sha1值;
示例:

二、Lua腳本的基本語法:
Lua的詳細語法可以參考網上的一些網站學習,列如:Lua菜鳥教程,學習任何一門語言都是從基本的如:變量、數據類型、循環、邏輯判斷、運算等入手的,Lua的語法與java有很多相似之處,具體的就不一一細說了,在這里只舉例一些最基本的用法:
1、聲明變量:
-- test.lua 文件腳本 a = 5 -- 全局變量 local b = 5 -- 局部變量
2、打印結果;
> print("Hello World!") Hello World! >
3、條件控制:
if(布爾表達式) then --[ 布爾表達式為 true 時執行該語句塊 --] else --[ 布爾表達式為 false 時執行該語句塊 --] end
4、循環語句:
while( true ) do print("循環將永遠執行下去") end
5、Lua調用Redis指令:
當我們在Redis中允許Lua腳本時,有一個內置變量redis,並且具備兩個函數:
redis.call("命令名稱",參數1,參數2):執行redis命令,執行遇到錯誤會直接返回;
redis.pcall("命令名稱",參數1,參數2):執行redis命令,遇到錯誤時,錯誤會以Lua腳本的方式返回;
例如:
redis.call('SET' ,'num' , '123'):執行這段腳本含義的的redis命令就是:set num 123;

不過我們編寫腳本的時候不希望把set后面的key和value寫死,而是可以由調用腳本的人來指定,把key和value做為參數傳入腳本中,在EVAL執行腳本是接受參數,key和arg,並且會用兩個內置變量(數格式)來接受用戶傳來的參數,像這樣:
KEYS:用來存放key參數;
ARGV:用來存放key以外的參數;
我們在腳本中,可以從數組中根據角標取出用戶傳入的參數,如下;
redis.call('SET', KEYS[1], ARGV[1]):
然后,我們執行腳本使可以動態key即需要存放的的value值:
EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 num 123:
