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限流的实际应用
Apr 24 Redis
Redis6.0搭建集群Redis-cluster的方法
May 08 Redis
Redis实现订单自动过期功能的示例代码
May 08 Redis
Redis数据结构之链表与字典的使用
May 11 Redis
在redisCluster中模糊获取key方式
Jul 09 Redis
Redis中有序集合的内部实现方式的详细介绍
Mar 16 Redis
Redis分布式锁的7种实现
Apr 01 Redis
Redis中key的过期删除策略和内存淘汰机制
Apr 12 Redis
muduo TcpServer模块源码分析
Apr 26 Redis
windows安装 redis 6.2.6最新步骤详解
Apr 26 Redis
如何使用注解方式实现 Redis 分布式锁
Jul 23 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 在文件指定行插入数据的代码
2010/05/08 PHP
php中的filesystem文件系统函数介绍及使用示例
2014/02/13 PHP
smarty简单应用实例
2015/11/03 PHP
Symfony2学习笔记之插件格式分析
2016/03/17 PHP
老生常谈PHP面向对象之标识映射
2017/06/21 PHP
php利用ob_start()清除输出和选择性输出的方法
2018/01/18 PHP
javascript:;与javascript:void(0)使用介绍
2013/06/05 Javascript
JavaScript动态操作表格实例(添加,删除行,列及单元格)
2013/11/25 Javascript
JS+CSS实现经典的左侧竖向滑动菜单效果
2015/09/23 Javascript
JS Attribute属性操作详解
2016/05/19 Javascript
jQuery的ajax下载blob文件
2016/07/21 Javascript
JS作用域深度解析
2016/12/29 Javascript
JavaScript基础之AJAX简单的小demo
2017/01/29 Javascript
纯js的右下角弹窗实例
2017/03/12 Javascript
JS实现批量上传文件并显示进度功能
2017/06/27 Javascript
原生JS实现 MUI导航栏透明渐变效果
2017/11/07 Javascript
jquery实现楼层滚动效果
2018/01/01 jQuery
JavaScript文档加载模式以及元素获取
2020/07/28 Javascript
Python subprocess模块学习总结
2014/03/13 Python
Python中的zip函数使用示例
2015/01/29 Python
django中的图片验证码功能
2019/09/18 Python
Python 分布式缓存之Reids数据类型操作详解
2020/06/24 Python
termux中matplotlib无法显示中文问题的解决方法
2021/01/11 Python
浅谈基于Canvas的手绘风格图形库Rough.js
2018/03/19 HTML / CSS
html5 figure和figcaption的使用方法
2018/09/10 HTML / CSS
英国内衣连锁店:Boux Avenue
2018/01/24 全球购物
LEGO玩具英国官方商店:LEGO Shop GB
2018/03/27 全球购物
Trina Turk官网:美国时装和泳装品牌
2018/06/10 全球购物
最畅销的视频游戏享受高达90%的折扣:CDKeys
2020/02/10 全球购物
统计员岗位职责
2013/11/14 职场文书
最新的咖啡店创业计划书
2013/12/30 职场文书
婚礼证婚人证婚词
2014/01/13 职场文书
亲子读书活动方案
2014/02/22 职场文书
模具专业求职信
2014/06/26 职场文书
死者家属慰问信
2015/03/24 职场文书
python编程项目中线上问题排查与解决
2021/11/01 Python