本地消息表这种实现方式应该是业界使用最多的 , 其核心思想是将分布式事务拆分成本地事务进行处理 。
文章插图
对于本地消息队列来说 , 核心就是将大事务转变为小事务 , 还是用上面下订单扣库存的例子说明:
- 当我们去创建订单的时候 , 我们新增一个本地消息表 , 把创建订单和扣减库存写入到本地消息表 , 放在同一个事务(依靠数据库本地事务保证一致性) 。
- 配置一个定时任务去轮询这个本地事务表 , 扫描这个本地事务表 , 把没有发送出去的消息 , 发送给库存服务 , 当库存服务收到消息后 , 会进行减库存 , 并写入服务器的事务表 , 更新事务表的状态 。
- 库存服务器通过定时任务或直接通知订单服务 , 订单服务在本地消息表更新状态 。
本地消息队列是 BASE 理论 , 是最终一致性模型 , 适用对一致性要求不高的情况 。
④MQ 事务
RocketMQ 在 4.3 版本已经正式宣布支持分布式事务 , 在选择 RokcetMQ 做分布式事务请务必选择 4.3 以上的版本 。
RocketMQ 中实现了分布式事务 , 实际上是对本地消息表的一个封装 , 将本地消息表移动到了 MQ 内部 。
文章插图
事务消息作为一种异步确保型事务 , 将两个事务分支通过 MQ 进行异步解耦 , RocketMQ 事务消息的设计流程同样借鉴了两阶段提交理论 。
整体交互流程如下图所示:
文章插图
MQ 事务是对本地消息表的一层封装 , 将本地消息表移动到了 MQ 内部 , 所以也是基于 BASE 理论 , 是最终一致性模式 , 对强一致性要求不那么高的事务适用 , 同时 MQ 事务将整个流程异步化了 , 也非常适合在高并发情况下使用 。
RocketMQ 选择同步/异步刷盘 , 同步/异步复制 , 背后的 CP 和 AP 思考
虽然同步刷盘/异步刷盘 , 同步/异步复制 , 并没有对 CAP 直接的应用 , 但在配置的过程中也一样涉及到可用性和一致性的考虑 。
同步刷盘/异步刷盘
文章插图
RocketMQ 的消息是可以做到持久化的 , 数据会持久化到磁盘 , RocketMQ 为了提高性能 , 尽可能保证磁盘的顺序写入 。
消息在 Producer 写入 RocketMQ 的时候 , 有两种写入磁盘方式:
- 异步刷盘:消息快速写入到内存的 Pagecache , 就立马返回写成功状态 , 当内存的消息累计到一定程度的时候 , 会触发统一的写磁盘操作 。这种方式可以保证大吞吐量 , 但也存在着消息可能未存入磁盘丢失的风险 。
- 同步刷盘:消息快速写入内存的 Pagecahe , 立刻通知刷盘线程进行刷盘 , 等待刷盘完成之后 , 唤醒等待的线程 , 返回消息写成功的状态 。
文章插图
一个 Broker 组有 Master 和 Slave , 消息需要从 Master 复制到 Slave 上 , 所以有同步和异步两种复制方式:
- 同步复制:是等 Master 和 Slave 均写成功后才反馈给客户端写成功状态 。
- 异步复制:是只要 Master 写成功即可反馈给客户端写成功状态 。
同步复制的优点是可以保证一致性(一般通过两阶段提交协议) , 但是开销较大 , 可用性不好(参见 CAP 定理) , 带来了更多的冲突和死锁等问题 。
值得一提的是 Lazy+Primary/Copy 的复制协议在实际生产环境中是非常实用的 。
推荐阅读
- 泡茶不好喝 你的茶汤出尽了吗
- 爱喝茶的人常有的四条怪癖
- 每种茶都有自己的特性茶文化博大精深
- 茶叶香气的九大类型
- 这篇java的NIO编程,保证你能看懂
- 到底什么才是好茶
- 一篇全面的 MySQL 高性能优化实战总结
- 普洱散茶的冲泡技巧
- 武夷水仙和凤凰水仙有啥区别
- Python数据类型详解——元组