Redis 使用 Lua 脚本进行原子操作

Intro

之前写过一篇文章也是 Redis 使用 LUA 脚本实现分布式的 CAS 操作,可以参考:基于 Redis 实现 CAS 操作

最近使用 Redis 的时候有一个需求,只有值发生变化的时候才更新,如果要更新的值和现在的值是一样的就不用更新,有点类似于 SET NX,只是 SET NX 只有值不存在的时候才会 SET,我的需求则是要检查要 SET 的值和 Redis 里的值,如果不一样就 SET,一样就直接返回

Implement

我实现了针对 StringHash 的 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 脚本进行原子操作相关推荐

  1. Redis进阶-lua脚本

    文章目录 Pre 语法 jedis操作lua 好处 lua实战 注意事项 Pre Redis在2.6推出了脚本功能,允许开发者使用Lua语言编写脚本传到Redis中执行. 语法 从Redis2.6.0 ...

  2. nx set 怎么实现的原子性_【redis进阶(1)】redis的Lua脚本控制(原子性)

    [toc] 为什么要用lua 减少网络开销:本来5次网络请求的操作,可以用一个请求完成,原先5次请求的逻辑放在redis服务器上完成.使用脚本,减少了网络往返时延. 原子操作:Redis会将整个脚本作 ...

  3. PHP中使用redis执行lua脚本示例

    一.引言 redis学了一段时间了,基本的东西都没问题了.从今天开始讲写一些redis和lua脚本的相关的东西,lua这个脚本是一个好东西,可以运行在任何平台上,也可以嵌入到大多数语言当中,来扩展其功 ...

  4. PHP中使用redis 执行lua脚本

    在php中,可以通过redis执行lua脚本 1.脚本 <?php $redis = new Redis(); #实例化redis类 $redis->connect('127.0.0.1' ...

  5. Redis 中 Lua 脚本的应用和实践

    引言 前段时间组内有个投票的产品,上线前考虑欠缺,导致被刷票严重.后来,通过研究,发现可以通过 redis lua 脚本实现限流,这里将 redis lua 脚本相关的知识分享出来,讲的不到位的地方还 ...

  6. 添加lua_非关系型数据库Redis之Lua脚本

    [本文详细介绍了非关系型数据库Redis中Lua脚本的基本概念和使用方法,欢迎读者朋友们阅读.转发和收藏!] 1 Lua 简介 Lua 是一个小巧的脚本语言,其设计目的是为了嵌入应用程序中,从而为应用 ...

  7. redis之lua脚本: 原子性 调试 嵌入高级语言

    实验环境: redis: 6.0.9 redis执行lua脚本时, 出错不会回滚(rollback) 我们知道, 使用lua脚本可以在执行一串redis命令时, 实现一定原子性(lua脚本中多条指令执 ...

  8. Redis学习笔记(八)redis之lua脚本学习

    redis系列文章目录 使用spring-data-redis实现incr自增 Redis 利用Hash存储节约内存 Redis学习笔记(九)redis实现时时直播列表缓存,支持分页[热点数据存储] ...

  9. Redis的Lua脚本总结

    Redis的Lua脚本总结 在redis操作中有时我们需要多条命令在执行时保证原子性,例如:使用redis做分布式锁时的加锁(判断key是否存在,不存在就set)和解锁(判断key是否存在并等于指定值 ...

最新文章

  1. 明日开播 | 7 场不可错过的 AI 技术专题
  2. 聊聊 HashMap 和 TreeMap 的内部结构
  3. R语言ggplot2可视化分面图(facet_grid):去除分面图灰色矩形框以及框中的标签、Getting rid of facet_grid labels on those gray boxes
  4. js类数组转数组的方法(ArrayLike)
  5. Swift3.0语言教程使用指针创建和初始化字符串
  6. docker报错:OCI runtime create failed...process_linux.go:449: container init caused “write /proc/self/
  7. 【HDU - 6186】CS Course(按位与,按位或,按位异或的区间统计,二进制拆位)
  8. Python中数组,列表,元组的区别、定义、功能
  9. java中进制转换_java中的进制转换
  10. oracle凭证编号,R12 AP模块的发票过账后如何关联对应的凭证编号
  11. Vegas18 pro视频编辑软件下载官方激活码-序列号-密钥版-注册
  12. PPT:人工智能在物流与供应链中的应用
  13. 2011盘点国内外24款免费个人防火墙
  14. ToString格式转换大全(1)
  15. wx:key的使用及wx:key的值
  16. mac怎么显示服务器中隐藏文件,mac怎么显示隐藏文件夹-mac显示隐藏文件命令与使用教程 - 河东软件园...
  17. nginx 静态资源优化配置
  18. 笨方法学python6-10
  19. android EditText 属性
  20. MFC详解显示BMP格式图片

热门文章

  1. SQLite入门之数据类型
  2. Quartz 的SB问题 GetNextValidTimeAfter 输出和输出 时区 不同步,好傻的方法?
  3. 转:编写高效的Android代码
  4. 在C#2.0中使用Nullable可空类型
  5. mysql数据库三大引擎优缺点
  6. SpringBoot集成Druid不支持多条SQL
  7. 解决WDCP3环境gbk网站编码程序乱码问题
  8. sybase sp_procxmode简述
  9. mongoDB 高级查询之取模查询$mod
  10. 微软输入法2010下载使用-IME2010下载使用