當前位置:首頁 » 網頁前端 » redis中lua腳本的使用
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

redis中lua腳本的使用

發布時間: 2022-08-16 17:45:02

Ⅰ 如何在 Redis 實現 Lua 腳本事務

在 1998-2003 年間,如果你想運行一個正規的資料庫驅動的網站/服務,但又沒有足夠的資金購買微軟或 Oracle 的資料庫,你可以選擇 MySQL 或 Postgres 。很多人都選擇了 MySQL,因為它速度較快——主要是因為 MyISAM 存儲引擎沒有提供事務功能以此來換取性能,但速度確實很快。另一些人轉向 Postgres,因為雖然在相同硬體上其性能明顯低於 MySQL,但 Postgres 不會丟失數據(說實話,MySQL 數據丟失的情況非常少見,但丟了可不是鬧著玩的)。
就這樣湊合著過了很久;MySQL 將其默認的存儲引擎從 MyISAM 過渡到了 InnoDB (其實很早

Ⅱ 使用redis實現的分布式鎖原理是什麼

一、寫在前面

現在面試,一般都會聊聊分布式系統這塊的東西。通常面試官都會從服務框架(Spring Cloud、Dubbo)聊起,一路聊到分布式事務、分布式鎖、ZooKeeper等知識。

所以咱們這篇文章就來聊聊分布式鎖這塊知識,具體的來看看Redis分布式鎖的實現原理。

說實話,如果在公司里落地生產環境用分布式鎖的時候,一定是會用開源類庫的,比如Redis分布式鎖,一般就是用Redisson框架就好了,非常的簡便易用。

大家如果有興趣,可以去看看Redisson的官網,看看如何在項目中引入Redisson的依賴,然後基於Redis實現分布式鎖的加鎖與釋放鎖。

下面給大家看一段簡單的使用代碼片段,先直觀的感受一下:

大家看到了吧,那個myLock的hash數據結構中的那個客戶端ID,就對應著加鎖的次數

(5)釋放鎖機制

如果執行lock.unlock(),就可以釋放分布式鎖,此時的業務邏輯也是非常簡單的。

其實說白了,就是每次都對myLock數據結構中的那個加鎖次數減1。

如果發現加鎖次數是0了,說明這個客戶端已經不再持有鎖了,此時就會用:

「del myLock」命令,從redis里刪除這個key。

然後呢,另外的客戶端2就可以嘗試完成加鎖了。

這就是所謂的分布式鎖的開源Redisson框架的實現機制。

一般我們在生產系統中,可以用Redisson框架提供的這個類庫來基於redis進行分布式鎖的加鎖與釋放鎖。

(6)上述Redis分布式鎖的缺點

其實上面那種方案最大的問題,就是如果你對某個redis master實例,寫入了myLock這種鎖key的value,此時會非同步復制給對應的master slave實例。

但是這個過程中一旦發生redis master宕機,主備切換,redis slave變為了redis master。

接著就會導致,客戶端2來嘗試加鎖的時候,在新的redis master上完成了加鎖,而客戶端1也以為自己成功加了鎖。

此時就會導致多個客戶端對一個分布式鎖完成了加鎖。

這時系統在業務語義上一定會出現問題,導致各種臟數據的產生。

所以這個就是redis cluster,或者是redis master-slave架構的主從非同步復制導致的redis分布式鎖的最大缺陷:在redis master實例宕機的時候,可能導致多個客戶端同時完成加鎖。

Ⅲ redis有腳本語言嗎

有,lua腳本語言

Redis腳本

使用腳本的好處:

  • 減少網路開銷。可以將多個請求通過腳本的形式一次發送,減少網路時延

  • 原子操作。redis會將整個腳本作為一個整體執行,中間不會被其他命令插入。因此在編寫腳本的過程中無需擔心會出現競態條件,無需使用事務。

  • 復用。客戶端發送的腳步會永久存在redis中,這樣,其他客戶端可以復用這一腳本而不需要使用代碼完成相同的邏輯。

  • 調用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 提供用於調試的函數

    在腳本中調用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數據類型

    redis返回值類型

    數字類型 整數回復(Lua的數字類型會被自動轉換成整數)

    字元串類型 字元串回復

    table類型(數組形式) 多行字元串回復

    table類型(只有一個ok欄位存儲狀態信息) 狀態回復

    table類型(只有一個err欄位存儲錯誤信息) 錯誤回復

    腳本相關命令

  • EVAL "lua-script" [key ...] [arg ...]

    通過key和arg這兩類參數向腳本傳遞數據,它們的值在腳本中分別使用KEYS和ARGV兩個表類型的全局變數訪問

    注意: EVAL命令依據參數key-number來將其後面的所有參數分別存入腳本中KEYS和ARGV兩個table類型的全局變數。當腳本不需要任何參數時,也不能省略這個參數(設為0)

    redis>EVAL "return redis.call('SET', KEYS[1], ARGV[1])" 1 foo bar
    OK
    redis>GET foo"bar"
  • EVALSHA命令

    在腳本比較長的情況下,如果每次調用腳本都需要將整個腳本傳給Redis會佔用較多的帶寬。為了解決這個問題,Redis提供了EVALSHA命令,允許開發者通過腳本內容的SHA1摘要來執行腳本,該命令的用法和EVAL一樣,只不過是將腳本內容替換成腳本內容的SHA1摘要。

    Redis在執行EVAL命令時會計算腳本的SHA1摘要並記錄在腳本緩存中,執行EVALSHA命令時Redis會根據提供的摘要從腳本緩存中查找對應的腳本內容,如果找到了則執行腳本,否則會返回錯誤:"NOSCRIPT No matching script. Please use EVAL."

    在程序中使用EVALSHA命令的一般流程如下。

    雖然這一流程略顯麻煩,但值得慶幸的是很多編程語言的Redis客戶端都會代替開發者完成這一流程。執行EVAL命令時,先嘗試執行EVALSHA命令,如果失敗了才會執行EVAL命令。

  • 先計算腳本的SHA1摘要,並使用EVALSHA命令執行腳本。

  • 獲得返回值,如果返回「NOSCRIPT」錯誤則使用EVAL命令重新執行腳本。

  • SCRIPTLOAD "lua-script"

    將腳本加入緩存,但不執行. 返回:腳本的SHA1摘要

  • SCRIPT EXISTS lua-script-sha1

    判斷腳本是否已被緩存

  • SCRIPT FLUSH

    清空腳本緩存 redis將腳本的SHA1摘要加入到腳本緩存後會永久保留,不會刪除,但可以手動使用SCRIPT FLUSH命令情況腳本緩存。

  • SCRIPT KILL

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

  • Redis的腳本執行是原子的,即腳本執行期間Redis不會執行其他命令。所有的命令都必須等待腳本執行完成後才能執行。為了防止某個腳本執行時間過長導致Redis無法提供服務(比如陷入死循環),Redis提供了lua-time-limit參數限制腳本的最長運行時間,默認為5秒鍾。當腳本運行時間超過這一限制後,Redis將開始接受其他命令但不會執行(以確保腳本的原子性,因為此時腳本並沒有被終止),而是會返回「BUSY」錯誤

Ⅳ redis lua腳本有什麼用

主要用途是: (1)描述界面:WOW和劍網三的界面都是用LUA寫的; (2)溝通引擎:游戲圖形引擎提供了一些介面庫,可以在LUA中調用; (3)伺服器端:有些游戲,例如劍網三,在伺服器端也會大量使用LUA。

Ⅳ redis執行lua腳本的時候會不會刪除過期鍵

生命周期為一天,現在執行lua腳本,邏輯是判斷a是否存在,存在則往其中添加其它的元素

Ⅵ hash類型的redis怎樣實現聯合查詢

Redis不僅僅是一個簡單的key-value內存資料庫,Redis官網對自身的定義是「數據結構伺服器」。通過用心設計各種數據結構類型的數據存儲,可以實現部分的數據查詢功能。因為在Redis的設計中,key是一切,對於Redis是可見的,而value對於Redis來說就是一個位元組數組,Redis並不知道你的value中存儲的是什麼,所以要想實現比如
『select * from users where user.location="shanghai"』

這樣的查詢,在Redis是沒辦法通過value進行比較得出結果的。但是可以通過不同的數據結構類型來做到這一點。比如如下的數據定義

users:1 {name:Jack,age:28,location:shanghai}
users:2 {name:Frank,age:30,location:beijing}
users:location:shanghai [1]
其中users:1 users:2 分別定義了兩個用戶信息,通過Redis中的hash數據結構,而users:location:shanghai 記錄了所有上海的用戶id,通過集合數據結構實現。這樣通過兩次簡單的Redis命令調用就可以實現我們上面的查詢。

Jedis jedis = jedisPool.getResource();
Set<String> shanghaiIDs = jedis.smembers("users:location:shanghai");
//遍歷該set
//...
//通過hgetall獲取對應的user信息
jedis.hgetAll("users:" + shanghaiIDs[0]);
通過諸如以上的設計,可以實現簡單的條件查詢。但是這樣的問題也很多,首先需要多維護一個ID索引的集合,其次對於一些復雜查詢無能為力(當然也不能期望Redis實現像關系資料庫那樣的查詢,Redis不是干這的)。

但是Redis2.6集成了Lua腳本,可以通過eval命令,直接在RedisServer環境中執行Lua腳本,並且可以在Lua腳本中調用Redis命令。其實,就是說可以讓你用Lua這種腳本語言,對Redis中存儲的key value進行操作,這個意義就大了,甚至可以將你們系統所需的各種業務寫成一個個lua腳本,提前載入進入Redis,然後對於請求的響應,只需要調用一個個lua腳本就行。當然這樣說有點誇張,但是意思就是這樣的。

比如,現在我們要實現一個『所有age大於28歲的user』這樣一個查詢,那麼通過以下的Lua腳本就可以實現

public static final String SCRIPT =
"local resultKeys={};"
+ "for k,v in ipairs(KEYS) do "
+ " local tmp = redis.call('hget', v, 'age');"
+ " if tmp > ARGV[1] then "
+ " table.insert(resultKeys,v);"
+ " end;"
+ "end;"
+ "return resultKeys;";

執行腳本代碼
Jedis jedis = jedisPool.getResource();
jedis.auth(auth);
List<String> keys = Arrays.asList(allUserKeys);

List<String> args = new ArrayList<>();
args.add("28");

List<String> resultKeys = (List<String>)jedis.evalsha(funcKey, keys, args);
return resultKeys;
注意,以上的代碼中使用的是evalsha命令,該命令參數的不是直接Lua腳本字元串,而是提前已經載入到Redis中的函數的一個SHA索引,通過以下的代碼將系統中所有需要執行的函數提前載入到Redis中,我們的系統維護一個函數哈希表,後續需要實現什麼功能,就從函數表中獲取對應功能的SHA索引,通過evalsha調用就行。
String shaFuncKey = jedis.scriptLoad(SCRIPT);//載入腳本,獲取sha索引
funcTable.put(funcName_age, shaFuncKey);//添加到函數表中
通過以上的方法,便可以使較為復雜的查詢放到Redis中去執行,提高效率。

Ⅶ redis載入lua腳本,怎麼獲取數據

需要用lua寫redis的操作函數,先連接redis資料庫,然後執行命令,來獲取數據。

Ⅷ 為什麼在 Redis 實現 Lua 腳本事務

從很多方面來看,Redis 很像當初採用 InnoDB 前的 MySQL。而 Redis 採用了一種很合理的方式來保證數據完整性(復制,AOF 等),並且從 Redis2.6 開始引入的 Lua 腳本在功能與易用性方面為 Redis 的成長提供了很大助力。

相對來說,Lua 腳本與其他資料庫中的存儲過程很相似,但腳本的執行有些許不同。在本文中最重要的一點就是一旦將腳本寫入資料庫,它會一直執行直到以下任一種情況出現:

1. 完成所有工作,所有寫操作處理完成後腳本會自動退出。

2. 腳本運行時出錯並中途退出,所有以前執行的寫操作都已發生,但不會再有其他寫操作。

3. Redis 通過 SHUTDOWN NOSAVE 關閉時(不保存)。

4. 你附加了調試器來「使」腳本完成 #1 與 #2 (或其他手段來保證不會丟失數據)。

對於使用資料庫開發軟體的人,我想你也認同只有情景 #1 是最理想的。情景 #2,#3,#4 都會導致數據異常(#2 與
#4)和/或數據丟失(#3 和 #4)。如果你很重視數據,你應該盡可能地阻止數據異常與丟失。這不是哲學,而是工作(This is not
philosophy, this is doing your job)。但很遺憾目前的 Redis 也幫不了你多少。所以我決定改變這種情況。

Ⅸ Redis中嵌套結構的替代方案

基本上有兩種策略:

  • 可以序列化復雜對象並將其存儲為字元串。我們建議使用json或msgpack作為序列化格式。這很容易從大多數客戶端語言進行操作。如果需要伺服器端訪問,則伺服器端的Lua腳本可以輕松地對此類對象進行編碼/解碼,因為Redis是使用針對Lua的msgpack和json支持進行編譯的。

  • 可以將對象拆分為不同的鍵。除了存儲user:id和該ID的復雜數據結構之外,還可以存儲幾個鍵,如user:id,user:id:address_list,user:id:document_lists等…如果需要原子性,則對MULTI / EXEC塊可用於保證數據一致性並匯總往返次數。

    Redis不是面向文檔的資料庫。如果確實有很多復雜的文檔,那麼MongoDB,ArangoDB,CouchDB,Couchbase等解決方案可能會更好地提供服務