Redis 從 2.6 版本開始引入使用 Lua 編程語言進行的服務器端腳本編程功能,這個功能可讓用戶直接在 Redis 內部執行各類操做,從而達到簡化代碼並提升性能的做用。 P248
git
P248
經過使用 Lua 對 Redis 進行腳本編程,咱們能夠避免一些減慢開發速度或者致使性能降低對常見陷阱。 P248
github
P249
SCRIPT LOAD
命令能夠將腳本載入 Redis ,這個命令接受一個字符串格式的 Lua 腳本爲參數,它會把腳本存儲起來等待以後使用,而後返回被存儲腳本的 SHA1 校驗和EVALSHA
命令能夠調用以前存儲的腳本,這個命令接收腳本的 SHA1 校驗和以及腳本所需的所有參數EVAL
命令能夠直接執行指定的腳本,這個命令接收腳本字符串以及腳本所需的所有參數。這個命令除了會執行腳本以外,還會將被執行的腳本緩存到 Redis 服務器裏面因爲 Lua 的數據傳入和傳出限制, Lua 與 Redis 須要進行相互轉換。由於腳本在返回各類不一樣類型的數據時可能會產生含糊不清的結果,因此咱們應該儘可能顯式的返回字符串。 P250
redis
咱們能夠在 官方文檔 中找到 Redis 和 Lua 不一樣類型之間的轉換表:數據庫
Redis 類型轉換至 Lua 類型編程
Redis | Lua |
---|---|
integer reply | number |
bulk reply | string |
multi bulk reply | table (may have other Redis data types nested) |
status reply | table with a single ok field containing the status |
error reply | table with a single err field containing the error |
Nil bulk reply | false boolean type |
Nil multi bulk reply | false boolean type |
Lua 類型轉換至 Redis 類型緩存
Lua | Redis |
---|---|
number | integer reply (the number is converted into an integer) |
string | bulk reply |
table (array) | multi bulk reply (truncated to the first nil inside the Lua array if any) |
table with a single ok field | status reply |
table with a single err field | error reply |
boolean false | Nil bulk reply |
boolean true | integer reply with value of 1 |
P251
MULTI
/EXEC
事務同樣,都是原子操做lua-time-limit
選項指定的時間以後,執行 SCRIPT KILL
命令殺死正在運行對腳本P254
若是咱們事先不知道哪些鍵會被讀取和寫入,那麼就應該使用 WATCH
/MULTI
/EXEC
事務或者鎖,而不是 Lua 腳本。所以,在腳本里面對未被記錄到 KEYS
參數中的鍵進行讀取或者寫入,可能會在程序遷移至 Redis 集羣的時候出現不兼容或者故障。 P254
服務器
獲取鎖在目前已不須要使用 Lua 腳本實現,能夠直接使用 SET
,並用 PX
和 NX
選項便可在鍵不存在的時候設置帶過時時間的值。釋放鎖時爲了保證釋放的時本身獲取的鎖,須要使用 Lua 腳本實現。相關代碼已在 實現自動補全、分佈式鎖和計數信號量 中實現。網絡
WATCH
/MULTI
/EXEC
事務 P258
通常來講,若是隻有少數幾個客戶端嘗試對被 WATCH
命令監視對數據進行修改,那麼事務一般能夠在不發生明顯衝突或重試的狀況下完成。可是,若是操做須要進行好幾回通訊往返,或者操做發生衝突的機率較高,又或者網絡延遲較大,那麼客戶端可能須要重試不少次才能完成操做。 P258
編程語言
使用 Lua 腳本替代事務不只能夠保證客戶端嘗試的執行均可以成功,還能下降通訊開銷,大幅提升 TPS 。同時因爲 Lua 腳本沒有屢次通訊往返,因此執行速度也會明顯快於細粒度鎖的版本。分佈式
Lua 腳本能夠提供巨大的性能優點,而且能在一些狀況下大幅地簡化代碼,但運行在 Redis 內部但 Lua 腳本只能訪問位於 Lua 腳本以內或者 Redis 數據庫以內的數據,而鎖或者 WATCH
/MULTI
/EXEC
事務並無這一限制。 P263
P263
P263
爲了可以對分片列表的兩端執行推入操做和彈出操做,在構建分片列表時除了須要存儲組成列表的各個分片以外,還須要記錄列表第一個分片的 ID 以及最後一個分片的 ID 。當分片列表爲空時,這兩個字符串存儲的分片 ID 將是相同的。 P263
組成分片列表的每一個分片都會被命名爲 <listname>:<shardid>
,並按照順序進行分配。具體來講,若是程序老是從左端彈出元素,並從右端推入元素,那麼最後一個分配的索引就會逐漸增大,而且新分片的 ID 也會變得愈來愈大。若是程序老是從右端彈出元素,並從左端推入元素,那麼第一個分片的索引就會逐漸減小,而且新分片的 ID 也會變得愈來愈小。 P264
當分片列表包含多個列表時,位於分片兩端的列表多是被填滿的,但位於兩端之間的其餘列表老是被填滿的。 P264
P265
Lua 腳本根據命令 LPUSH
/RPUSH
找到列表的第一個分片或者最後一個分片,而後將元素推入分片對應的列表中,若分片已達個數上限(能夠取配置中的 list-max-ziplist-entries
的值 - 1 做爲上限),則會自動產生一個新的分片,繼續推入,並更新第一個分片或者最後一個分片的分片 ID 。當推入操做執行完畢後,它會返回被推入元素的數量。 P265
P266
Lua 腳本根據命令 LPOP
/RPOP
找到列表的第一個分片或者最後一個分片,而後在分片非空的狀況下,從分片裏面彈出一個元素,若是列表在執行彈出操做以後再也不包含任何元素,那麼程序就對記錄着列表兩端分片信息的字符串鍵進行修改(注意只有列表端分片爲空時才修改對應的字符串鍵,而整個列表爲空時,不作調整) P267
P267
這一段書上講得看不懂,也不知道爲何須要書中的花式操做才能完成。
我的以爲分片列表的阻塞彈出其實並不須要列表自身的阻塞彈出,咱們能夠不斷執行上述 Lua 腳本實現的彈出元素的操做,若彈出成功,則直接返回,若彈出失敗,則睡 1 ms 後繼續執行彈出操做,直至彈出成功或者達到超時時間。這樣咱們對 Redis 對操做只在 Lua 腳本中,原子性保證了必定會彈出分片列表兩端的元素。
本文首發於公衆號:滿賦諸機(點擊查看原文) 開源在 GitHub :reading-notes/redis-in-action