• 分布式锁选型+缓存db一致性


    基于Redis Cluster模式

    setnx就可以实现加锁,del实现解锁,但是这样不具备原子性,存在无法释放的可能。

    因此可以使用在加锁时增加过期时间命令,做到原子性的加锁并且可以自动释放。

    一些问题:

    • key的过期时间不能设置太长,避免其他线程阻塞
    • 可能出现误解锁,比如当前线程在锁期间没有完成,解锁时锁已经被别人占据,导致解掉别人的锁
    • 获取锁是非阻塞的,直接返回结果
    • 存在锁公平问题,需要自己实现

    解决:

    • 守护线程对当前任务进度进行监控,及时续过期时间,知道锁释放或任务完成
    • 释放验证,释放时比对线程id和锁的value,防止释放不属于自己的锁
    • 阻塞机制,只能通过代码比如死循环去实现
    • 公平机制需要依赖等待队列来实现

    可重入性需要自己开发,安全性方面可能丢失锁(redis主从复制)

    可以了解一下基于多节点的高可用分布式锁的算法 RedLock。

    缓存一致性问题解决讨论(增加思考能力)

    加入缓存就不可避免的引入数据一致性的问题,所以在这里讨论,先说结论,我在项目上使用的是旁路缓存模式——

    读策略:

    从缓存中读取数据;如果缓存命中,则直接返回数据;如果缓存不命中,则从数据库中查询数据;查询到数据后,将数据写入到缓存中,并且返回给用户。

    写策略:

    更新数据库中(HBASE,Redis)的记录;删除缓存记录。

    然后探讨一下各种模式的优缺点以及相应的解决方案。

    再来对比一下几种策略:

    一共分为四种

    先更新数据库,再更新缓存
    先更新数据库,再删除缓存
    先更新缓存,再更新数据库
    先删除缓存,再更新数据库
    
    • 1
    • 2
    • 3
    • 4

    先更新数据库,再更新缓存

    第一个问题是这个过程不具备原子性,可能存在

    a改库
    b改库
    b更新缓存
    a更新换粗
    
    • 1
    • 2
    • 3
    • 4

    导致出现了更改丢失的情况,同时每次更新db都去更新缓存,如果是写多读少的情况下,无疑是浪费感情。

    先更新数据库,再删除缓存

    问题:

    缓存失效
    a去查数据库
    b改库
    b删缓存
    a放置旧数据缓存
    
    • 1
    • 2
    • 3
    • 4
    • 5

    但是细想就会发现这个事情的概率比较低,因为并发的情况下,写库要比读库的耗时更长,正常情况下应该是b执行删缓存放到最后。

    另外更新可以看做先删后加,删除就只有一步,相当于一个惰性处理,即懒加载的思想,只有用到的时候才去加在,因为缓存的成本是较高的,尽量需要保证会被访问到才缓存。

    遗留问题,如果删缓存失败也会导致存在数据不一致问题,这种情况下可以依赖过期时间(缓存尽量不要设置为永久,如果能永久的我相信本地缓存或数据库更合适一些),允许一段时间的脏数据来达到最终一致性。

    或者再引入一个中间件,删除缓存失败的时候入mq,随机时间后进行重试(不推荐,搞这么复杂还是别用缓存了)

    先更新缓存,再更新数据库

    同样属于双更模式,双更模式带来的不确定性比较大,并非原子操作,不推荐。

    先删除缓存,再更新数据库

    可能删完马上又有读请求过来查库读到了脏数据放入缓存中,毕竟读快于写,不推荐

  • 相关阅读:
    SpringBoot学习11 - Spring-Aop(常用的切点表达式关键字Demo讲解演示)
    图解LeetCode——7. 整数反转(难度:中等)
    太阳能发电与蓄电池研究(Matlab代码实现)
    【云原生之k8s】K8s 管理工具 kubectl 详解(二)
    芯片工艺PVT STA分析 OCV分析
    HTML5Plus
    安全防御设备——防火墙总结【2】
    go进阶语法10问
    Dockershim即将被正式废弃,你准备好了吗?
    LeetCode_674_最长连续递增序列
  • 原文地址:https://blog.csdn.net/m0_56033865/article/details/136303760