1. 程式人生 > >Redis之使用lua指令碼操作redis資料庫的原因

Redis之使用lua指令碼操作redis資料庫的原因

前言

為什麼要用lua指令碼操作redis資料庫?
1.減少開銷–減少向redis伺服器的請求次數
2.原子操作–redis將lua指令碼作為一個原子執行
3.可複用–其他客戶端可以使用已經執行過的lua指令碼
4.增加redis靈活性–lua指令碼可以幫助redis做更多的事情

lua指令碼本身體積小,啟動速度快.

因此,從redis 2.6.0開始,redis在伺服器端內建lua直譯器

EVAL命令語法

EVAL script numkeys key [key …] arg [arg …]

EVAL —lua程式的執行環境上下文
script —lua指令碼
numkeys —引數的個數(key的個數)
key —redis鍵 訪問下標從1開始,例如:KEYS[1]
arg —redis鍵的附加引數

lua指令碼

EVAL和EVALSHA用redis內建的lua編譯器執行指令碼
舉例說明:

123456 127.0.0.1:6379> eval "return {KEYS[1],KEYS[2],ARGV[1],ARGV[2]}" 2 username password test 1234561) "username"2) "password"3) "test"4) "123456"127.0.0.1:6379>

上面lua指令碼的意思是返回以lua陣列的形式返回key1,key2和value1,value2,2是key的個數.

lua函式

主要有兩個函式來執行redis命令
redis.call() – 出錯時返回具體錯誤資訊,並且終止指令碼執行
redis.pcall() –出錯時返回lua table的包裝錯誤,但不引發錯誤
舉例說明:

123 127.0.0.1:6379> eval "return redis.call('set',KEYS[1],ARGV[1])" 1 name redisOK127.0.0.1:6379>

該指令碼中的函式作用是類似於執行 set name redis 的redis命令.並返回執行結果,ok

redis.call()出錯時:

123 127.0.0.1:6379> eval "return redis.call('get',KEYS[1],ARGV[1])" 1 name redis(error) ERR Error running script (call to f_b943d620b079a29d99eccaaa7317e05f8eb8ce88): @user_script:1: @user_script: 1: Wrong number of args calling Redis command From Lua script 127.0.0.1:6379>

redis.pcall()出錯時:

123 127.0.0.1:6379> eval "return redis.pcall('get',KEYS[1],ARGV[1])" 1 name redis(error) @user_script: 1: Wrong number of args calling Redis command From Lua script127.0.0.1:6379>

lua與redis資料型別轉換

lua通過redis.call()或者redis.pcall()函式執行redis命令的返回值被轉換成了lua資料結構,當然了,當lua指令碼在redis的內建直譯器裡執行時,lua指令碼的返回值也會被轉換成redis資料結構,然後由EVAL將值返回給客戶端.

那麼lua和redis資料型別之間時如何轉換的呢?對應關係又是怎樣的呢?

redis資料型別 lua資料型別
integer number
bulk string
multi bulk table
status 包含ok域的table
error 包含err域的table
nil bulk false

從redis資料型別到lua資料型別或者從lua資料型別到redis資料型別,都有以上對應規則,但是從

從lua轉換到redis有一條額外的對應規則

  • lua boolean true –> redis 1
    即,lua的true對應redis 的整型1.

EVAL和EVALSHA

EVAL命令在每次執行指令碼時,都發送一次指令碼主體,而EVALSHA並非如此,它的第一個引數時指令碼的sha1校驗和.

EVALSHA命令的機制如下:

  • 如果伺服器記得SHA1校驗和指定的指令碼,那麼執行該指令碼
  • 如果伺服器不記得SHA1校驗和指定的指令碼,那麼它返回一個錯誤,提醒使用者使用EVAl代替EVALSHA

因此在指令碼主體不變的情況下使用EVALSHA,可以使指令碼複用,而節省頻寬

lua指令碼要求

指令碼需要被寫成純函式

對於同樣的資料輸入,給定相同的引數,指令碼執行的redis寫命令的結果總是相同的.
為此,redis做了以下事情:

  • lua沒有訪問系統時間或者其他內部狀態的命令
  • redis阻止上面所提到的指令碼執行
  • lua指令碼呼叫返回序命令的返回資料會被排序(字典序.)
  • 對 Lua 的偽隨機數生成函式 math.random 和 math.randomseed 進行修改,使得每次在執行新指令碼的時候,總是擁有同樣的 seed 值.

不允許建立全域性變數

為了防止資料洩露進lua環境,redis指令碼不循序建立全域性變數.

訪問一個全域性變數(無論是否存在)都會引起指令碼停止

總結

使用lua操作redis資料庫能夠帶來很多便利,後續將提供例項展示lua指令碼是如何操作redis資料庫的.