Lua 腳本在 Redis 中的運用-23(Lua 腳本語法教程)

在 Redis 中編寫和執行 Lua 腳本

Lua 腳本是在 Redis 中執行自定義邏輯的強大功能,可以直接在 Redis 服務器上執行。這減少了延遲,提高了性能,并能夠實現客戶端腳本難以或不可能實現的原子操作。通過在 Redis 中嵌入 Lua 腳本,您可以執行復雜的數據操作和業務邏輯,而無需為每個單獨的命令承擔網絡通信的開銷。本課將涵蓋在 Redis 中編寫和執行 Lua 腳本的基礎知識,為您提供有效利用這一功能的知識。

Redis 中的 Lua 腳本簡介

Redis 使用 Lua 作為其腳本語言。Lua 是一種輕量級、可嵌入的腳本語言,以其速度和簡潔性而聞名。Redis 在服務器端執行 Lua 腳本,這意味著腳本直接在 Redis 實例內運行。這有幾個優勢:

  • 減少延遲:?避免了客戶端和服務器之間為每個命令進行的網絡往返。
  • 原子性:?確保整個腳本作為一個單一的原子操作執行,防止競態條件和數據不一致。這類似于事務的工作方式,但增加了自定義邏輯的靈活性。
  • 代碼復用性:?腳本可以存儲在 Redis 中,并在多個客戶端和會話之間復用。
  • 簡化邏輯:?復雜操作可以封裝在單個腳本中,使客戶端代碼更清晰且易于維護。

為什么是 Lua?

Lua 被選為 Redis 腳本語言的原因是:

  • 簡潔性: Lua 的語法相對容易學習,特別是對于那些熟悉其他腳本語言的人。
  • 速度: Lua 以其性能而聞名,適合在 Redis 中進行服務器端執行。
  • 可嵌入性:?Lua 被設計為可以輕松嵌入到其他應用程序中,使其成為 Redis 的理想選擇。
  • 安全性:?Redis 為 Lua 腳本提供了一個受保護的環境,阻止它們訪問文件系統或進行網絡調用(某些需要顯式配置的例外情況除外)。

為 Redis 編寫 Lua 腳本

Redis 中的 Lua 腳本通過一個特殊的?redis?對象與 Redis 數據存儲交互。該對象提供了與標準 Redis 命令相對應的函數。

基礎 Lua 語法

在深入 Redis 特定腳本之前,讓我們復習一些基本的 Lua 語法:

  • 變量:?變量無需指定類型聲明。

    local my_variable = "Hello, Redis!"
    local my_number = 123
    
  • 數據類型: Lua 支持多種數據類型,包括:

    • string: 文本數據。
    • number: 數值數據(整數和浮點數)。
    • boolean:?true?或?false.
    • table: 一種通用的數據結構,可以用作數組或字典。
    • nil: 表示值不存在。
  • 注釋:?使用?--?進行單行注釋。

    -- This is a comment
    
  • 控制流: Lua 提供了標準的控制流語句:

    • if-then-else:

      local x = 10
      if x > 5 then-- Code to execute if x is greater than 5
      else-- Code to execute otherwise
      end
      
    • for?循環:

      for i = 1, 10 do-- Code to execute 10 times
      end
      
    • while?循環:

      local i = 1
      while i <= 10 do-- Code to execute while i is less than or equal to 10i = i + 1
      end
      
  • 功能:?功能使用?function?關鍵字定義。

    function add(a, b)return a + b
    endlocal result = add(5, 3) -- result will be 8
    

redis?對象

redis?對象是從 Lua 腳本中與 Redis 交互的主要接口。它提供了一個?call()?函數,允許你執行 Redis 命令。

local value = redis.call('GET', 'mykey')

在這個例子中,redis.call('GET', 'mykey')?對鍵?mykey?執行?GET?命令并返回值。

示例:遞增計數器

這是一個簡單的 Lua 腳本,用于增加存儲在 Redis 中的計數器:

-- Get the current value of the counter
local current_value = redis.call('GET', KEYS[1])-- If the counter doesn't exist, initialize it to 0
if not current_value thencurrent_value = 0
end-- Increment the counter
local new_value = tonumber(current_value) + 1-- Set the new value in Redis
redis.call('SET', KEYS[1], new_value)-- Return the new value
return new_value

在這個腳本中:

  • KEYS[1]?指的是傳遞給腳本的第一個鍵。 Redis 中的 Lua 腳本以數組形式接收鍵和參數。?KEYS?是一個包含鍵名稱的數組,而?ARGV?是一個包含參數值的數組。
  • redis.call('GET', KEYS[1])?獲取計數器的當前值。
  • tonumber(current_value)?將從 Redis 獲取的值(始終是字符串)轉換為數字。
  • redis.call('SET', KEYS[1], new_value)?設置計數器的新值。
  • 腳本返回計數器的新值。

示例:賬戶間原子轉賬

考慮一個需要原子方式在兩個賬戶間轉賬的場景。這可以使用 Lua 腳本實現:

-- KEYS[1]: Source account key
-- KEYS[2]: Destination account key
-- ARGV[1]: Amount to transferlocal source_balance = tonumber(redis.call('GET', KEYS[1]))
local destination_balance = tonumber(redis.call('GET', KEYS[2]))
local amount = tonumber(ARGV[1])if source_balance and source_balance >= amount thenredis.call('DECRBY', KEYS[1], amount)redis.call('INCRBY', KEYS[2], amount)return {1, "OK"} -- Success
elsereturn {0, "Insufficient funds"} -- Failure
end

在這個腳本中:

  • KEYS[1]?是源賬戶余額的密鑰。
  • KEYS[2]?是目標賬戶余額的密鑰。
  • ARGV[1]?是轉賬金額。
  • 腳本檢查源賬戶是否有足夠的資金。
  • 如果資金充足,它會減少源賬戶余額并增加目標賬戶余額。
  • 腳本返回成功或失敗消息。

Lua 腳本的錯誤處理

Lua 腳本在執行過程中可能會遇到錯誤。優雅地處理這些錯誤非常重要。如果 Lua 腳本遇到錯誤,Redis 將自動撤銷腳本所做的任何更改,確保原子性。

您可以使用?pcall(受保護調用)來捕獲腳本中的錯誤:

local status, result = pcall(function()-- Your code herereturn redis.call('GET', 'nonexistent_key')
end)if status then-- Code to handle successful executionif result then-- Process the resultend
else-- Code to handle errorsredis.log(redis.LOG_WARNING, "Error: " .. result)
end

在這個例子中:

  • pcall?在受保護的環境中執行匿名函數。
  • 如果函數執行成功,status?將為?trueresult?將包含函數的返回值。
  • 如果函數遇到錯誤,status?將是?false,而?result?將包含錯誤信息。
  • redis.log?用于將錯誤信息記錄到 Redis 日志中。

在 Redis 中執行 Lua 腳本

在 Redis 中執行 Lua 腳本主要有兩種方式:

  1. EVAL:?通過將腳本代碼直接傳遞給 Redis 服務器來執行腳本。
  2. EVALSHA:?通過將腳本的 SHA1 哈希值傳遞給 Redis 服務器來執行腳本。這需要首先使用?SCRIPT LOAD?命令將腳本加載到 Redis 腳本緩存中。

使用 EVAL

EVAL?命令接受以下參數:

EVAL script numkeys key [key ...] arg [arg ...]
  • script?: 要執行的 Lua 腳本。
  • numkeys: 腳本將訪問的鍵的數量。這些鍵必須立即作為參數傳遞給?numkeys
  • key [key ...]: 腳本將訪問的鍵。
  • arg [arg ...]: 腳本可使用的額外參數。

示例:

redis-cli EVAL "return redis.call('GET', KEYS[1])" 1 mykey

該命令執行一個 Lua 腳本,獲取鍵?mykey?的值。1?表示該腳本訪問一個鍵,即?mykey

使用 EVALSHA

EVALSHA?命令接受以下參數:

EVALSHA sha1 numkeys key [key ...] arg [arg ...]
  • sha1: Lua 腳本的 SHA1 哈希值。
  • numkeys: 腳本將訪問的鍵的數量。
  • key [key ...]: 腳本將訪問的鍵。
  • arg [arg ...]: 腳本可使用的額外參數。

在使用?EVALSHA?之前,你必須使用?SCRIPT LOAD?命令將腳本加載到 Redis 腳本緩存中:

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

此命令返回腳本的 SHA1 哈希值。然后您可以使用?EVALSHA

redis-cli EVALSHA <sha1_hash> 1 mykey

EVALSHA 的優點

  • 降低帶寬:EVALSHA?僅發送腳本的 SHA1 哈希值,這比腳本本身小得多。這減少了網絡帶寬,特別是對于大型腳本。
  • 性能提升: Redis 可以緩存編譯后的腳本,這可以提升性能,特別是如果腳本執行頻繁的話。

示例:使用 EVAL 和 EVALSHA

讓我們重新審視計數器自增的例子,并使用?EVAL?和?EVALSHA?來執行它。

使用 EVAL:

redis-cli EVAL "local current_value = redis.call('GET', KEYS[1]) if not current_value then current_value = 0 end local new_value = tonumber(current_value) + 1 redis.call('SET', KEYS[1], new_value) return new_value" 1 mycounter

使用 EVALSHA:

首先,加載腳本:

redis-cli SCRIPT LOAD "local current_value = redis.call('GET', KEYS[1]) if not current_value then current_value = 0 end local new_value = tonumber(current_value) + 1 redis.call('SET', KEYS[1], new_value) return new_value"

這將返回腳本的 SHA1 哈希值(例如,?a7e5b98b9d4a8a2a3b1c2c3d4e5f6a7b8c9d0e1f?)。

然后,使用?EVALSHA?執行腳本:

redis-cli EVALSHA a7e5b98b9d4a8a2a3b1c2c3d4e5f6a7b8c9d0e1f 1 mycounter

管理腳本

Redis 提供了幾個用于管理 Lua 腳本的命令:

  • SCRIPT LOAD:?將腳本加載到腳本緩存中,并返回其 SHA1 哈希。
  • SCRIPT EXISTS:?檢查是否存在具有給定 SHA1 哈希值的腳本在腳本緩存中。
  • SCRIPT FLUSH:?從腳本緩存中移除所有腳本。
  • SCRIPT KILL:?終止當前正在執行的腳本。這只有在腳本被標記為可中斷時才可能實現(這需要 Redis 版本 7 或更高版本,并在腳本中使用?redis.yield()?函數)。

實際練習

  1. 實現一個速率限制器:?編寫一個 Lua 腳本,實現一個簡單的速率限制器。該腳本應接受一個鍵(例如,用戶 ID)和一個限制值作為參數。它應在指定的時間窗口內允許一定數量的請求。如果超出限制,該腳本應返回錯誤。使用 Redis 列表來存儲請求的時間戳。
  2. 原子增量帶過期功能:?編寫一個 Lua 腳本,原子性地遞增一個計數器,并在該鍵不存在時設置過期時間。這可以用于跟蹤臨時事件。
  3. 實現一個簡單的布隆過濾器:?編寫一個 Lua 腳本,實現一個簡化的布隆過濾器。該腳本應接受一個鍵(布隆過濾器的名稱)和一個要插入的值。它應多次對值進行哈希處理,并在布隆過濾器中設置相應的位。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/82730.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/82730.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/82730.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

從零實現本地語音識別(FunASR)

FunASR 是達摩院開源的綜合性語音處理工具包&#xff0c;提供語音識別&#xff08;ASR&#xff09;、語音活動檢測&#xff08;VAD&#xff09;、標點恢復&#xff08;PUNC&#xff09;等全流程功能&#xff0c;支持多種主流模型&#xff08;如 Paraformer、Whisper、SenseVoic…

deepseek開源資料匯總

參考&#xff1a;DeepSeek“開源周”收官&#xff0c;連續五天到底都發布了什么? 目錄 一、首日開源-FlashMLA 二、Day2 DeepEP 三、Day3 DeepGEMM 四、Day4 DualPipe & EPLB 五、Day5 3FS & Smallpond 總結 一、首日開源-FlashMLA 多頭部潛在注意力機制&#x…

【C++ Qt】認識Qt、Qt 項目搭建流程(圖文并茂、通俗易懂)

每日激勵&#xff1a;“不設限和自我肯定的心態&#xff1a;I can do all things。 — Stephen Curry” 緒論?&#xff1a; 本章將開啟Qt的學習&#xff0c;Qt是一個較為古老但仍然在GUI圖形化界面設計中有著舉足輕重的地位&#xff0c;因為它適合嵌入式和多種平臺而被廣泛使用…

AI應用 Markdown 渲染對比與原生實現方案

DeepSeek、豆包、騰訊元寶、ChatGPT 渲染實現對比表 產品解析方式渲染引擎/庫UI 組件架構Markdown支持范圍流程圖/導圖支持擴展架構及裁剪流式解析渲染DeepSeek原生解析&#xff08;非WebView&#xff09;采用 CommonMark 標準解析器&#xff08;推測使用 Markwon 庫&#xff…

Ubuntu20.04系統安裝,使用系統盤安裝

1、系統安裝 Ubuntu20.04系統安裝&#xff0c;使用系統盤安裝 查看ubuntu系統版本 lsb_release -a&#xff1a;顯示發行版名稱、版本號及代號 (base) rootai-System-Product-Name:/media/ai/wh/clash-for-linux-master# lsb_release -a No LSB modules are available. Distri…

(自用)Java學習-5.19(地址管理,三級聯動,預支付)

1. 地址管理模塊 地址展示 前端&#xff1a;通過 showAddress() 發起 Ajax GET 請求&#xff0c;動態渲染地址列表表格&#xff0c;使用 #{tag}、#{name} 等占位符替換真實數據。 后端&#xff1a; 控制器層調用 AddressService&#xff0c;通過 AddressMapper 查詢用戶地址數…

Spring 循環依賴:從原理到解決方案的全面解析

Spring 循環依賴&#xff1a;從原理到解決方案的全面解析 一、循環依賴的定義與分類 1. 什么是循環依賴&#xff1f; 在 Spring 框架中&#xff0c;循環依賴指的是多個 Bean 之間形成了依賴閉環。例如&#xff1a; Bean A 依賴 Bean BBean B 依賴 Bean CBean C 又依賴 Bean…

n 階矩陣 A 可逆的充分必要條件是 ∣ A ∣ ≠ 0

n 階矩陣 A 可逆的充分必要條件是 ∣ A ∣ ≠ 0 |A| \neq 0 ∣A∣0 的幾何意義 1. 行列式的幾何意義回顧 行列式 ∣ A ∣ |A| ∣A∣&#xff08;或 det ? ( A ) \det(A) det(A)&#xff09;表示矩陣 A A A 所對應的線性變換對空間的體積縮放因子&#xff1a; ∣ A ∣ &…

Rockey Linux 安裝ffmpeg

1.環境準備 Rockey linux 9.2 ffmpeg 靜態資源包 這個是我自己的&#xff1a; https://download.csdn.net/download/liudongyang123/90920340https://download.csdn.net/download/liudongyang123/90920340 這個是官網的 Releases BtbN/FFmpeg-Builds GitHub 以上兩個資…

wordcount在集群上的測試

1.將louts.txt文件從cg計算機復制到master節點上面&#xff0c;存放在/usr/local/hadoop 需要輸入密碼&#xff1a;83953588abc scp /root/IdeaProjects/mapReduceTest/lotus.txt root172.18.0.2:/usr/local/hadoop /WordCountTest/input 2.將lotus.txt文件從master這臺機器…

AI+制造:中小企業的低成本智能化轉型

文章內容過長&#xff0c;可以考慮直接跳轉到文章末尾查看概要圖 在制造業競爭日益激烈的今天&#xff0c;中小企業正面臨著前所未有的挑戰&#xff1a;人力成本持續攀升、能源消耗居高不下、質量控制難度增加。與此同時&#xff0c;數字化轉型已成為行業共識&#xff0c;但高…

Linux C/C++編程 —— 線程技術總結

一、線程基本概念 線程是進程內的一個執行單元&#xff0c;多個線程共享進程的資源&#xff08;如內存、文件描述符等&#xff09;&#xff0c;但每個線程擁有自己的寄存器、棧等。與進程相比&#xff0c;線程的創建、切換開銷較小&#xff0c;能更高效地利用 CPU 資源。 二、…

Femap許可證與網絡安全策略

隨著科技的快速發展&#xff0c;網絡安全問題已成為各行各業關注的焦點。在電磁仿真領域&#xff0c;Femap作為一款領先的軟件&#xff0c;其許可證的安全性和網絡策略的重要性不言而喻。本文將探討Femap許可證與網絡安全策略的關系&#xff0c;確保您的電磁仿真工作能夠在一個…

深度解析:SQLynx 如何筑牢數據庫安全防線?

在數據驅動業務發展的時代&#xff0c;數據庫作為企業核心資產的 “保險箱”&#xff0c;其安全性至關重要。一旦數據庫遭遇攻擊、數據泄露或被誤操作&#xff0c;將給企業帶來不可估量的損失。而 SQLynx 作為一款專注于數據庫安全管理的工具&#xff0c;憑借其多項創新技術與功…

更新時間相差8個小時

下面的java代碼在updateFill方法里面生成的modifiedTime時間是當前時間是正確的&#xff0c;為什么到service層testCommonFieldAutoUpdate方法里面去更新的時候modifiedTime就差8個小時呢&#xff1f;代碼如下所示&#xff1a; Slf4j Component public class MpMetaObjectHand…

Windows逆向工程提升之IMAGE_TLS_DIRECTORY

公開視頻 -> 鏈接點擊跳轉公開課程博客首頁 -> ???鏈接點擊跳轉博客主頁 目錄 TLS的作用 TLS的實現 靜態 TLS?? 動態 TLS?? 內部實現 回調機制 TLS Directory 的結構 TLS的作用 TLS (Thread Local Storage) 是一種用于為多線程應用程序提供線程獨立存儲空…

云效流水線Flow使用記錄

概述 最近在頻繁使用阿里云云效的幾款產品&#xff0c;如流水線。之前寫過一篇&#xff0c;參考云效流水線緩存問題。 這篇文章來記錄更多問題。 環境變量 不管是云效流水線Flow還是應用交付AppStack&#xff08;基于流水線&#xff0c;后文不再贅述&#xff09;&#xff0…

Android中獲取控件尺寸進階方案

在Android開發中,很多場景都需要獲取控件(View)的寬高信息,比如動態布局調整、動畫效果實現等。然而,直接在Activity的onCreate()中調用控件的getWidth()或getHeight()`方法,得到結果卻是0,因為控件還沒完成布局測量。 本文總結了幾種獲取控件大小的實用方法,并對各方…

android 輸入系統

一、輸入系統的核心角色與分層架構 Android 輸入系統的本質是橋梁&#xff1a;一端連接硬件驅動產生的原始事件&#xff0c;另一端將事件精準派發給應用窗口。整個過程涉及三層架構和多個關鍵組件&#xff0c;可類比為 “快遞分揀系統”&#xff1a; 1. 硬件與內核層&#xf…

ubuntu中,c和c+程序,預編譯、編譯、鏈接和運行命令

目錄 安裝編譯器一.c編譯運行&#xff08;粗暴簡單&#xff09;1.編寫 C 程序&#xff1a;2. 預處理&#xff1a;3.編譯&#xff1a;4. 匯編&#xff1a;5. 鏈接&#xff1a;6.運行 二.c編譯運行&#xff08;粗暴簡單&#xff09;1.編寫 C 程序&#xff1a;2.預處理&#xff1a…