2、為什麼需要Lua指令碼?
簡而言之:效能提升。您在Redis中執行的大多數任務都涉及許多步驟。您可以使用Lua在Redis內部進行操作,而不必使用應用程式語言來執行這些步驟。
這可能會導致更好的效能。同樣,指令碼中的所有步驟都以原子方式執行。執行指令碼時,無法執行其他Redis命令。例如,我使用Lua指令碼改變儲存在Redis的JSON字串。我將在本文後半部分對此進行詳細描述。
3、可是我什麼都不知道一個不認識Lua的人
別擔心,Lua並不是很難理解。如果您瞭解C語言系列中的任何語言,那麼您應該很容易上手Lua。另外,我在本文中提供了程式碼示例。
4、給我看個例子讓我們開始透過redis-cli執行指令碼。從以下內容開始:
redis-cli
現在執行以下命令:
eval “redis.call(‘set’, KEYS[1], ARGV[1])” 1 key:name value
該EVAL命令就是告訴Redis的執行下面的指令碼。該”redis.call(‘set’, KEYS[1], ARGV[1])” 字串是我們的指令碼,其功能與Redis的set命令相同。指令碼文字後面跟隨三個引數:
鍵的個數鍵名鍵值指令碼引數分為兩類:KEYS和ARGV。
我們用緊隨其後的數字指定指令碼需要多少個鍵。在我們的示例中,該值為1。在此編號之後,我們需要立即接這些key。它們可以作為指令碼中的KEYS表訪問。在我們的例子中,它key:name在索引1處包含一個值。
注意,Lua索引表從索引 1開始,而不是0。
我們可以在鍵之後提供任意數量的引數,這些引數可以在Lua中作為ARGV表使用。在此示例中,我們提供了一個ARGV引數:string value。您已經猜到了,上面的命令將鍵設定key:name為value value。
提供指令碼使用的key作為KEYS以及提供所有其他引數作為ARGV是一種好習慣。因此,您不應該將KEYS指定為0,然後在ARGV表中提供所有key。
現在讓我們檢查指令碼是否成功完成。我們將透過執行另一個從Redis獲取金鑰的指令碼來做到這一點:
eval “return redis.call(‘get’, KEYS[1])” 1 key:name
輸出應該為”value”,這意味著先前的指令碼成功設定了鍵值 “key:name”`。
5、你能解釋一下指令碼嗎?我們的第一個指令碼包含一個語句:redis.call函式:
redis.call('set',KEYS [1],ARGV [1])
使用redis.call它可以執行任何Redis命令。第一個引數是此命令的名稱,後跟其引數。對於set命令,這些引數是key和value。支援所有Redis命令。根據文件:
Redis使用相同的Lua直譯器來執行所有命令
我們的第二個指令碼不僅僅執行一個命令,它還返回一個值:
eval “return redis.call(‘get’, KEYS[1])” 1 key:name
指令碼返回的所有內容都發送到呼叫過程。在我們的情況下,此過程為redis-cli,您將在終端視窗中看到結果。
6、還有更復雜的東西嗎?我曾經使用Lua指令碼以特定順序從雜湊對映中返回元素。順序是儲存在有序集order鍵中。
首先,透過在redis-cli中執行以下命令來設定資料:
hmset hkeys key:1 value:1 key:2 value:2 key:3 value:3 key:4 value:4 key:5 value:5 key:6 value:6zadd order 1 key:3 2 key:1 3 key:2
這條命令處建立了一個名為:hkeys的雜湊對映,filed名為:key:XXX ,order其中包含hkeys按特定順序從中選擇filed。
您可能需要檢視hmset和zadd命令參考以獲取詳細資訊。
讓我們執行以下指令碼:
eval “local order = redis.call(‘zrange’, KEYS[1], 0, -1); return redis.call(‘hmget’,KEYS[2],unpack(order));” 2 order hkeys
您應該看到以下輸出:
“value:3”“value:1”“value:2”
這意味著我們以正確的順序獲得了所需hash filed的值。
7、是否必須指定完整的指令碼文字才能執行它?Redis允許您使用SCRIPT LOAD命令將指令碼預載入到記憶體中:
script load “return redis.call(‘get’, KEYS[1])”
您應該看到如下輸出:
“4e6d8fc8bb01276962cce5371fa795a7763657ae”
這是您需要提供給EVALSHA命令以執行指令碼的指令碼的唯一雜湊:
evalsha 4e6d8fc8bb01276962cce5371fa795a7763657ae 1 key:name
注意:您應該使用SCRIPT LOAD命令返回的實際SHA1雜湊,上面的雜湊只是一個示例。
8、更改JSON的內容有時人們在Redis中儲存JSON物件。可以檢視我前面一篇文章的講解《Redis使用字串和hash儲存JSON,哪個更高效?》。
如果必須在此JSON物件中更改key,則需要從Redis中獲取key的value值,然後對其進行解析,更改key的value值,然後進行序列化並將其設定回Redis。這種方法存在兩個問題:
併發。另一個過程可以在我們的get和set操作之間更改此JSON。在這種情況下,更改將丟失。效能。如果您經常進行這些更改,並且json物件很大,則可能成為應用程式的瓶頸。您可以透過在Lua中實現此邏輯來提高一些效能。讓我們在key下的Redis中新增一個測試JSON字串obj:
set obj ‘{“a”:”foo”,”b”:”bar”}’
現在執行指令碼:
EVAL ‘local obj = redis.call(“get”,KEYS[1]); local obj2 = string.gsub(obj,”(“ .. ARGV[1] .. “\”:)([^,}]+)”, “%1” .. ARGV[2]); return redis.call(“set”,KEYS[1],obj2);’ 1 obj b bar2
現在我們將在key下具有以下物件obj:
{“ a”:“ foo”,“ b”:“ bar2”}
您可以使用SCRIPT LOAD命令來載入此指令碼,然後像這樣執行它:
EVALSHA <your_script_sha> 1 obj b bar2
一些注意事項:
1、指令碼中..是Lua中字串連線運算子。2、我們使用RegEx模式來匹配鍵並替換其值。3、Lua RegEx風味與大多數其他風格的不同之處在於,我們%同時將它們用作RegEx特殊符號的回溯標記和跳脫字元。9、我應該一直使用Lua指令碼嗎?不。我建議僅在可以證明它可以帶來更好的效能時才使用它們。首先都要做基準測試,確定性能會提高才需要使用lua指令碼。如果您只需要原子性,則應改為檢查Redis事務。
另外,您的指令碼不應太長。請記住,指令碼執行時,其他所有內容都在等待指令碼完成。如果您的指令碼需要花費一些時間,則可能會導致瓶頸,而不是提高效能。指令碼在達到超時(預設為5秒)後停止。有關Lua的更多資訊,請訪問lua.org。