Redis Lua脚本实现原子性操作
一、簡介
redis操作時單線程的,平常如果想要redis原子性操作的話,可以使用incrBy()和decrBy()方法進行原子性的加減,但是對于事務性的邏輯操作,沒有辦法實現原子性,Redis 使用單個 Lua 解釋器去運行所有腳本,當某個腳本正在運行的時候,不會有其他腳本或 Redis 命令被執行,因此,lua腳本需要運行的使用比較快,不會妨礙其它lua腳本執行
二、內容說明
| EVAL script numkeys key [key ...] arg [arg ...] | eval() | 執行lua腳本 |
| EVALSHA sha1 numkeys key [key ...] arg [arg ...] | evalsha() | 執行lua腳本對應的緩存值 |
| SCRIPT EXISTS script [script ...] | scriptExists() | 判斷腳本是否已經添加到緩存中去了,1代表已經添加,0代表沒有添加 |
| SCRIPT FLUSH | scriptFlush() | 清除lua腳本緩存 |
| SCRIPT KILL | scriptKill() | 殺死當前正在運行的 Lua 腳本,當且僅當這個腳本沒有執行過任何寫操作時,這個命令才生效,防止lua腳本死循環 |
| SCRIPT LOAD script | scriptLoad() | 將腳本 script 添加到腳本緩存中,但并不立即執行這個腳本,eval是執行并添加緩存 |
1.eval命令
eval script numkeys key [key ...] arg [arg ...] #參數說明 #script:它會被運行在 Redis 服務器上下文中,這段腳本不必(也不應該)定義為一個 Lua 函數。 #numkeys:用于指定鍵名參數的個數。 #key:鍵名參數,表示在腳本中所用到的那些 Redis 鍵(key),這些鍵名參數可以在 Lua 中通過全局變量 KEYS 數組,用 1 為基址的形式訪問( KEYS[1] , KEYS[2] ,以此類推)。 #arg:全局變量,可以在 Lua 中通過全局變量 ARGV 數組訪問,訪問的形式和 KEYS 變量類似( ARGV[1] 、 ARGV[2] ,諸如此類)使用樣例如下:執行腳本,,KEYS[1]對應foo,ARGV[1]對應第二個1,ARGV[2]對應100,角標[1]對應第1個參數,表示一個key,redis.call里面第一個參數redis命令,之后是該命令對應的key-value
--如果當前foo,對應的值減去1之后,小于0,那么就給它加上100返回,否則直接返回減去1值后的值 eval "if redis.call('decrBy',KEYS[1],ARGV[1]) < 0 thenreturn redis.call('incrBy',KEYS[1],ARGV[2]) elsereturn redis.call('get',KEYS[1]) end" 1 foo 1 100執行結果如下:
?注意:腳本里使用的所有鍵都應該由 KEYS 數組來傳遞,變量入參通過ARGV數組傳遞
通過redis的eval命令來實現,使用 EVAL 命令對 Lua 腳本進行求值。在lua腳本中可以通過兩個不同的函數調用redis命令,分別是:redis.call() 和 redis.pcall(),這兩者方法對于錯誤的處理方式不同
redis.call():redis.call關鍵字執行redis命令,在執行命令的過程中發生錯誤時,腳本會停止執行,并返回一個腳本錯誤,錯誤的輸出信息會說明錯誤造成的原因
redis.pcall(): 出錯時并不引發(raise)錯誤,而是返回一個帶 err 域的 Lua 表(table),用于表示錯誤
eval 命令會在每次執行腳本的時候都發送一次腳本主體(script body),每次都需要重新編譯腳本,會有一定的損耗
2.evalsha命令
evalsha命令和eval一樣,但它可以使用腳本生成的緩存sha來執行,出現錯誤是,會使用eval命令執行lua腳本?
#格式 evalsha sha1 numkeys key [key ...] arg [arg ...](1)Redis 保證所有被運行過的腳本都會被永久保存在腳本緩存當中,當 eval命令在一個 Redis 實例上成功執行某個腳本之后,隨后針對這個腳本的所有 evalsha命令都會成功執行,
(2)清空lua腳本方法需要使用 script flush命令
(3)不能使用全局性的腳本變量
3.其他命令
script load加載腳本到緩存中,但不立即執行,script exists判斷是否存在該緩存,script flush清除所有lua腳本緩存,script kill殺死正在執行中的命令
三、Java中使用Jedis操作
1.定義lua腳本
這里代表 key的value當前值減去ARGV[1],如果減去ARGV[1]之后小于0的話則返回原來值,否則返回減去之后的值
public static final String LUA = "if redis.call('decrBy',KEYS[1],ARGV[1]) < 0 then\n" +" return redis.call('incrBy',KEYS[1],ARGV[1])\n" +" else\n" +" return redis.call('get',KEYS[1])\n" +"end ";2.加載lua腳本(可省略)
String key = "demo"; jedisClusterTemplate.set(key, "2"); log.info("加載腳本lua腳本:lua script={}", LUA); jedisClusterTemplate.scriptLoad(LUA, key);3.使用eval執行lua腳本
執行jedisClusterTemplate中的eval方法,傳入lua腳本,key的個數,只有接入對應參數,參數對應前面2.1里面redis用法
總結
以上是生活随笔為你收集整理的Redis Lua脚本实现原子性操作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: EasyExcel实现文件读取、导出、上
- 下一篇: 操作系统中PV操作之顾客理发师问题