文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Redis怎么使用乐观锁保证数据一致性

2023-06-29 16:53

关注

这篇文章主要介绍了Redis怎么使用乐观锁保证数据一致性,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。

场景

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

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

问题模拟

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

Redis怎么使用乐观锁保证数据一致性

下面让我们模拟一下,客户端 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 来保证数据一致性

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怎么使用乐观锁保证数据一致性

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

感谢你能够认真阅读完这篇文章,希望小编分享的“Redis怎么使用乐观锁保证数据一致性”这篇文章对大家有帮助,同时也希望大家多多支持编程网,关注编程网行业资讯频道,更多相关知识等着你来学习!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯