- 💖大家好,我是 爪哇小白2021。
- 半路出家的程序员,在从事开发之前在 某省测绘院 玩了一年的飞机,机缘巧合之下发现了更有趣的事情,从此步入了编程的世界… ps:同时也是一个热爱旅行的航拍小能手!
- 💬 目的:记录自己的学习历程,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流 🖊
事务简介
事务(Transaction)是访问并可能更新数据库中各种数据项的一个程序执行单元(unit)。在关系数据库中,一个事务由一组SQL语句组成。事务应该具有4个属性:原子性、一致性、隔离性、持久性。这四个属性通常称为ACID特性。
- 原子性(atomicity):事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。
- 一致性(consistency):事务必须是使数据库从一个一致性状态变到另一个一致性状态,事务的中间状态不能被观察到的。
- 隔离性(isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。隔离性又分为四个级别:读未提交(read uncommitted)、读已提交(read committed,解决脏读)、可重复读(repeatable read,解决虚读)、串行化(serializable,解决幻读)。
- 持久性(durability):持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
任何事务机制在实现时,都应该考虑事务的ACID特性,包括:本地事务、分布式事务,即使不能都很好的满足,也要考虑支持到什么程度。
本地事务
大多数场景下,我们的应用都只需要操作单一的数据库,这种情况下的事务称之为本地事务(Local Transaction)。本地事务的ACID特性是数据库直接提供支持。本地事务应用架构如下所示:
在JDBC编程中,我们通过java.sql.Connection对象来开启、关闭或者提交事务。代码如下所示:
Connection conn = ... //获取数据库连接
conn.setAutoCommit(false); //开启事务
try{
//...执行增删改查sql
conn.commit(); //提交事务
}catch (Exception e) {
conn.rollback();//事务回滚
}finally{
conn.close();//关闭链接
}
分布式事务典型场景
当下互联网发展如火如荼,绝大部分公司都进行了数据库拆分和服务化(SOA)。在这种情况下,完成某一个业务功能可能需要横跨多个服务,操作多个数据库。这就涉及到到了分布式事务,用需要操作的资源位于多个资源服务器上,而应用需要保证对于多个资源服务器的数据的操作,要么全部成功,要么全部失败。本质上来说,分布式事务就是为了保证不同资源服务器的数据一致性。
典型的分布式事务场景:
跨库事务
跨库事务指的是,一个应用某个功能需要操作多个库,不同的库中存储不同的业务数据。我见过一个相对比较复杂的业务,一个业务中同时操作了9个库。下图演示了一个服务同时操作2个库的情况:
分库分表
通常一个库数据量比较大或者预期未来的数据量比较大,都会进行水平拆分,也就是分库分表。如下图,将数据库B拆分成了2个库:
对于分库分表的情况,一般开发人员都会使用一些数据库中间件来降低sql操作的复杂性。
如,对于sql:insert into user(id,name) values (1,“张三”),(2,“李四”)。
这条sql是操作单库的语法,单库情况下,可以保证事务的一致性。
但是由于现在进行了分库分表,开发人员希望将1号记录插入分库1,2号记录插入分库2。所以数据库中间件要将其改写为2条sql,分别插入两个不同的分库,此时要保证两个库要不都成功,要不都失败,因此基本上所有的数据库中间件都面临着分布式事务
的问题。
服务化
微服务架构是目前一个比较一个比较火的概念。例如上面我提到的一个案例,某个应用同时操作了9个库,这样的应用业务逻辑必然非常复杂,对于开发人员是极大的挑战,应该拆分成不同的独立服务,以简化业务逻辑。拆分后,独立服务之间通过RPC框架来进行远程调用,实现彼此的通信。下图演示了一个3个服务之间彼此调用的架构:
Service A完成某个功能需要直接操作数据库,同时需要调用Service B和Service C,而Service B又同时操作了2个数据库,Service C也操作了一个库。需要保证这些跨服务的对多个数据库的操作要不都成功,要不都失败,实际上这可能是最典型的分布式事务场景。
小结:上述讨论的分布式事务场景中,无一例外的都直接或者间接的操作了多个数据库。如何保证事务的ACID特性,对于分布式事务实现方案而言,是非常大的挑战。同时,分布式事务实现方案还必须要考虑性能的问题,如果为了严格保证ACID特性,导致性能严重下降,那么对于一些要求快速响应的业务,是无法接受的。