Redis 使用 Lua 脚本进行原子操作
Redis 使用 Lua 腳本進(jìn)行原子操作
Intro
之前寫過一篇文章也是 Redis 使用 LUA 腳本實(shí)現(xiàn)分布式的 CAS 操作,可以參考:基于 Redis 實(shí)現(xiàn) CAS 操作
最近使用 Redis 的時(shí)候有一個(gè)需求,只有值發(fā)生變化的時(shí)候才更新,如果要更新的值和現(xiàn)在的值是一樣的就不用更新,有點(diǎn)類似于 SET NX,只是 SET NX 只有值不存在的時(shí)候才會(huì) SET,我的需求則是要檢查要 SET 的值和 Redis 里的值,如果不一樣就 SET,一樣就直接返回
Implement
我實(shí)現(xiàn)了針對 String 和 Hash 的 SET 檢查,核心就是我們的 Lua 腳本
實(shí)現(xiàn)代碼如下:
對于 Hash 會(huì)多一個(gè)參數(shù) —— hash field name,?對于 string?則直接是 value 了,就會(huì)比 hash?少一個(gè)參數(shù)
實(shí)現(xiàn)起來也比較簡單,就是先取一下 Redis 中的數(shù)據(jù),如果和輸入的值是一樣就返回 0,不一樣則更新值,然后返回 1
StackExchange.Redis 使用 API
在 StackExchange.Redis 中可以使用 ScriptEvaluate/ScriptEvaluateAsync 來執(zhí)行 Lua 腳本,為了方便使用我把他們封裝成了擴(kuò)展方法,實(shí)現(xiàn)如下:
public?static?bool?StringSetWhenValueChanged(this?IDatabase?db,?RedisKey?key,?RedisValue?value) {return?(int)db.ScriptEvaluate(StringSetWhenValueChangedLuaScript,?new[]?{?key?},?new[]?{?value?})?==?1; }public?static?async?Task<bool>?StringSetWhenValueChangedAsync(this?IDatabase?db,?RedisKey?key,?RedisValue?value) {return?await?db.ScriptEvaluateAsync(StringSetWhenValueChangedLuaScript,?new[]?{?key?},?new[]?{?value?}).ContinueWith(r?=>?(int)r.Result?==?1); }public?static?bool?HashSetWhenValueChanged(this?IDatabase?db,?RedisKey?key,?RedisValue?field,?RedisValue?value) {return?(int)db.ScriptEvaluate(HashSetWhenValueChangedLuaScript,?new[]?{?key?},?new[]?{?field,?value?})?==?1; }public?static?async?Task<bool>?HashSetWhenValueChangedAsync(this?IDatabase?db,?RedisKey?key,?RedisValue?field,?RedisValue?value) {return?await?db.ScriptEvaluateAsync(HashSetWhenValueChangedLuaScript,?new[]?{?key?},?new[]?{?field,?value?}).ContinueWith(r?=>?(int)r.Result?==?1); }Sample
使用示例可以參考下面的測試用例:
[Fact] public?void?StringSetWhenValueChangedTest() {var?key?=?$"{nameof(StringSetWhenValueChangedTest)}";var?redis?=?DependencyResolver.Current.GetRequiredService<IConnectionMultiplexer>().GetDatabase();redis.StringSet(key,?1);//?update?to?1?if?now?is?not?1Assert.False(redis.StringSetWhenValueChanged(key,?1));Assert.Equal(1,?redis.StringGet(key));//?update?to?2?if?now?is?not?2Assert.True(redis.StringSetWhenValueChanged(key,?2));Assert.Equal(2,?redis.StringGet(key)); }[Fact] public?void?HashSetWhenValueChangedTest() {var?key?=?$"{nameof(HashSetWhenValueChangedTest)}";var?field?=?"testField";var?redis?=?DependencyResolver.Current.GetRequiredService<IConnectionMultiplexer>().GetDatabase();redis.HashSet(key,?field,?1);Assert.False(redis.HashSetWhenValueChanged(key,?field,?1));Assert.Equal(1,?redis.HashGet(key,?field));Assert.True(redis.HashSetWhenValueChanged(key,?field,?2));Assert.Equal(2,?redis.HashGet(key,?field)); }More
在使用 Lua 腳本的時(shí)候,如果要使用不等于的邏輯需要小心一些,和其他語言不同,需要使用 ~= 而非 != 來表示不等
References
https://github.com/WeihanLi/WeihanLi.Redis/blob/dev/src/WeihanLi.Redis/RedisExtensions.cs
https://github.com/WeihanLi/WeihanLi.Redis/blob/dev/test/WeihanLi.Redis.UnitTest/RedisExtensionsTest.cs
基于 Redis 實(shí)現(xiàn) CAS 操作
總結(jié)
以上是生活随笔為你收集整理的Redis 使用 Lua 脚本进行原子操作的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 提升心力---摆脱拿着锤子看啥都是钉子
- 下一篇: MySQL从原理到实践,一篇从头到尾讲清