• 分布式事务相关


    分布式事务问题

    背景

    • 积分支付,需要查询用户积分;
    • 商品扣减库存;
    • 保存订单;

    这些一定要保证一致性;

    解决办法有

    • 基于XA协议的两阶段提交
    • 事务补偿机制
    • 本地消息表+定时任务
    • MQ

    XA协议

    一个事务管理器和多个资源管理器组成

    提交分为两阶段

    • prepare
    • commit

    第一阶段

    https://img2020.cnblogs.com/blog/3327/202103/3327-20210328134525770-628211280.png

     第二阶段

    https://img2020.cnblogs.com/blog/3327/202103/3327-20210328134715095-1131354898.png

    这个需要事务管理器给所有RM发送准备指令和执行指令,效率特别低。

    DEMO:

    MySql5.7支持XA,尽量使用8.0版本

    Mysql连接器,Mysql Connector/J 5.0以上支持

    Atomikos做分布式事务,管理分布式数据库

    分库分表的组件:MyCat和Sharding-JDBC这两个都已经集成Atomikos了,直接使用就可以。

    事务补偿机制

    我们现在用的这个,分布式事务处理,就是事务补偿机制的原理。嗯,这个存在的问题就是有可能A扣减了,B也扣减了,但是B扣钱失败了。然后此时A回滚的时候会有问题,嗯,这是一个待解决的一块儿。

    在catch里写回滚逻辑。涉及到重试,这块有一些复杂,具体怎么处理才能尽可能一致?

    本地消息表+定时任务

    这个就是落一个表,定时任务扫表处理

    基于MQ

    不同公司之间,因为机房不一致,就无法使用MQ来达到消息一致性,可以使用本地消息表,推送信息去处理,例如对接支付宝微信。

    消息的重试

    rocketmq有messageExt.getReconsumeTimes()可以记录重试次数,可以达到一定次数后进行consume,然后报警,人工处理。

    幂等

    怎么防止重复下单

    表单怎么防止重复提交

    接口重复调用,重试,怎么幂等

    UPDATE时,使用乐观锁

    insert使用版本号,可以是token

    混合操作,可以使用分布式锁来锁住业务单号,如果没有业务单号,也是可以用token

     UPDATE幂等

    列表更新时,可以使用数据的version来做乐观锁,加上数据库的行锁来保证。

    insert幂等

    有唯一业务号的insert操作,例如下单场景,商品id+用户id ,

    分布式锁来保证幂等,保证接口幂等,

    这个也可以作为一个唯一索引到数据库,

    业务执行后,不进行锁的释放,过期自动释放。

    没有这种唯一业务号的就用token作为key来获取分布式锁。

    redis缓存穿透

    现象:

    1. 请求查询缓存,缓存不存在。
    2. 查询数据库,数据库也不存在这条数据,空则不写入缓存。
    3. 若相同查询条件查询了多次,则数据库压力大。

    解决办法

    1. 数据库查询为空,也写入缓存一个空值,设置上过期时间。>>这个比较好<<
    2. 加分布式锁,同时只能有一个请求查询数据库。
    3. 限流,只能少量数据查询数据库。
    4. 布隆过滤器,能迅速判断一个元素是否在集合里。二进制,空间小速度快。不存在就直接返回null就可以了。

    redis缓存雪崩

    现象

      缓存都会存在雪崩的现象,很多的缓存key,过期时间大面积失效,同时请求都会打到数据库上。

    防护措施(不能做到完全解决)

    1. 热点数据,永不过期
    2. 多缓存结合,长短缓存
      1. 请求redis
      2. 请求memcache,这个过期时间更久
    3. 过期时间错开,加一个失效时间随机数
    4. 开发时可以使用第三方redis,提高可靠性。
      1. 阿里云

    数据库和缓存双写一致性方案解析

    https://www.cnblogs.com/rjzheng/p/9041659.html#!comments 

    文章中:

    对缓存操作只是尽最大努力即可。也就是说如果数据库写成功,缓存更新失败,那么只要到达过期时间,则后面的读请求自然会从数据库中读取新值然后回填缓存

    1.先更新数据库,再更新缓存(这个不好)

    并发情况下,更新缓存的线程有可能是老数据的线程,存在脏数据。

    写多场景,浪费性能。

    计算后写入缓存场景,也浪费性能。

    (2和3是争议较大的)

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

    A删了B马上又写入,不设过期时间不做更新的话就永远不一致。

    需要用:延时双删策略

    (1)先淘汰缓存
    (2)再写数据库(这两步和原来一样)
    (3)休眠n秒,再次淘汰缓存

    (3)的这个n需要根据处理时常判断,读写分离库需要额外时间。

    (3)的这个删除也可以异步删除,增加吞吐,但还是存在删除失败的问题。

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

    2和3都会存在删除失败的问题。提供一个保障重试机制

    方案1:发送消息,继续删除key,直至成功;代码侵入多,不好;

    方案2:使用binlog订阅方法:在订阅逻辑中使用1,删除不成功后,继续发消息删除。

    数据库写成功后会有binlog写入

    binlog 具体见:todo 待补充

    上述的订阅binlog程序在mysql中有现成的中间件叫canal

  • 相关阅读:
    数组与指针
    壁纸
    2019/1/11
    指针A
    数组B
    一起来抓老鼠啊!快乐呀!
    打印沙漏
    I think I need a boat house
    币值转换
    《C语言程序设计》编程总结汇总
  • 原文地址:https://www.cnblogs.com/jiangym/p/15881521.html
Copyright © 2020-2023  润新知