热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

五分钟学会分布式事务

从概念开始我们先从事务的定义开始。事务即一系列读存动作被当作一个执行单元,这些动作要么全成功,要么全失败,执行动作的过程中保证数据的隔离性

从概念开始

我们先从事务的定义开始。事务即一系列读存动作被当作一个执行单元,这些动作要么全成功,要么全失败,执行动作的过程中保证数据的隔离性和一致性。

我们抛离数据库这个特定场景,先假设一个数据存储设备,我们定义两个标准操作,一个读一个写。当写操作依赖于读到的数据时,执行的顺序决定了得到的结果。


当单线程时,任意读或写操作在这个数据容器上,他必然是符合上述所有的要求的。

 

当多线程的时候,任意读写,实际上就是导致标准的 race condition,大部分情况下我们是不知道执行结果的。对于单核cpu来说,多线程实际上是cpu模拟,可以理解成所有的操作是合并到一起顺序执行的。在我们不加以控制的前提下,合并的操作队列必然是相对之间无序的。要想多线程情况下,达到单线程一样的事务性。最简单粗暴的办法,就是保证所有请求串行。

 

保证所有请求串行执行的最简单粗暴的办法就是锁。任意线程操作的适合,上锁,只有这个线程的所有动作做完之后,才能开始下一线程提供的动作。这样一来不管多少事务并行过来,保证了组内的动作一定是串行的。多线程下的动作组的事务性也就保证了。实际上工程后来的进化也是这样的,把执行顺序会影响结果的操作锁住,强制线性执行。

 

对于数据库来说也是一样,要完成事务的特性,本质还是锁。数据库实际上把读锁和写锁是分开的,颗粒度更细。mvcc的本质也是锁,可以理解成利用 copy-on-write 让写锁独立出来,不影响其他的操作,数据库事务进化的本质就是对锁的优化,从表锁到行锁,不停的降低锁的颗粒度,针对不同的场景使用颗粒度更小的锁。

 

不一致性的由来

单数据库实例读写必然是高度一致性的。问题是,单实例,更确切来说是单实例MySQL,是很难扛住所有流量的。绝大部分web应用必然是读多写少的,针对这一系统,大部分业务都做了读写分离,主写从读。这样的情况下,主从实际上是有一个不一致窗口的。不去管这个一致性窗口有多么的小,只要经过网络这样的一个慢速设备,不一致窗口,实际上必然存在。所幸的事情是,大部分应用对这个不一致性是可以容忍的。

 

再随着业务的发展,单机甚至都可能扛不住所有流量。我们需要去分拆数据库。这时问题就更大了,这不仅仅是外部的问题了,核心的问题可能是,在在没有单机事务的庇护下,我们如何去实现一致性。

 

大部分人的第一个想法是,数据库 XA ,对于两个库,引入外部协调者,然后做2pc。这样真的可行么。我们仔细想一下,如果数据库扛不住流量分拆,那么必然是分拆到两个机器,那么原本在内存中进行的协调操作必须经过网络这个慢速设备,并且XA为了做2pc,必然加长锁, 系统正常性能一下子干掉9/10,还自带随机抽风。这样搞必然走不通。

 

另外一部分的高端解法是,CockroachDB, TiDB,各种基于F1的高精尖分布式数据库,彻底替换掉MySQL。公司最重要的部分,可能就是数据部分,完全替换数据库的做法,实际上容易踩坑,并且踩进去还得爬出来,最气的这坑是还是自己挖的。在笔者个人的看法里,一个文件系统的彻底成熟大概要经过5到10年的时间,ex4是差不多经过5年才成熟起来的。对于数据库系统想来也不会差太多。底层数据储存,可以尝试,不建议勇猛。不是这个方法不可行,只是可能需要再等上一段时间。但是从另一角度来说,长期来看,这种方案很可能是最优解法。

 

重新定义需要解决的问题

 分布式环境的著名问题是强CAP不可兼得。我们可以这样想一下这句话,分布式环境下,要不等数据同步一致再提供服务,要么我保证服务,不去管数据同步的问题。因为数据同步必然有时间窗口,所以强CAP 不可兼得。又因为服务可用对互联网公司应该说是最基本的要求。大部分场景下,互联网公司的选择都是强A弱C,追求最终一致性,保证高可用。(这里无意去争辩CAP是否过时,只希望能够表达清楚场景,如果对这里有疑问的话,需要想一下,心中所列反例到底是A还是HA)

 

任意一个系统,想要达成最终一致性,场景无非是两种。

第一个场景,某动作发生后,后续动作保证完成。

第二个场景,某动作发生后,中间动作硬性无法完成,那么需要回滚操作,并清除之前操作的副作用。

 

第一个场景非常适合消息队列,消息队列在处理这个场景的时候非常合适,消息队列天然具有编排服务的能力,单事件触发后续的多个服务,后续操作异步完成,不降低系统的吞吐的下限。消息做 at least once 的投递,消息消费对消息幂等,消息的发送机制利用单机事务结合事务消息,是对于这个场景非常优雅的解法。

 

第二个场景,更接近标准的事务场景,只不过因为跨实例,跨机器,单机事务是不可依靠的。

 

更确切的说,第二个场景就是分布式事务组件需要解决的问题。

 

设计与实现

我们基于 SAGAS 来模拟长事务,从而解决来解决上述的第二个场景。

SAGA是上个世纪80年代诞生的思想,当时想解决的问题,更多的是长事务的问题,因为事务过长,实际资源锁的时限也过长,资源损耗严重 。在现有微服务演化的前提下,我们需求的不就是可模拟的类似长事务的行为么,于是SAGAS就非常的适用于我们的场景。

 

SAGAS简单来说,用短的单机事务拼接长跨机事务,这一组单机事务我们称为事务组。当事务正常时正常处理,事务组执行中间有异常case时,反向补偿整个事务组。我们把事务组想象成一个状态机,那么最终要么在完成状态,要么最终在补偿成功状态。补偿善后完成后,可以认为系统到达最终一致性的状态。

 


 

我们依据上述抽象,设计开发落地了我们的分布式事务组件。

 

系统架构如下图:

 


 

分布式事务组件流程如下,当用户请求进入上层服务的时候,在当前线程上下文生成一个唯一的事务ID做标示事务组,rpc组件在上层服务中调用原子层时,会把事务组内所有对远程的原子层的调用,记录为一个事务组,持久化到远端储存层。

 

事务补偿器运行状态机,找到需要补偿的事务组,多次尝试补偿调用,直至成功为止,事务组补偿组间并行,组内串行,补偿调用经过限流器,防止后端雪崩。

 

这样,原子层通过事务保证一致性,逻辑层通过记录和补偿服务保证每一次逻辑层作业都会到达最终一致的状态。事务组件请求的耗损仅为持久化远程调用的时间加上初始化事务组的时间。对于业务有即时强一致性的要求的场景,实际上是业务需要一个分布式锁来保证某一属性是不存在一致性窗口的。由于补偿服务本身不含有状态,水平扩展是非常容易的。另一方面来说,因为事务组实际上是通过事务ID标示,我们可以通过事务ID去把整个事务的补偿流程串起来,可视化分析也非常的容易。

  

总结

1.事务本质还是并发和锁的问题。

2.分布式环境下,一般情况一致性需要为可用性牺牲,保障最终一致性,两种场景,两种手段。

3.基于 sagapattern 模拟长事务来解决分布式事务问题。

4.转转分布式事务组件架构。

 

参考

1.https://www.cs.cornell.edu/andru/cs711/2002fa/reading/sagas.pdf

2. http://www.bailis.org/papers/hat-vldb2014.pdf

3. Reactive Microservices Architecture Jonas Bonér

 

 

转载请连同下方内容一起转载

-------------

观看架构师之美其他文章,请通过微信右上角关注功能 或者 长按下面二维码中间图标关注我们!

-------------

  1. 设计满足业务的架构、大数据平台、合适算法,并非易事。

在这里分享我们的思考和实践,让我们在互联网架构设计、大数据开发、机器学习实践之路上共同成长!

-------------

 请关注我们!

 



推荐阅读
  • 篇首语:本文由编程笔记#小编为大家整理,主要介绍了软件测试知识点之数据库压力测试方法小结相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • Tomcat/Jetty为何选择扩展线程池而不是使用JDK原生线程池?
    本文探讨了Tomcat和Jetty选择扩展线程池而不是使用JDK原生线程池的原因。通过比较IO密集型任务和CPU密集型任务的特点,解释了为何Tomcat和Jetty需要扩展线程池来提高并发度和任务处理速度。同时,介绍了JDK原生线程池的工作流程。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • MySQL中的MVVC多版本并发控制机制的应用及实现
    本文介绍了MySQL中MVCC的应用及实现机制。MVCC是一种提高并发性能的技术,通过对事务内读取的内存进行处理,避免写操作堵塞读操作的并发问题。与其他数据库系统的MVCC实现机制不尽相同,MySQL的MVCC是在undolog中实现的。通过undolog可以找回数据的历史版本,提供给用户读取或在回滚时覆盖数据页上的数据。MySQL的大多数事务型存储引擎都实现了MVCC,但各自的实现机制有所不同。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • 安装mysqlclient失败解决办法
    本文介绍了在MAC系统中,使用django使用mysql数据库报错的解决办法。通过源码安装mysqlclient或将mysql_config添加到系统环境变量中,可以解决安装mysqlclient失败的问题。同时,还介绍了查看mysql安装路径和使配置文件生效的方法。 ... [详细]
  • 本文详细介绍了MysqlDump和mysqldump进行全库备份的相关知识,包括备份命令的使用方法、my.cnf配置文件的设置、binlog日志的位置指定、增量恢复的方式以及适用于innodb引擎和myisam引擎的备份方法。对于需要进行数据库备份的用户来说,本文提供了一些有价值的参考内容。 ... [详细]
  • 数据库(外键及其约束理解)(https:www.cnblogs.comchenxiaoheip6909318.html)My ... [详细]
  • 本文介绍了高校天文共享平台的开发过程中的思考和规划。该平台旨在为高校学生提供天象预报、科普知识、观测活动、图片分享等功能。文章分析了项目的技术栈选择、网站前端布局、业务流程、数据库结构等方面,并总结了项目存在的问题,如前后端未分离、代码混乱等。作者表示希望通过记录和规划,能够理清思路,进一步完善该平台。 ... [详细]
  • 解决VS写C#项目导入MySQL数据源报错“You have a usable connection already”问题的正确方法
    本文介绍了在VS写C#项目导入MySQL数据源时出现报错“You have a usable connection already”的问题,并给出了正确的解决方法。详细描述了问题的出现情况和报错信息,并提供了解决该问题的步骤和注意事项。 ... [详细]
  • Linux如何安装Mongodb的详细步骤和注意事项
    本文介绍了Linux如何安装Mongodb的详细步骤和注意事项,同时介绍了Mongodb的特点和优势。Mongodb是一个开源的数据库,适用于各种规模的企业和各类应用程序。它具有灵活的数据模式和高性能的数据读写操作,能够提高企业的敏捷性和可扩展性。文章还提供了Mongodb的下载安装包地址。 ... [详细]
  • 一次上线事故,30岁+的程序员踩坑经验之谈
    本文主要介绍了一位30岁+的程序员在一次上线事故中踩坑的经验之谈。文章提到了在双十一活动期间,作为一个在线医疗项目,他们进行了优惠折扣活动的升级改造。然而,在上线前的最后一天,由于大量数据请求,导致部分接口出现问题。作者通过部署两台opentsdb来解决问题,但读数据的opentsdb仍然经常假死。作者只能查询最近24小时的数据。这次事故给他带来了很多教训和经验。 ... [详细]
author-avatar
sx-March23
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有