全部產品
Search
文件中心

PolarDB:Lua指令碼使用說明

更新時間:Feb 11, 2026

當業務需要在一次資料庫互動中原子性地執行多個命令時,例如實現分布式鎖、限流器或進行條件更新,多次網路往返會增加延遲並引入競態條件風險。Orca(相容Redis協議)提供的Lua指令碼功能,允許您將多個Redis相容命令封裝在一個指令碼中,由資料庫原子化執行,從而有效解決此類問題,提升複雜操作的執行效率與資料一致性。

功能簡介

Orca(相容Redis協議)的Lua指令碼功能,允許您像在Redis中一樣執行Lua指令碼。其核心優勢在於原子性和高效能。

  • 原子性:指令碼內的所有命令作為一個不可分割的單元執行。指令碼執行期間,不會有其他命令或指令碼插入,保證了操作的原子性,避免了部分執行導致的中間狀態。

  • 高效能

    • 減少網路開銷:將多個命令打包在單個指令碼中一次性發送給資料庫,顯著減少了用戶端與伺服器之間的網路往返次數。

    • 指令碼緩衝:通過SCRIPT LOAD命令將指令碼預先載入到叢集記憶體中,並獲得一個SHA1校正和。後續可通過EVALSHA命令,僅發送此簡短的SHA1值來呼叫指令碼,進一步降低網路傳輸的資料量。

適用範圍

  • 叢集版本:需為MySQL 8.0.2,且核心小版本需為8.0.2.2.33及以上版本。

  • 串連地址:需使用读写模式可读可写(自动读写分离)Orca地址執行Lua指令碼命令。

  • 不支援的命令:以下命令無法在Lua指令碼中通過redis.call()redis.pcall()執行,否則將返回錯誤。

    • 事務控制命令:WATCHUNWATCHMULTIEXECDISCARD

    • 阻塞命令:BLPOPBRPOP

    • Lua指令碼命令:EVALEVAL_ROEVALSHAEVALSHA_ROSCRIPT

    • 訂閱發布命令:SUBSCRIBEUNSUBSCRIBEPSUBSCRIBEPUNSUBSCRIBE

    • 串連管理命令:AUTHHELLOCLIENT

基本文法

以下是操作Lua指令碼的核心命令。更多資訊請參見Redis官網Lua 指令碼相關命令Scripting with Lua

命令

文法

說明

EVAL

EVAL script numkeys [key [key ...]] [arg [arg ...]]

直接執行給定的Lua指令碼。這是最基礎的執行方式。參數說明:

  • script:Lua指令碼。

  • numkeys:指定KEYS[]參數的數量,非負整數。

  • KEYS[]:傳入的Redis鍵參數,數組的索引均從1開始。

  • ARGV[]:傳入的指令碼參數,數組的索引均從1開始。

EVAL_RO

EVAL_RO script numkeys [key [key ...]] [arg [arg ...]]

唯讀模式下執行Lua指令碼。適用於讀寫分離情境,確保指令碼不會執行寫操作。若指令碼中包含寫命令,將返回錯誤。參數說明與EVAL相同。

EVALSHA

EVALSHA sha1 numkeys [key [key ...]] [arg [arg ...]]

通過指令碼的SHA1校正和執行已緩衝的指令碼。如果指令碼未緩衝,將返回NOSCRIPT錯誤。請通過EVALSCRIPT LOAD命令將目標指令碼緩衝後進行重試。

EVALSHA_RO

EVALSHA_RO sha1 numkeys [key [key ...]] [arg [arg ...]]

唯讀模式下通過指令碼的SHA1校正和執行已緩衝的指令碼。適用於讀寫分離情境,確保指令碼不會執行寫操作。若指令碼中包含寫命令,將返回錯誤。參數說明與EVALSHA相同。

SCRIPT LOAD

SCRIPT LOAD script

將指令碼載入到緩衝中,並返回其SHA1校正和,以便後續通過EVALSHA調用。

SCRIPT EXISTS

SCRIPT EXISTS sha1 [sha1 ...]

檢查一個或多個SHA1校正和對應的指令碼是否存在於緩衝中。存在返回1,不存在返回0。

SCRIPT KILL

SCRIPT KILL

終止當前正在執行的Lua指令碼。Orca支援終止任意指令碼(包括寫指令碼),並會自動復原該指令碼已產生的資料變更,保證資料一致性。

SCRIPT FLUSH

SCRIPT FLUSH [ASYNC | SYNC]

清空當前叢集中的所有Lua指令碼緩衝。

  • ASYNC:非同步清空指令碼緩衝。

  • SYNC(預設):同步清空指令碼緩衝。

操作樣本

  1. 準備測試資料:

    SET polardb orca
  2. 測試命令:

    EVAL

    EVAL "return redis.call('GET', KEYS[1])" 1 polardb

    返回樣本:

    "orca"

    EVAL_RO

    EVAL_RO "return redis.call('GET', KEYS[1])" 1 polardb

    返回樣本:

    "orca"

    SCRIPT LOAD

    SCRIPT LOAD "return redis.call('GET', KEYS[1])"

    返回樣本:

    "d3c21d0c2b9ca22f82737626a27bcaf5d288f99f"

    EVALSHA

    EVALSHA d3c21d0c2b9ca22f82737626a27bcaf5d288f99f 1 polardb

    返回樣本:

    "orca"

    EVALSHA_R

    EVALSHA_RO d3c21d0c2b9ca22f82737626a27bcaf5d288f99f 1 polardb

    返回樣本:

    "orca"

    SCRIPT EXISTS

    SCRIPT EXISTS d3c21d0c2b9ca22f82737626a27bcaf5d288f99f ffffffffffffffffffffffffffffffffffffffff

    返回樣本:

    1) (integer) 1
    2) (integer) 0

    SCRIPT FLUSH

    警告

    該命令會清空叢集中的所有Lua指令碼緩衝(SCRIPT LOAD/EVAL產生的緩衝都會被移除)。

    • 清空後,所有依賴EVALSHA/EVALSHA_RO的調用都可能返回NOSCRIPT錯誤,需要重新載入指令碼並重試。

    • 若緩衝指令碼較多,SCRIPT FLUSH可能阻塞叢集較長時間,建議在業務低峰期執行。

    • 建議您在用戶端/應用側保留指令碼原始碼,並實現NOSCRIPT的自動回復機制(重新SCRIPT LOAD或使用EVAL重新註冊)。

    SCRIPT FLUSH

    返回樣本:

    OK

常見使用情境

配置並執行一個原子計數器

避免並發INCR導致的競態,確保計數器嚴格遞增且可帶條件重設。

流程概述

  1. 編寫Lua指令碼。

  2. 載入至叢集緩衝。

  3. 通過EVALSHA複用執行。

SCRIPT LOAD + EVALSHA減少網路傳輸量,提升效能與等冪性。指令碼內容不暴露於請求體。

操作步驟

  1. 編寫指令碼(本地儲存):

    -- counter.lua:若 key 不存在則設為 0,再 +1;返回新值
    if redis.call('EXISTS', KEYS[1]) == 0 then
      redis.call('SET', KEYS[1], 0)
    end
    return redis.call('INCR', KEYS[1])
  2. 載入指令碼並擷取SHA1

    SCRIPT LOAD "if redis.call('EXISTS', KEYS[1]) == 0 then redis.call('SET', KEYS[1], 0) end return redis.call('INCR', KEYS[1])"
    
    
    -- 返回結果
    "b547eabbcde73b25330442e4f4e4dc1783b91241"
  3. (推薦)複用執行:

    EVALSHA b547eabbcde73b25330442e4f4e4dc1783b91241 1 my_counter
    
    -- 返回結果
    (integer) 1
    -- 再次執行返回結果
    (integer) 2

執行純查詢指令碼

  1. 確認指令碼不含寫操作檢查是否調用 SETDELHSETLPUSH等任意寫命令。若存在,禁止使用EVAL_RO

    說明

    若指令碼含寫命令,將返回錯誤(error) ERR Write commands are not allowed from read-only scripts.

  2. 使用EVAL_RO執行:

    EVAL_RO "return {redis.call('SET', KEYS[1]), redis.call('TTL', KEYS[1])}" 1 polardb
    
    -- 返回結果
    1) "orca"
    2) (integer) -1

終止異常啟動並執行Lua指令碼

防止長耗時指令碼阻塞叢集。中斷執行,系統將自動復原全部變更,保持資料一致性。執行SCRIPT KILL

說明

僅能終止當前正在執行的指令碼,不支援指定SHA1終止。

SCRIPT KILL

-- 返回結果
OK

效能最佳化實踐

為降低Lua指令碼對叢集的阻塞影響,並避免叢集緩衝大量功能重複的指令碼導致記憶體佔用過高,建議遵循以下實踐:

  • 逾時限制:單個Lua指令碼的預設執行逾時時間為300秒。

    • 避免編寫過大的Lua指令碼,防止佔用過多的記憶體。

    • 避免在Lua指令碼中長時間、大批量寫入資料。

  • 阻塞風險:Lua指令碼在叢集中以原子方式執行。在執行期間,會阻塞其他命令與指令碼,因此應控制寫入批量與執行時間長度,以避免長迴圈或大範圍遍曆。如有必要,應將複雜邏輯拆分為多個獨立指令碼進行執行。

  • 指令碼緩衝非持久化:叢集運行時Lua指令碼緩衝不會被清除,叢集重啟、主備切換(HA)或執行SCRIPT FLUSH命令後會清理Lua指令碼緩衝,需要重新註冊。

  • 指令碼編寫規範:使用KEYS[]ARGV[]傳遞參數,避免將參數寫入程式碼在指令碼中。

  • 減少網路流量:使用SCRIPT LOAD + EVALSHA組合,獲得最佳效能並減少網路流量。

操作樣本

  1. 載入指令碼:在應用初始化或首次使用時,通過SCRIPT LOAD將指令碼載入到叢集並擷取其SHA1值。您的應用程式應在本地儲存此SHA1值。

    # 載入一個設定索引值的指令碼
    SCRIPT LOAD "return redis.call('set', KEYS[1], ARGV[1])"
    # 返回樣本
    "55b22c0d0cedf3866879ce7c854970626dcef0c3"
  2. 執行指令碼:後續通過EVALSHA命令,使用上一步擷取的SHA1值來執行指令碼。

    # 使用緩衝的指令碼設定 k1 = v1
    EVALSHA 55b22c0d0cedf3866879ce7c854970626dcef0c3 1 k1 v1
    # 使用同一個緩衝的指令碼設定 k2 = v2
    EVALSHA 55b22c0d0cedf3866879ce7c854970626dcef0c3 1 k2 v2
  3. 處理NOSCRIPT錯誤:如果EVALSHA返回NOSCRIPT錯誤,應用程式應捕獲此錯誤,然後自動切換回使用EVALSCRIPT LOAD命令執行一次指令碼將指令碼緩衝至叢集中。

    • EVAL:在執行的同時會自動將指令碼重新緩衝。

    • SCRIPT LOAD:將指令碼載入到叢集並擷取其SHA1值。

  4. 清理Lua指令碼的記憶體佔用:當您在不需要執行Lua指令碼時,可執行SCRIPT FLUSH命令清除Lua指令碼緩衝。若叢集中緩衝的Lua指令碼過多,該命令可能會阻塞叢集較長時間,建議在業務低峰期執行。

常見問題

  • 如何處理NOSCRIPT No matching script. Please use EVAL.錯誤?

    問題原因:這個錯誤表示您嘗試使用EVALSHAEVALSHA_RO命令執行一個不存在於叢集緩衝中的指令碼。通常由叢集重啟、主備切換或執行了SCRIPT FLUSH導致。
    解決方案:您的用戶端代碼需要具備錯誤處理邏輯。當捕獲到NOSCRIPT錯誤時,應改用EVALSCRIPT LOAD命令重新執行該指令碼將指令碼緩衝至叢集中,後續的EVALSHA調用即可恢複正常。

  • Lua指令碼執行逾時怎麼辦?

    指令碼預設逾時時間為300秒。逾時通常意味著指令碼邏輯過於複雜或處理的資料量過大。
    解決方案:

    • 最佳化指令碼邏輯,避免在指令碼內執行低效的迴圈或大規模資料遍曆。

    • 如果一個指令碼執行時間過長,可以通過SCRIPT KILL命令終止它。PolarDB會復原該指令碼已做的所有修改。

    • 將複雜的商務邏輯拆分為多個更小、更快的指令碼分步執行。

  • 為什麼使用EVAL_RO執行指令碼會報錯ERR Write commands are not allowed from read-only scripts.

    EVAL_ROEVALSHA_RO是唯讀模式,嚴禁執行任何寫操作命令(如SETHSETDEL等)。此錯誤明確指出您的指令碼中包含了寫命令。
    解決方案:

    • 檢查並移除指令碼中的所有寫命令。

    • 如果您確實需要執行寫操作,請改用EVALEVALSHA命令。