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遍历所有key的两个命令(KEYS 和 SCAN)
Apr 12 Redis
详解Redis集群搭建的三种方式
May 31 Redis
压缩Redis里的字符串大对象操作
Jun 23 Redis
聊一聊Redis与MySQL双写一致性如何保证
Jun 26 Redis
在redisCluster中模糊获取key方式
Jul 09 Redis
Redis调用Lua脚本及使用场景快速掌握
Mar 16 Redis
redis击穿 雪崩 穿透超详细解决方案梳理
Mar 17 Redis
Redis 操作多个数据库的配置的方法实现
Mar 23 Redis
Redis数据同步之redis shake的实现方法
Apr 21 Redis
Redis全局ID生成器的实现
Jun 05 Redis
使用Redis实现分布式锁的方法
Jun 16 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
Php部分常见问题总结
2006/10/09 PHP
关于PHP二进制流 逐bit的低位在前算法(详解)
2013/06/13 PHP
浅谈Laravel模板实体转义带来的坑
2019/10/22 PHP
javascript web对话框与弹出窗口
2009/02/22 Javascript
仿淘宝TAB切换搜索框搜索切换的相关内容
2014/09/21 Javascript
关于获取DIV内部内容报错的原因分析及解决办法
2016/01/29 Javascript
node.js微信公众平台开发教程
2016/03/04 Javascript
javascript与jquery动态创建html元素示例
2016/07/25 Javascript
JS实现类似百叶窗下拉菜单效果
2016/12/30 Javascript
Vue.js实现移动端短信验证码功能
2017/03/29 Javascript
ES6中字符串string常用的新增方法小结
2017/11/07 Javascript
彻底理解js面向对象之继承
2018/02/04 Javascript
微信小程序实现天气预报功能
2018/07/18 Javascript
解决vue-cli单页面手机应用input点击手机端虚拟键盘弹出盖住input问题
2018/08/25 Javascript
基于Vue和Element-Ui搭建项目的方法
2019/09/06 Javascript
angular8和ngrx8结合使用的步骤介绍
2019/12/01 Javascript
Vue获取页面元素的相对位置的方法示例
2020/02/05 Javascript
vue-video-player 解决微信自动全屏播放问题(横竖屏导致样式错乱问题)
2020/02/25 Javascript
JavaScript设计模式之策略模式实现原理详解
2020/05/29 Javascript
react实现复选框全选和反选组件效果
2020/08/25 Javascript
举例讲解Python中的身份运算符的使用方法
2015/10/13 Python
Python简单格式化时间的方法【strftime函数】
2016/09/18 Python
python爬虫入门教程--快速理解HTTP协议(一)
2017/05/25 Python
使用Python计算玩彩票赢钱概率
2019/06/26 Python
Python 分享10个PyCharm技巧
2019/07/13 Python
python 公共方法汇总解析
2019/09/16 Python
浅谈python输出列表元素的所有排列形式
2020/02/26 Python
python GUI库图形界面开发之PyQt5布局控件QHBoxLayout详细使用方法与实例
2020/03/06 Python
pycharm安装及如何导入numpy
2020/04/03 Python
15款Python编辑器的优缺点,别再问我“选什么编辑器”啦
2020/10/19 Python
好药师网上药店:安全合法的网上药品零售药房
2017/02/15 全球购物
vue实现倒计时功能
2021/03/24 Vue.js
应届大学生自荐书
2014/06/17 职场文书
工作批评与自我批评范文
2014/10/16 职场文书
2016年秋季新学期致辞
2015/07/30 职场文书
2016年劳模先进事迹材料
2016/02/25 职场文书