基于 Redis 实现 CAS 操作

Intro

在 .NET 里并发情况下我们可以使用 Interlocked.CompareExchange 来实现 CAS (Compare And Swap) 操作,在分布式的情景下很多时候我们都会使用 Redis ,最近在改之前做的一个微信小游戏项目,之前是单机运行的,有些数据存储是基于内存的,直接基于对象操作的,最近要改成支持分布式的,于是引入了 redis,原本基于内存的数据就要迁移到 redis 中存储,原来的代码里有一些地方使用了 Interlocked.CompareExchange 来实现 CAS 操作,迁移到 redis 中之后也需要类似的功能,于是就想基于 redis 实现 CAS 操作。

CAS

CAS (Compare And Swap) 通常可以使用在并发操作中更新某一个对象的值,CAS 是无锁操作,CAS 相当于是一种乐观锁,而直接加锁相当于是悲观锁,所以相对来说 CAS 操作 是会比直接加锁更加高效的。(——个人理解)

Redis Lua

redis 从 2.6.0 版本开始支持 Lua 脚本,Lua 脚本的执行是原子性的,所以我们在实现基于 redis 的分布式锁释放锁的时候或者下面要介绍的实现CAS 操作的,要执行多个操作但是希望操作是原子操作的时候就可以借助 Lua 脚本来实现(也可以使用事务来做)

基于 Redis Lua 实现 CAS

String CAS Lua Script:

KEYS[1] 对应要操作的String 类型的 redis 缓存的 key,ARGV[1]对应要比较的值,值相同则更新成 ARGV[2],并返回 1,否则返回 0

if redis.call(""get"", KEYS[1]) == ARGV[1] thenredis.call(""set"", KEYS[1], ARGV[2])return 1
elsereturn 0
end

Hash CAS Lua Script:

KEYS[1] 对应要操作的 Hash 类型的 redis 缓存的 key,ARGV[1] 对应 Hash 的 field,ARGV[2]对应要比较的值,值相同则更新成 ARGV[3],并返回 1,否则返回 0

if redis.call(""hget"", KEYS[1], ARGV[1]) == ARGV[2] thenredis.call(""hset"", KEYS[1], ARGV[1], ARGV[3])return 1
elsereturn 0
end

基于 StackExchange.Redis 的实现

为了方便使用,基于 IDatabase 提供了几个方便使用的扩展方法,实现如下:

public static bool StringCompareAndExchange(this IDatabase db, RedisKey key, RedisValue newValue, RedisValue originValue)
{return (int)db.ScriptEvaluate(StringCasLuaScript, new[] { key }, new[] { originValue, newValue }) == 1;
}
public static async Task<bool> StringCompareAndExchangeAsync(this IDatabase db, RedisKey key, RedisValue newValue, RedisValue originValue)
{return await db.ScriptEvaluateAsync(StringCasLuaScript, new[] { key }, new[] { originValue, newValue }).ContinueWith(r => (int)r.Result == 1);
}
public static bool HashCompareAndExchange(this IDatabase db, RedisKey key, RedisValue field, RedisValue newValue, RedisValue originValue)
{return (int)db.ScriptEvaluate(HashCasLuaScript, new[] { key }, new[] { field, originValue, newValue }) == 1;
}
public static async Task<bool> HashCompareAndExchangeAsync(this IDatabase db, RedisKey key, RedisValue field, RedisValue newValue, RedisValue originValue)
{return await db.ScriptEvaluateAsync(HashCasLuaScript, new[] { key }, new[] { field, originValue, newValue }).ContinueWith(r => (int)r.Result == 1);
}

实际使用

使用可以参考下面的测试代码:

[Fact]
public void StringCompareAndExchangeTest()
{var key = "test:String:cas";var redis = DependencyResolver.Current.GetRequiredService<IConnectionMultiplexer>().GetDatabase();redis.StringSet(key, 1);// set to 3 if now is 2Assert.False(redis.StringCompareAndExchange(key, 3, 2));Assert.Equal(1, redis.StringGet(key));// set to 4 if now is 1Assert.True(redis.StringCompareAndExchange(key, 4, 1));Assert.Equal(4, redis.StringGet(key));redis.KeyDelete(key);
}
[Fact]
public void HashCompareAndExchangeTest()
{var key = "test:Hash:cas";var field = "testField";var redis = DependencyResolver.Current.GetRequiredService<IConnectionMultiplexer>().GetDatabase();redis.HashSet(key, field, 1);// set to 3 if now is 2Assert.False(redis.HashCompareAndExchange(key, field, 3, 2));Assert.Equal(1, redis.HashGet(key, field));// set to 4 if now is 1Assert.True(redis.HashCompareAndExchange(key, field, 4, 1));Assert.Equal(4, redis.HashGet(key, field));redis.KeyDelete(key);
}

References

  • https://redis.io/commands/eval

  • https://redisbook.readthedocs.io/en/latest/feature/scripting.html

  • 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 操作相关推荐

  1. 基于redis的cas集群配置(转)

    1.cas ticket统一存储 做cas集群首先需要将ticket拿出来,做统一存储,以便每个节点访问到的数据一致.官方提供基于memcached的方案,由于项目需要,需要做计入redis,根据官方 ...

  2. 基于redis的悲观锁

    所谓悲观锁:具有强烈的独占和排他特性.它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态.悲观锁的实现,往往依靠 ...

  3. 基于 Redis + Lua 脚本实现分布式锁,确保操作的原子性

    为了保证数据的争用安全,通常要采用锁机制控制. 如果是单应用部署,直接通过synchronized关键字修改方法,就能解决,但是如果是分布式的部署 该方法就不能解决这个问题啦,此时就引出了一个分布式锁 ...

  4. Redis Lua 脚本常用操作总结及实现 CAS 操作

    一.什么是 Lua ?   Lua 是一个小巧的脚本语言.它是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个由 R ...

  5. redis系列:基于redis的分布式锁

    一.介绍 这篇博文讲介绍如何一步步构建一个基于Redis的分布式锁.会从最原始的版本开始,然后根据问题进行调整,最后完成一个较为合理的分布式锁. 本篇文章会将分布式锁的实现分为两部分,一个是单机环境, ...

  6. 基于 Redis 的分布式锁到底安全吗?

    [完整版] 网上有关Redis分布式锁的文章可谓多如牛毛了,不信的话你可以拿关键词"Redis 分布式锁"随便到哪个搜索引擎上去搜索一下就知道了.这些文章的思路大体相近,给出的实现 ...

  7. 基于redis的乐观锁实践

    基于redis的事务机制以及watch指令(CAS)实现乐观锁的过程. 所谓乐观锁,就是利用版本号比较机制,只是在读数据的时候,将读到的数据的版本号一起读出来,当对数据的操作结束后,准备写数据的时候, ...

  8. 基于Redis的分布式锁真的安全吗?

    说明: 我前段时间写了一篇用consul实现分布式锁,感觉理解的也不是很好,直到我看到了这2篇写分布式锁的讨论,真的是很佩服作者严谨的态度, 把这种分布式锁研究的这么透彻,作者这种技术态度真的值得我好 ...

  9. 基于Redis的分布式锁到底安全吗?

    网上有关Redis分布式锁的文章可谓多如牛毛了,不信的话你可以拿关键词"Redis 分布式锁"随便到哪个搜索引擎上去搜索一下就知道了.这些文章的思路大体相近,给出的实现算法也看似合 ...

最新文章

  1. node--非阻塞式I/O,单线程,异步,事件驱动
  2. Anaconda中快速安装Tensorflow与Keras并在pycharm中完成相应配置(win10cpu版)
  3. haproxy实现discuz论坛的动静分离和负载均衡
  4. kettle的基本介绍
  5. 转载大神的一篇文章----【如何选择开源许可证?】
  6. silence丶你的名字
  7. BGP——软收敛(讲解+配置命令)
  8. 10突然只剩下c盘和d盘了_科普:为什么软件不能装C盘?会卡!这是真的吗?
  9. sklearn——model_selection——knn手写识别系统+iris分类
  10. 全球及中国锗行业发展规模与前景调查分析报告2022-2028年
  11. 快速学习navicat安装教程
  12. python selenium 键盘操作 常用
  13. 从0基础文科生到全国亚军,我的人工智能学习路径
  14. 零基础快速入行入职软件测试工程师
  15. 华为p40pro android11,华为P40Pro上手对比iPhone11Pro:差距拉开、黑马逆袭!
  16. Numpy库的安装与初次使用
  17. JavaScript中如何严格的判断NaN
  18. 撤回的消息服务器还可以看到,微信消息被撤回,居然还能看到!原来还有11个你不知道...
  19. Unsupported or unrecognized SSL message
  20. 上班在群里摸鱼,逮到一个字节10年测试开发,聊过之后羞愧难当...

热门文章

  1. php自动生成mysql的触发代码。
  2. linux top 命令---VIRT,RES,SHR,虚拟内存和物理内存(
  3. android之在view中内嵌浏览器的方法
  4. DNS resolving 占用大量日志
  5. 如何使用Windows搜索在任何文件中搜索文本
  6. keep-alive使用_如何使用Google Keep进行无忧笔记
  7. 转: javascript技术栈
  8. Oracle hang 之sqlplus -prelim使用方法
  9. Relaltek声卡在UBUNTU下没有声音的解决方法。
  10. 最好的FLV视频下载器 维棠 (支持优酷视频下载、土豆视频下载等)