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分布式锁的这些坑
May 19 Redis
Redis 彻底禁用RDB持久化操作
Jul 09 Redis
Redisson实现Redis分布式锁的几种方式
Aug 07 Redis
基于Redis的List实现特价商品列表功能
Aug 30 Redis
Redis 持久化 RDB 与 AOF的执行过程
Nov 07 Redis
CentOS8.4安装Redis6.2.6的详细过程
Nov 20 Redis
Redis监控工具RedisInsight安装与使用
Mar 21 Redis
解决 Redis 秒杀超卖场景的高并发
Apr 12 Redis
windows安装 redis 6.2.6最新步骤详解
Apr 26 Redis
Redis实现分布式锁的五种方法详解
Jun 14 Redis
redis lua限流算法实现示例
Jul 15 Redis
Redis配置外网可访问(redis远程连接不上)的方法
Dec 24 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 与 UTF-8 的最佳实践详细介绍
2017/01/04 PHP
php-fpm.conf配置文件中文说明详解及重要参数说明
2018/10/10 PHP
Laravel6.2中用于用户登录的新密码确认流程详解
2019/10/16 PHP
PHP 实现重载
2021/03/09 PHP
document.all与WEB标准
2020/05/13 Javascript
javascript 对象定义方法 简单易学
2009/03/22 Javascript
javascript eval和JSON之间的联系
2009/12/31 Javascript
javascript基础知识大集锦(二) 推荐收藏
2011/01/13 Javascript
关于图片按比例自适应缩放的js代码
2011/10/30 Javascript
JavaScript中的作用域链和闭包
2012/06/30 Javascript
js 图片随机不定向浮动的实现代码
2013/07/02 Javascript
Jquery自定义button按钮的几种方法
2014/06/11 Javascript
JavaScript实现的链表数据结构实例
2015/04/02 Javascript
基于jQuery实现的扇形定时器附源码下载
2015/10/20 Javascript
Vue中引入样式文件的方法
2017/08/18 Javascript
Redux 和 Mobx的选择问题:让你不再困惑!
2017/09/18 Javascript
JavaScript常用截取字符串的三种方式用法区别实例解析
2018/05/15 Javascript
node跨域转发 express+http-proxy-middleware的使用
2018/05/31 Javascript
vue3.0 CLI - 2.3 - 组件 home.vue 中学习指令和绑定
2018/09/14 Javascript
详解Vue-axios 设置请求头问题
2018/12/06 Javascript
Vue动态加载图片在跨域时无法显示的问题及解决方法
2020/03/10 Javascript
[36:02]DOTA2上海特级锦标赛D组小组赛#2 Liquid VS VP第一局
2016/02/28 DOTA
Python查看多台服务器进程的脚本分享
2014/06/11 Python
pandas的唯一值、值计数以及成员资格的示例
2018/07/25 Python
浅谈python str.format与制表符\t关于中文对齐的细节问题
2019/01/14 Python
Python常见的pandas用法demo示例
2019/03/16 Python
Python Numpy数组扩展repeat和tile使用实例解析
2019/12/09 Python
Timberland俄罗斯官方网上商店:全球领先的户外品牌
2020/03/15 全球购物
生物化学研究助理员求职信
2013/10/09 职场文书
商业项目策划方案
2014/06/05 职场文书
环保志愿者活动总结
2014/06/27 职场文书
一次性工伤赔偿协议书范本
2014/11/25 职场文书
留学推荐信中文范文
2015/03/26 职场文书
房产销售员2015年终工作总结
2015/10/22 职场文书
hive数据仓库新增字段方法
2022/06/25 数据库