天天看點

Redis進階實踐之七Redis和Lua初步整合使用

一、引言

        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的數字類型會被自動轉換成整數)

        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)

       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指令情況腳本緩存。

       6、SCRIPT KILL(該指令不區分大小寫)

              強制終止目前腳本的執行。 但是,如果目前執行的腳步對redis的資料進行了寫操作,則SCRIPT KILL指令不會終止腳本的運作,以防止腳本隻執行了一部分。腳本中的所有指令,要麼都執行,要麼都不執行。

       7、lua-time-limit 5000(redis.conf配置檔案中)

              為了防止某個腳本執行時間過長導緻Redis無法提供服務(比如陷入死循環),Redis提供了lua-time-limit參數限制腳本的最長運作時間,預設為5秒鐘。當腳本運作時間超過這一限制後,Redis将開始接受其他指令但不會執行(以確定腳本的原子性,因為此時腳本并沒有被終止),而是會傳回“BUSY”錯誤。

五、安裝和使用Lua腳本

       1、安裝lua類庫環境

              1.1、yum install -y readline

              1.2、yum install -y readline-devel

Redis進階實踐之七Redis和Lua初步整合使用

        2、下載下傳lua最新的版本安裝

                2.1、去官網下載下傳lua,可以直接通過wget下載下傳,位址如下:http://www.lua.org/download.html

Redis進階實踐之七Redis和Lua初步整合使用

               2.2、通過ssh SSH Secure File Transfer Client工具,把軟體包上傳到Linux伺服器上。目錄是:/root/software/download/lua/

Redis進階實踐之七Redis和Lua初步整合使用

                2.3、進入到已經解壓的目錄lua-5.3.4,準備安裝檔案。

             2.4、準備安裝環境,使用make linux指令,目前也是需要gcc指令的支援,事先必須安裝,安裝gcc指令:yum install gcc。

Redis進階實踐之七Redis和Lua初步整合使用

            2.5、開始安裝lua軟體包,使用make install指令

Redis進階實踐之七Redis和Lua初步整合使用

           2. 6、最後進行測試,進到Linux的指令行,然後輸入lua指令,開始測試。

Redis進階實踐之七Redis和Lua初步整合使用

            2.7、按Ctrl+C退出lua指令模式。

           2.8、lua腳本檔案名必須以.lua字尾名,如果在Linux指令行執行lua腳本,直接lua 腳本名稱。

           2.9、redis與lua腳本結合使用,如果在lua腳本裡使用了 redis.call指令來操作Redis,執行lua腳步如下面:

            2.10、Redis用戶端執行帶有參數的lua腳本,腳本檔案的名稱是:03.lua。

             2.11、Redis用戶端執行有參數lua,并傳回lua的表類型。

六、總結

   今天就寫到這裡,還有很多内容要寫,一次也寫不完,慢慢來吧,我也需要消化一下。如果有時間,下一篇文章或者下某一篇文章準備來單獨寫一些關于Lua腳本文法的東西,我也是第一次接觸這個東西,大家一起學習吧。

繼續閱讀