MySQL数据库精研之旅第十六期:深度拆解事务核心(上)
专栏:MySQL数据库成长记
个人主页:手握风云
目录
一、什么是事务
二、事务的ACID特性
三、为什么要使用事务
四、如何使用事务
4.1. 查看支持事务的存储引擎
4.2. 语法
4.3. 手动/自动提交事务
4.4. 开启一个事务,执行修改后回滚
4.5. 开启一个事务,执行修改后提交
4.6. 保存点
一、什么是事务
事务把一组SQL语句打包成为一个整体,在这组SQL的执行过程中,要么全部成功,要么全部失败。这组SQL语句可以是一条也可以是多条。我们拿转账举例:
create table bank_account (id bigint primary key auto_increment,`name` varchar(25) not null, -- 姓名balance decimal(10,2) not null -- 余额
);insert into bank_account (`name`, balance) values ('张三',1000);
insert into bank_account (`name`, balance) values ('李四',1000);-- 张三的余额减少100
update bank_account set balance = balance - 100 where `name` = '张三';
-- 李四的余额增加100
update bank_account set balance = balance + 100 where `name` = '李四';
张三给李四转100元,需要保证:不能出现张三的余额减少而李四的余额没有增加的情况,要么全部成功,要么全部失败;保证转账前后两人的总余额不变,转账前后数额要一致;转账后的余额结果应当保存到存储介质中,以便以后读取,对数据的修改应当落盘(保存到磁盘),持久化;在转账的处理过程中张三和李四的余额不能因其他的转账事件而受到干扰,把其他操作隔离开。
以上这四点在事务的整个执行过程中必须要得到保证,这也就是事务的ACID特性。
对于隔离性,因为不同的客户端可以同时对数据库进行操作,执行的SQL语句也有可能相同。我们对一个账户余额减少100的操作,先从数据库中把1000读出来,把余额在原来的基础上减100,最后把修改过后的值写回数据库。如果有两个客户端并行执行此操作,减少了两次,余额应该是800,由于客户端1和客户端2同时(并行)进行了对数据的修改,导致得到了一个错误的结果。
二、事务的ACID特性
事务的ACID特性指的是 Atomicity(原子性),Consistency(一致性),Isolation((隔离性)和 Durability(持久性)。
- Atomicity (原子性):⼀个事务中的所有操作,要么全部成功,要么全部失败,不会出现只执 ⾏了⼀半的情况,如果事务在执行过程中发⽣错误,会回滚( Rollback )到事务开始前的状 态,就像这个事务从来没有执⾏过一样。
- Consistency (一致性):在事务开始之前和事务结束以后,数据库的完整性不会被破坏。这表 示写⼊的数据必须完全符合所有的预设规则,包括数据的精度、关联性以及关于事务执行过程中服务器崩溃后如何恢复。
- Isolation (隔离性):数据库允许多个并发事务同时对数据进行读写和修改,隔离性可以防⽌多个事务并发执行时由于交叉执行而导致数据的不⼀致。事务可以指定不同的隔离级别,以权衡在不同的应用场景下数据库性能和安全。
- Durability (持久性):事务处理结束后,对数据的修改将永久的写入存储介质,即便系统故障 也不会丢失。如果事务一旦提交,就需要永久的保存到存储介质当中,不论数据库软件或操作系统是否崩溃,都不会对数据的安闐啡悄全造成影响(数据丢失)。
三、为什么要使用事务
事务具备的ACID特性,是我们使用事务的原因,在我们日常的业务场景中有大量的需求要用事务 来保证。支持事务的数据库能够简化我们的编程模型,不需要我们去考虑各种各样的潜在错误和并发问题,在使用事务过程中,要么提交,要么回滚,不用去考虑⽹络异常,服务器宕机等其他因素,因此我们经常接触的事务本质上是数据库对 ACID 模型的⼀个实现,是为应⽤层服务的。
主流的数据库都支持事务,如果数据库无法支持ACID特性,那么就需要程序员耗费大量精力去维护。
四、如何使用事务
4.1. 查看支持事务的存储引擎
show engines;
要使用事务那么数据库就要支持事务,从上图可以看出,MySQL支持事务的存储引擎是InnoDB。
4.2. 语法
-- 开启事务
start transaction;
-- 或
begin;-- 事务中要执行的SQL,一条或一组-- 提交当前事务,,并对更改持久化保存
commit;-- 回滚当前事务,取消其更改
rollback;-- 无论执行了commit或者rollback,事务都会结束
4.3. 手动/自动提交事务
我们之前写的单条update、insert、delete语句也都在事务中,这是因为在默认情况下,,MySQL是自动提交事务的。我们执行的每个修改操作,都会自动开启一个事务并在语句执行完成之后自动提交,发生异常时自动回滚。
-- 查看当前语句是否自动提交
show variables like 'autocommit';
如果是OFF,就需要手动commit或者rollback。
我们也可以通过下列语句设置事务自动或者手动。
-- 设置事务自动提交
set autocommit=1;
set autocommit=ON;-- 设置事务手动提交
set autocommit=0;
set autocommit=OFF;
手动提交模式下,不用显示开启事务,执行修改操作后,提交或回滚事务时直接使用 commit或 rollback。
4.4. 开启一个事务,执行修改后回滚
start transaction;
只要手动开启一个事务,必须以commit或rollback结束,和是否自动提交没有关系。
-- 执行转账逻辑
update bank_account set balance = balance - 100 where `name` = '张三';
select * from bank_account;
update bank_account set balance = balance + 100 where `name` = '李四';
select * from bank_account;
-- 回滚之后,数据回退到最初状态
ROLLBACK;
select * from bank_account;
4.5. 开启一个事务,执行修改后提交
-- 开启一个新事务
begin;
update bank_account set balance = balance - 100 where `name` = '张三';
update bank_account set balance = balance + 100 where `name` = '李四';select * from bank_account;
-- 提交之后,才进行永久保存
commit;
事务提交之后,无论哪个客户端查询之后,都是落盘的结果。已提交的事务不能回滚。
4.6. 保存点
如同游戏当中存档点,在事务执⾏的过程中设置保存点,回滚时指定保存点可以把数据恢复到保存点的状态。我们不光可以回滚到保存点,甚至可以回到事务的开启。
start transaction;
update bank_account set balance = balance - 100 where `name` = '张三';
update bank_account set balance = balance + 100 where `name` = '李四';
select * from bank_account;-- 设置第一个保存点
savepoint sp1;
update bank_account set balance = balance - 100 where `name` = '张三';
update bank_account set balance = balance + 100 where `name` = '李四';
select * from bank_account;-- 设置第二个保存点
savepoint sp2;-- 新增一条数据
insert into bank_account values(3, '王五',5000);
rollback to sp2;
select * from bank_account;
在实际工作中,回滚使用的频率比保存点要高很多。