玩轉 lua in Redis

來源:互聯網
上載者:User

一、引言

        Redis學了一段時間了,基本的東西都沒問題了。從今天開始講寫一些redis和lua指令碼的相關的東西,lua這個指令碼是一個好東西,可以運行在任何平台上,也可以嵌入到大多數語言當中,來擴充其功能。lua指令碼是用C語言寫的,體積很小,運行速度很快,並且每次的執行都是作為一個原子事務來執行的,我們可以在其中做很多的事情。由於篇幅很多,一次無法概述全部,這個系列可能要通過多篇文章的形式來寫,好了,今天我們進入正題吧。

二、Lua簡介
    
        Lua 是一個小巧的指令碼語言。是巴西裡約熱內盧天主教大學(Pontifical Catholic University of Rio de Janeiro)裡的一個研究小組,由Roberto Ierusalimschy、Waldemar Celes 和 Luiz Henrique de Figueiredo所組成並於1993年開發。 其設計目的是為了嵌入應用程式中,從而為應用程式提供靈活的擴充和定製功能。Lua由標準C編寫而成,幾乎在所有作業系統和平台上都可以編譯,運行。Lua並沒有提供強大的庫,這是由它的定位決定的。所以Lua不適合作為開發獨立應用程式的語言。Lua 有一個同時進行的JIT項目,提供在特定平台上的即時編譯功能。

       Lua指令碼可以很容易的被C/C++ 代碼調用,也可以反過來調用C/C++的函數,這使得Lua在應用程式中可以被廣泛應用。不僅僅作為擴充指令碼,也可以作為普通的設定檔,代替XML,ini等檔案格式,並且更容易理解和維護。 Lua由標準C編寫而成,代碼簡潔優美,幾乎在所有作業系統和平台上都可以編譯,運行。一個完整的Lua解譯器不過200k,在目前所有指令碼引擎中,Lua的速度是最快的。這一切都決定了Lua是作為嵌入式指令碼的最佳選擇。

三、使用Lua指令碼的好處

       1、減少網路開銷:可以將多個請求通過指令碼的形式一次發送,減少網路時延和請求次數。

       2、原子性的操作:Redis會將整個指令碼作為一個整體執行,中間不會被其他命令插入。因此在編寫指令碼的過程中無需擔心會出現競態條件,無需使用事務。

       3、代碼複用:用戶端發送的腳步會永久存在redis中,這樣,其他用戶端可以複用這一指令碼來完成相同的邏輯。

       4、速度快:見 與其它語言的效能比較, 還有一個 JIT編譯器可以顯著地提高多數任務的效能; 對於那些仍然對效能不滿意的人, 可以把關鍵區段使用C實現, 然後與其整合, 這樣還可以享受其它方面的好處。

       5、可以移植:只要是有ANSI C 編譯器的平台都可以編譯,你可以看到它可以在幾乎所有的平台上運行:從 Windows 到Linux,同樣Mac平台也沒問題, 再到移動平台、遊戲主機,甚至瀏覽器也可以完美使用 (翻譯成JavaScript).

       6、源碼小巧:20000行C代碼,可以編譯進182K的可執行檔,載入快,運行快。

四、redis和lua整合詳解

      1、調用Lua指令碼的文法:
              $ redis-cli --eval path/to/redis.lua KEYS[1] KEYS[2] , ARGV[1] ARGV[2] ...

              --eval,告訴redis-cli讀取並運行後面的lua指令碼
               path/to/redis.lua,是lua指令碼的位置
               KEYS[1] KEYS[2],是要操作的鍵,可以指定多個,在lua指令碼中通過KEYS[1], KEYS[2]擷取
               ARGV[1] ARGV[2],參數,在lua指令碼中通過ARGV[1], ARGV[2]擷取。

              注意: KEYS和ARGV中間的 ',' 兩邊的空格,不能省略。

             redis支援大部分Lua標準庫

庫名 說明
Base 提供一些基礎函數
String 提供用於字串操作的函數
Table 提供用於表操作的函數
Math 提供數學計算函數
Debug 提供用於調試的函數


       2、在指令碼中調用redis命令
               在指令碼中可以使用redis.call函數調用Redis命令

              redis.call('set', 'foo', 'bar')
              local value=redis.call('get', 'foo') --value的值為bar

              redis.call函數的傳回值就是Redis命令的執行結果

              Redis命令的傳回值有5種類型,redis.call函數會將這5種類型的回複轉換成對應的Lua的資料類型,具體的對應規則如下(空結果比較特殊,其對應Lua的false)

               redis傳回值類型和Lua資料類型轉換規則

redis傳回值類型 Lua資料類型
整數回複 數字類型
字串回複 字串類型
多行字串回複 table類型(數組形式)
狀態回複 table類型(只有一個ok欄位儲存狀態資訊)
錯誤回複 table類型(只有一個err欄位儲存錯誤資訊)


                  redis還提供了redis.pcall函數,功能與redis.call相同,唯一的區別是當命令執行出錯時,redis.pcall會記錄錯誤並繼續執行,而redis.call會直接返回錯誤,不會繼續執行。在指令碼中可以使用return語句將值返回給用戶端,如果沒有執行return語句則預設返回nil

                  Lua資料類型和redis傳回值類型轉換規則

Lua資料類型 redis傳回值類型
數字類型 整數回複(Lua的數字類型會被自動轉換成整數)
字串類型 字串回複
table類型(數組形式) 多行字串回複
table類型(只有一個ok欄位儲存狀態資訊) 狀態回複
table類型(只有一個err欄位儲存錯誤資訊) 錯誤回複


        3、指令碼相關命令
             EVAL文法: eval script numkeys key [key ...] arg [arg ...]

              通過key和arg這兩類參數向指令碼傳遞資料,它們的值在指令碼中分別使用KEYS和ARGV兩個表類型的全域變數訪問。

              script: 是lua指令碼

              numkeys:表示有幾個key,分別是KEYS[1],KEYS[2]...,如果有值,從第numkeys+1個開始就是參數值,ARGV[1],ARGV[2]...

             注意: EVAL命令依據參數numkeys來將其後面的所有參數分別存入指令碼中KEYS和ARGV兩個table類型的全域變數。當指令碼不需要任何參數時,也不能省略這個參數(設為0)

       192.168.127.128:6379>eval "return redis.call('set',KEYS[1],ARGV[1])" 1 name liulei       OK       192.168.127.128:6379>get name       "liulei"


       4、 EVALSHA命令
              在指令碼比較長的情況下,如果每次呼叫指令碼都需要將整個指令碼傳給Redis會佔用較多的頻寬。為瞭解決這個問題,Redis提供了EVALSHA命令,允許開發人員通過指令碼內容的SHA1摘要來執行指令碼,該命令的用法和EVAL一樣,只不過是將指令碼內容替換成指令碼內容的SHA1摘要。

             Redis在執行EVAL命令時會計算指令碼的SHA1摘要並記錄在指令碼緩衝中,執行EVALSHA命令時Redis會根據提供的摘要從指令碼緩衝中尋找對應的指令碼內容,如果找到了則執行指令碼,否則會返回錯誤:"NOSCRIPT No matching script. Please use EVAL."

             在程式中使用EVALSHA命令的一般流程如下。

                 1)、先計算指令碼的SHA1摘要,並使用EVALSHA命令執行指令碼。

                 2)、獲得傳回值,如果返回“NOSCRIPT”錯誤則使用EVAL命令重新執行指令碼。

              雖然這一流程略顯麻煩,但值得慶幸的是很多程式設計語言的Redis用戶端都會代替開發人員完成這一流程。執行EVAL命令時,先嘗試執行EVALSHA命令,如果失敗了才會執行EVAL命令。

               SCRIPTLOAD "lua-script"  將指令碼加入緩衝,但不執行, 返回:指令碼的SHA1摘要

               SCRIPT EXISTS lua-script-sha1   判斷指令碼是否已被緩衝

       5、 SCRIPT FLUSH(該命令不區分大小寫)
               清空指令碼緩衝,redis將指令碼的SHA1摘要加入到指令碼緩衝後會持續保留,不會刪除,但可以手動使用SCRIPT FLUSH命令清除全部指令碼緩衝。

       192.168.127.128:6379>script flush       OK       192.168.127.128:6379>SCRIPT FLUSH       OK


       6、SCRIPT KILL(該命令不區分大小寫)
              強制終止當前指令碼的執行。 但是,如果當前執行的腳步對redis的資料進行了寫操作,則SCRIPT KILL命令不會終止指令碼的運行,以防止指令碼只執行了一部分。指令碼中的所有命令,要麼都執行,要麼都不執行。

       192.168.127.128:6379>script kill      (error)NOTBUSY No scripts in execution right now      192.168.127.128:6379>SCRIPT KILL      (error)NOTBUSY No scripts in execution right now       //這是當前沒有指令碼在執行,所以提示該錯誤


       7、lua-time-limit 5000(redis.conf設定檔中)

              為了防止某個指令碼執行時間過長導致Redis無法提供服務(比如陷入死迴圈),Redis提供了lua-time-limit參數限制指令碼的最長已耗用時間,預設為5秒鐘。當指令碼已耗用時間超過這一限制後,Redis將開始接受其他命令但不會執行(以確保指令碼的原子性,因為此時指令碼並沒有被終止),而是會返回“BUSY”錯誤。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.