Redis如何使用乐观锁(CAS)保证数据一致性


Posted in Redis onMarch 25, 2022

场景

在 Redis 中经常会存在这么一种情况,读取某一个 key 的值,做一些业务逻辑处理,然后根据读取到的值来计算出一个新的值,重新 set 进去。

如果客户端 A 刚读取到 key 值,紧接着客户端 B 就修改这个 key 的值,那么就会存在并发安全的问题。

问题模拟

假设 Redis Server 有个键名为 test 的key,里面存放的是一个 json 数组 [1, 2, 3]。

Redis如何使用乐观锁(CAS)保证数据一致性

下面让我们模拟一下,客户端 A 与 客户端 B 同时访问修改的情况,代码如下:

客户端 A:

class RedisClientA(username: String, password: String, host: String, port: Int) {
    val jedis: Jedis

    init {
        val pool = JedisPool(JedisPoolConfig(), host, port)
        jedis = pool.resource
        jedis.auth(username, password)
    }

    fun update(key: String) {
        val idStr = jedis.get(key)
        val idList = Json.decodeFromString<MutableList<Int>>(idStr)

        // 等待2秒,模拟业务
        TimeUnit.SECONDS.sleep(2L)

        idList.add(4)
        println("new id list: $idList")

        jedis.set(key, Json.encodeToString(idList))
    }

    fun getVal(key: String): String? {
        return jedis.get(key)
    }
}

fun main() {
    val key = "test"
    val redisClientA = RedisClientA("default", "123456", "127.0.0.1", 6379)
    redisClientA.update(key)
    val res = redisClientA.getVal(key)
    println("res: $res")
}

客户端 B:

class RedisClientB(username: String, password: String, host: String, port: Int) {
    val jedis: Jedis

    init {
        val pool = JedisPool(JedisPoolConfig(), host, port)
        jedis = pool.resource
        jedis.auth(username, password)
    }

    fun update(key: String) {
        val idStr = jedis.get(key)
        val idList = Json.decodeFromString<MutableList<Int>>(idStr)

        idList.add(5)
        println("new id list: $idList")

        jedis.set(key, Json.encodeToString(idList))
    }

    fun getVal(key: String): String? {
        return jedis.get(key)
    }
}

fun main() {
    val key = "test"
    val redisClientB = RedisClientB("default", "123456", "127.0.0.1", 6379)
    redisClientB.update(key)
    val res = redisClientB.getVal(key)
    println("res: $res")
}

客户端 A 阻塞了 2 秒,用来模拟耗时业务逻辑的处理。正在处理的时候,客户端 B 访问了 “test”,并增加了 id:5。

在客户端 A 耗时业务逻辑处理完的时候,增加了 id:4,并且会覆盖掉 id:5。

最终“test” 里的内容最终如下:

Redis如何使用乐观锁(CAS)保证数据一致性

CAS 来保证数据一致性

WATCH 命令可以为 Redis 事务提供 check-and-set(CAS)行为。被 WATCH 的键会被监视,并会发觉这些键是否被改动过了。如果有至少一个被监视的建在 EXEC 执行之前被修改了,那么整个事务都会被取消,EXEC 返回空(Null replay)来表示事务执行失败。我们只需要重复操作,希望在这个时间段内不会有新的竞争。这种形式的锁被称作乐观锁,它是一种非常强大的锁机制。

那么 CAS 的方式如何实现呢?我们只需要把 RedisClientA 的 update() 方法中的代码修改如下:

fun update(key: String) {
    var flag = true

    while (flag) {
        jedis.watch(key)

        val idStr = jedis.get(key)
        val idList = Json.decodeFromString<MutableList<Int>>(idStr)

        // 等待2秒,模拟业务
        TimeUnit.SECONDS.sleep(2L)

        val transaction = jedis.multi()
        idList.add(4)
        println("new id list: $idList")

        transaction.set(key, Json.encodeToString(idList))

        transaction.exec()?.let {
            flag = false
        }
    }

}

最终 “test” 的内容如下:

Redis如何使用乐观锁(CAS)保证数据一致性

可见我们通过使用 WATCH 和 TRANACTION 命令,采用 CAS 乐观锁的方式实现了数据的一致性。

到此这篇关于Redis如何使用乐观锁(CAS)保证数据一致性的文章就介绍到这了,更多相关Redis 乐观锁保证数据一致性内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Redis 相关文章推荐
解决redis sentinel 频繁主备切换的问题
Apr 12 Redis
Redis IP地址的绑定的实现
May 08 Redis
使用redis生成唯一编号及原理示例详解
Sep 15 Redis
Redis中缓存穿透/击穿/雪崩问题和解决方法
Dec 04 Redis
分布式Redis Cluster集群搭建与Redis基本用法
Feb 24 Redis
redis数据结构之压缩列表
Mar 21 Redis
基于Redis6.2.6版本部署Redis Cluster集群的问题
Apr 01 Redis
redis调用二维码时的不断刷新排查分析
Apr 01 Redis
redis sentinel监控高可用集群实现的配置步骤
Apr 01 Redis
 Redis 串行生成顺序编码的方法实现
Apr 03 Redis
redis 解决库存并发问题实现数量控制
Apr 08 Redis
基于Redission的分布式锁实战
Aug 14 Redis
Redis 操作多个数据库的配置的方法实现
Mar 23 #Redis
Redis安装使用RedisJSON模块的方法
Mar 23 #Redis
解决redis批量删除key值的问题
Mar 23 #Redis
源码分析Redis中 set 和 sorted set 的使用方法
Redis监控工具RedisInsight安装与使用
在Centos 8.0中安装Redis服务器的教程详解
redis数据结构之压缩列表
Mar 21 #Redis
You might like
去掉destoon资讯内容页keywords关键字自带的文章标题的方法
2014/08/21 PHP
CentOS6.5 编译安装lnmp环境
2014/12/21 PHP
PHP构造二叉树算法示例
2017/06/21 PHP
让Laravel API永远返回JSON格式响应的方法示例
2018/09/05 PHP
JQuery实现自定义对话框的代码
2008/06/15 Javascript
jquery 获取自定义属性(attr和prop)的实现代码
2012/06/27 Javascript
jQuery层级选择器用法分析
2015/02/10 Javascript
jQuery经过一段时间自动隐藏指定元素的方法
2015/03/17 Javascript
Jquery异步提交表单代码分享
2015/03/26 Javascript
Javascript 实现放大镜效果实例详解
2016/12/03 Javascript
Vue制作Todo List网页
2017/04/26 Javascript
实例详解JavaScript中setTimeout函数的执行顺序
2017/07/12 Javascript
jQuery中过滤器的基本用法示例
2017/10/11 jQuery
Vue+webpack+Element 兼容问题总结(小结)
2018/08/16 Javascript
分享5个小技巧让你写出更好的 JavaScript 条件语句
2018/10/20 Javascript
微信小程序 搜索框组件代码实例
2019/09/06 Javascript
js实现弹窗猜数字游戏
2020/11/26 Javascript
[01:20]DOTA2上海特级锦标赛现场采访:谁的ID最受青睐
2016/03/25 DOTA
[01:14]DOTA2亚洲邀请赛小组赛赛前花絮
2017/03/27 DOTA
Python实现图片转字符画的示例
2017/08/22 Python
Python3实现发送QQ邮件功能(附件)
2020/12/23 Python
遗传算法python版
2018/03/19 Python
python bmp转换为jpg 并删除原图的方法
2018/10/25 Python
NumPy统计函数的实现方法
2020/01/21 Python
详解python爬取弹幕与数据分析
2020/11/14 Python
使用CSS3来实现滚动视差效果的教程
2015/08/24 HTML / CSS
GLAMGLOW格莱魅美国官网:美国知名的面膜品牌
2016/12/31 全球购物
模具数控专业自荐信
2014/01/27 职场文书
祖国在我心中演讲稿500字
2014/05/04 职场文书
环保倡议书格式范文
2014/05/14 职场文书
小学校园之星事迹材料
2014/05/16 职场文书
初中班级口号
2014/06/09 职场文书
个人批评与自我批评材料
2014/10/17 职场文书
利用Python将list列表写入文件并读取的方法汇总
2022/03/25 Python
SQL Server携程核心系统无感迁移到MySQL实战
2022/06/01 SQL Server
Win10加载疑难解答时出错发生意外错误的解决方法
2022/07/07 数码科技