Redis Lua脚本实现原子性操作
一、簡介
redis操作時單線程的,平常如果想要redis原子性操作的話,可以使用incrBy()和decrBy()方法進(jìn)行原子性的加減,但是對于事務(wù)性的邏輯操作,沒有辦法實現(xiàn)原子性,Redis 使用單個 Lua 解釋器去運(yùn)行所有腳本,當(dāng)某個腳本正在運(yùn)行的時候,不會有其他腳本或 Redis 命令被執(zhí)行,因此,lua腳本需要運(yùn)行的使用比較快,不會妨礙其它lua腳本執(zhí)行
二、內(nèi)容說明
| EVAL script numkeys key [key ...] arg [arg ...] | eval() | 執(zhí)行l(wèi)ua腳本 |
| EVALSHA sha1 numkeys key [key ...] arg [arg ...] | evalsha() | 執(zhí)行l(wèi)ua腳本對應(yīng)的緩存值 |
| SCRIPT EXISTS script [script ...] | scriptExists() | 判斷腳本是否已經(jīng)添加到緩存中去了,1代表已經(jīng)添加,0代表沒有添加 |
| SCRIPT FLUSH | scriptFlush() | 清除lua腳本緩存 |
| SCRIPT KILL | scriptKill() | 殺死當(dāng)前正在運(yùn)行的 Lua 腳本,當(dāng)且僅當(dāng)這個腳本沒有執(zhí)行過任何寫操作時,這個命令才生效,防止lua腳本死循環(huán) |
| SCRIPT LOAD script | scriptLoad() | 將腳本 script 添加到腳本緩存中,但并不立即執(zhí)行這個腳本,eval是執(zhí)行并添加緩存 |
1.eval命令
eval script numkeys key [key ...] arg [arg ...] #參數(shù)說明 #script:它會被運(yùn)行在 Redis 服務(wù)器上下文中,這段腳本不必(也不應(yīng)該)定義為一個 Lua 函數(shù)。 #numkeys:用于指定鍵名參數(shù)的個數(shù)。 #key:鍵名參數(shù),表示在腳本中所用到的那些 Redis 鍵(key),這些鍵名參數(shù)可以在 Lua 中通過全局變量 KEYS 數(shù)組,用 1 為基址的形式訪問( KEYS[1] , KEYS[2] ,以此類推)。 #arg:全局變量,可以在 Lua 中通過全局變量 ARGV 數(shù)組訪問,訪問的形式和 KEYS 變量類似( ARGV[1] 、 ARGV[2] ,諸如此類)使用樣例如下:執(zhí)行腳本,,KEYS[1]對應(yīng)foo,ARGV[1]對應(yīng)第二個1,ARGV[2]對應(yīng)100,角標(biāo)[1]對應(yīng)第1個參數(shù),表示一個key,redis.call里面第一個參數(shù)redis命令,之后是該命令對應(yīng)的key-value
--如果當(dāng)前foo,對應(yīng)的值減去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執(zhí)行結(jié)果如下:
?注意:腳本里使用的所有鍵都應(yīng)該由 KEYS 數(shù)組來傳遞,變量入?yún)⑼ㄟ^ARGV數(shù)組傳遞
通過redis的eval命令來實現(xiàn),使用 EVAL 命令對 Lua 腳本進(jìn)行求值。在lua腳本中可以通過兩個不同的函數(shù)調(diào)用redis命令,分別是:redis.call() 和 redis.pcall(),這兩者方法對于錯誤的處理方式不同
redis.call():redis.call關(guān)鍵字執(zhí)行redis命令,在執(zhí)行命令的過程中發(fā)生錯誤時,腳本會停止執(zhí)行,并返回一個腳本錯誤,錯誤的輸出信息會說明錯誤造成的原因
redis.pcall(): 出錯時并不引發(fā)(raise)錯誤,而是返回一個帶 err 域的 Lua 表(table),用于表示錯誤
eval 命令會在每次執(zhí)行腳本的時候都發(fā)送一次腳本主體(script body),每次都需要重新編譯腳本,會有一定的損耗
2.evalsha命令
evalsha命令和eval一樣,但它可以使用腳本生成的緩存sha來執(zhí)行,出現(xiàn)錯誤是,會使用eval命令執(zhí)行l(wèi)ua腳本?
#格式 evalsha sha1 numkeys key [key ...] arg [arg ...](1)Redis 保證所有被運(yùn)行過的腳本都會被永久保存在腳本緩存當(dāng)中,當(dāng) eval命令在一個 Redis 實例上成功執(zhí)行某個腳本之后,隨后針對這個腳本的所有 evalsha命令都會成功執(zhí)行,
(2)清空lua腳本方法需要使用 script flush命令
(3)不能使用全局性的腳本變量
3.其他命令
script load加載腳本到緩存中,但不立即執(zhí)行,script exists判斷是否存在該緩存,script flush清除所有l(wèi)ua腳本緩存,script kill殺死正在執(zhí)行中的命令
三、Java中使用Jedis操作
1.定義lua腳本
這里代表 key的value當(dāng)前值減去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執(zhí)行l(wèi)ua腳本
執(zhí)行jedisClusterTemplate中的eval方法,傳入lua腳本,key的個數(shù),只有接入對應(yīng)參數(shù),參數(shù)對應(yīng)前面2.1里面redis用法
總結(jié)
以上是生活随笔為你收集整理的Redis Lua脚本实现原子性操作的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: EasyExcel实现文件读取、导出、上
- 下一篇: linux cmake编译源码,linu