1、ACID
示例如下,银行转账为例
START TRANSACTION;
SELECT balance FROM checking WHERE customer_id = 10233276;
UPDATE checking SET balance = balance - 200.00 WHERE customer_id = 10233276;
UPDATE savings SET balance = balance + 200.00 WHERE customer_id = 10233276;
COMMIT;
SQL标准定义了4类隔离级别,每一种级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。
-- 创建表
SET @@session.transaction_isolation = 'READ-UNCOMMITTED';
create database test;
use test;
create table test(id int primary key);
insert into test(id) values(1);
-- 开启一个终端,开启事务,更新ID为1的记录更新为2
begin;
update test set id = 2 where id = 1;
select * from test; -- 此时看到一条ID为2的记录
-- 开启另一个终端,开启事务,查看表中的数据
use test;
begin;
select * from test; -- 此时看到一条 ID 为 2 的记录
最后一步读取到了 mysql 终端 1 中未提交的事务(没有 commit 提交动作),即产生了 脏读 ,大部分业务场景都不允许脏读出现,但是此隔离级别下数据库的并发是最好的。
READ-UNCOMMITTED 中文叫未提交读,即一个事务读到了另一个未提交事务修改过的数据,整个过程如下图:
如上图,SessionA和SessionB分别开启一个事务,SessionB中的事务先将id为1的记录的name列更新为’lisi',然后Session 中的事务再去查询这条id为1的记录,那么在未提交读的隔离级别下,查询结果由’zhangsan’变成了’lisi',也就是说某个事务读到了另一个未提交事务修改过的记录。但是如果SessionB中的事务稍后进行了回滚,那么SessionA中的事务相当于读到了一个不存在的数据,这种现象也称为脏读。 可见READ-UNCOMMITTED是非常不安全。
-- 创建表
SET @@session.transaction_isolation = 'READ-COMMITTED';
create database test;
use test;
create table test(id int primary key);
insert into test(id) values(1);
-- 开启一个终端,开启事务,更新ID为1的记录更新为2,并确认记录数变更过来
begin;
update test set id = 2 where id = 1;
select * from test; -- 此时看到一条记录为 2
-- 开启另一个终端,开启事务,查看表中的数据
use test;
begin;
select * from test; -- 此时看一条 ID 为 1 的记录
-- 登录到第一个终端,提交事务
commit;
-- 切换到第二个终端
select * from test; -- 此时看到一条 ID 为 2 的记录
mysql 终端 2 在开启了一个事务之后,在第一次读取 test 表(此时 mysql 终端 1 的事务还未提交)时 ID 为 1 ,在第二次读取 test 表(此时 mysql 终端 1 的事务已经提交)时 ID 已经变为 2 ,说明在此隔离级别下已经读取到已提交的事务。
READ COMMITTED 中文叫已提交读,或者叫不可重复读。即一个事务能读到另一个已经提交事务修改后的数据,如果其他事务均对该数据进行修改并提交,该事务也能查询到最新值.
在第4步 SessionB 修改后,如果未提交,SessionA是读不到,但SessionB一旦提交后,SessionA即可读到SessionB修改的内容。
从某种程度上已提交读是违反事务的隔离性的
REPEATABLE READ 中文叫可重复读,即事务能读到另一个已经提交的事务修改过的数据,但是第一次读过某条记录后,即使后面其他事务修改了该记录的值并且提交,该事务之后再读该条记录时,读到的仍是第一次读到的值,而不是每次都读到不同的数据.
InnoDB默认是这种隔离级别,SessionB无论怎么修改id=1的值,SessionA读到依然是自己开启事务第一次读到的内容。
SERIALIZABLE 叫串行化, 上面三种隔离级别可以进行 读-读 或者 读-写、写-读三种并发操作,而SERIALIZABLE不允许读-写,写-读的并发操作。
SessionB 对 id=1 进行修改的时候,SessionA 读取id=1则需要等待 SessionB 提交事务。可以理解SessionB在更新的时候加了X锁。
分布式事务指允许多个独立的事务资源参与到一个全局的事务中。全局事务要求在其中的所有参与的事务要么都提交,要么都回滚。
InnoDB 是支持分布式事务,由一个或多个资源管理器(Resource Managers),一个事务管理器(Transaction Manager),以及一个应用程序(Application Program)组成。
如下图:
应用程序向一个或多个数据库执行事务操作,事务管理器进行管理事务,通过二段式提交,第一阶段所有参与的全局事务的节点都开始准备,告诉事务管理器都准备好了,可以提交了。第二阶段,事务管理器告诉每一个资源管理器是执行Commit 还是 Rollback。如果任何一个节点显示不能提交,则所有的节点被告知需要回滚
InnoDB的分布式是数据库实现的,看看数据库外如何分布式事务,比较常见的是TCC分布式事务。
上图描述了TCC分布式事务的流程,假设电商业务中,支付后需要修改库存,积分,物流仓储的数据,如果一个失败则全部回滚。
TCC分布式事务,有三个阶段,Try,Confirm, Cancel。也就是说每个参与事务的服务都需要实现这三个接口,库存、积分、仓储都需要实现这三个接口。
第一阶段,Try,业务应用调取各个服务的Try接口,告诉他们给我预留一个商品,有人要购买,可以理解为冻结,每一步都不执行成功,只是标记更新状态。
第二阶段,Confirm,确认阶段,即事务协调器调取每个服务Confirm执行事务操作,如果某一个服务的Confirm失败,则有第三个阶段。如果成功则结束事务。
第三个阶段,Cancel,如果在第二个阶段有一个事务提交失败,则事务协调器调取所有业务的Cancel接口,回滚事务,将第一阶段冻结的商品恢复。