Redis 使用 Lua 脚本进行原子操作
Redis 使用 Lua 脚本进行原子操作
Intro
之前写过一篇文章也是 Redis 使用 LUA 脚本实现分布式的 CAS 操作,可以参考:基于 Redis 实现 CAS 操作
最近使用 Redis 的时候有一个需求,只有值发生变化的时候才更新,如果要更新的值和现在的值是一样的就不用更新,有点类似于 SET NX
,只是 SET NX
只有值不存在的时候才会 SET
,我的需求则是要检查要 SET
的值和 Redis 里的值,如果不一样就 SET
,一样就直接返回
Implement
我实现了针对 String
和 Hash
的 SET 检查,核心就是我们的 Lua 脚本
实现代码如下:
对于 Hash 会多一个参数 —— hash field name, 对于 string 则直接是 value 了,就会比 hash 少一个参数
private const string HashSetWhenValueChangedLuaScript = @"
if redis.call(""HGET"", KEYS[1], ARGV[1]) == ARGV[2] thenreturn 0
elseredis.call(""HSET"", KEYS[1], ARGV[1], ARGV[2])return 1
end
";private const string StringSetWhenValueChangedLuaScript = @"
if redis.call(""GET"", KEYS[1]) == ARGV[1] thenreturn 0
elseredis.call(""SET"", KEYS[1], ARGV[1])return 1
end
";
实现起来也比较简单,就是先取一下 Redis 中的数据,如果和输入的值是一样就返回 0,不一样则更新值,然后返回 1
StackExchange.Redis
使用 API
在 StackExchange.Redis
中可以使用 ScriptEvaluate
/ScriptEvaluateAsync
来执行 Lua 脚本,为了方便使用我把他们封装成了扩展方法,实现如下:
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 脚本的时候,如果要使用不等于的逻辑需要小心一些,和其他语言不同,需要使用 ~=
而非 !=
来表示不等
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 实现 CAS 操作
Redis 使用 Lua 脚本进行原子操作相关推荐
- Redis进阶-lua脚本
文章目录 Pre 语法 jedis操作lua 好处 lua实战 注意事项 Pre Redis在2.6推出了脚本功能,允许开发者使用Lua语言编写脚本传到Redis中执行. 语法 从Redis2.6.0 ...
- nx set 怎么实现的原子性_【redis进阶(1)】redis的Lua脚本控制(原子性)
[toc] 为什么要用lua 减少网络开销:本来5次网络请求的操作,可以用一个请求完成,原先5次请求的逻辑放在redis服务器上完成.使用脚本,减少了网络往返时延. 原子操作:Redis会将整个脚本作 ...
- PHP中使用redis执行lua脚本示例
一.引言 redis学了一段时间了,基本的东西都没问题了.从今天开始讲写一些redis和lua脚本的相关的东西,lua这个脚本是一个好东西,可以运行在任何平台上,也可以嵌入到大多数语言当中,来扩展其功 ...
- PHP中使用redis 执行lua脚本
在php中,可以通过redis执行lua脚本 1.脚本 <?php $redis = new Redis(); #实例化redis类 $redis->connect('127.0.0.1' ...
- Redis 中 Lua 脚本的应用和实践
引言 前段时间组内有个投票的产品,上线前考虑欠缺,导致被刷票严重.后来,通过研究,发现可以通过 redis lua 脚本实现限流,这里将 redis lua 脚本相关的知识分享出来,讲的不到位的地方还 ...
- 添加lua_非关系型数据库Redis之Lua脚本
[本文详细介绍了非关系型数据库Redis中Lua脚本的基本概念和使用方法,欢迎读者朋友们阅读.转发和收藏!] 1 Lua 简介 Lua 是一个小巧的脚本语言,其设计目的是为了嵌入应用程序中,从而为应用 ...
- redis之lua脚本: 原子性 调试 嵌入高级语言
实验环境: redis: 6.0.9 redis执行lua脚本时, 出错不会回滚(rollback) 我们知道, 使用lua脚本可以在执行一串redis命令时, 实现一定原子性(lua脚本中多条指令执 ...
- Redis学习笔记(八)redis之lua脚本学习
redis系列文章目录 使用spring-data-redis实现incr自增 Redis 利用Hash存储节约内存 Redis学习笔记(九)redis实现时时直播列表缓存,支持分页[热点数据存储] ...
- Redis的Lua脚本总结
Redis的Lua脚本总结 在redis操作中有时我们需要多条命令在执行时保证原子性,例如:使用redis做分布式锁时的加锁(判断key是否存在,不存在就set)和解锁(判断key是否存在并等于指定值 ...
最新文章
- 明日开播 | 7 场不可错过的 AI 技术专题
- 聊聊 HashMap 和 TreeMap 的内部结构
- R语言ggplot2可视化分面图(facet_grid):去除分面图灰色矩形框以及框中的标签、Getting rid of facet_grid labels on those gray boxes
- js类数组转数组的方法(ArrayLike)
- Swift3.0语言教程使用指针创建和初始化字符串
- docker报错:OCI runtime create failed...process_linux.go:449: container init caused “write /proc/self/
- 【HDU - 6186】CS Course(按位与,按位或,按位异或的区间统计,二进制拆位)
- Python中数组,列表,元组的区别、定义、功能
- java中进制转换_java中的进制转换
- oracle凭证编号,R12 AP模块的发票过账后如何关联对应的凭证编号
- Vegas18 pro视频编辑软件下载官方激活码-序列号-密钥版-注册
- PPT:人工智能在物流与供应链中的应用
- 2011盘点国内外24款免费个人防火墙
- ToString格式转换大全(1)
- wx:key的使用及wx:key的值
- mac怎么显示服务器中隐藏文件,mac怎么显示隐藏文件夹-mac显示隐藏文件命令与使用教程 - 河东软件园...
- nginx 静态资源优化配置
- 笨方法学python6-10
- android EditText 属性
- MFC详解显示BMP格式图片