SQL基础理论篇(十):事务处理
文章目录
- 简介
- 事务的四大特性ACID
- 如何使用事务
- 参考文献
简介
MySQL在5.5版本之前,默认的存储引擎是MyISAM,它是不支持事务的,而5.5版本之后默认的引擎是InnoDB,这个是支持事务的,这也是InnoDB最终取代MyISAM称为主流引擎的重要原因。
事务的真实情况,比我下面写的还要更复杂,所以需要另外再自行查找资料。
事务,即transaction,是数据库的一个逻辑工作单元,是一组不可分割的操作集合,也是数据库进行一次处理的基本单元,要不完全执行,要么都不执行。
事务是数据库区别于文件系统的重要特性之一。有了事务,我们可以保证数据库始终保持一致性;有了事务,我们可以使得数据库恢复到任意有记录的时间点;有了事务,我们可以保证已经提交的修改不会因为中途断电而丢失(不完整)。
事务的四大特性ACID
事务的四大特性:ACID。
- A,即Atomicity,原子性。原子性意味着不可分割,是进行数据处理操作的最小单位;
- C,即Consistency,一致性。数据库在进行事务操作后,会由原来的一致状态,变成另一种一致状态。就是说事务提交后,或事务发生回滚后,数据库的完整性约束不能被破坏。
- I,即Isolation,隔离性。每个事务是彼此独立的,不会受到其他事务的影响。一个事务在提交之前,对其他事务都是不可见的。
- D,即Durability,持久性。事务提交后,对数据库的修改是持久的。即使系统出现故障、比如系统崩溃,数据的修改依旧是有效的。因为当事务完成,数据库的日志就会被更新,这时可以通过日志来使系统恢复到最后一次成功的更新状态。
这四个特性中,原子性是基础,隔离性是手段,一致性是约束条件,持久性是目的。
比较难理解的是一致性和持久性。
一致性,意味着任何写入数据库的数据都需要满足我们事先定义的约束规则。
比如说,事先我们设置name字段为唯一性约束(如主键约束),如果事务提交后或者回滚的时候,若name非唯一,就算打破了事务的一致性要求。
数据库常见的约束有:
- 主键约束:主键(一个字段或多个字段)唯一标识一条记录,不能重复,不能为空;
- 外键约束:确保了表与表之间引用的完整性,一个表的外键对应着另一张表的主键(也可以不是主键,但字段必须有唯一性约束);
- 唯一性约束:字段在表中的值是唯一的。需要注意的是,唯一性约束和普通索引是有区别的。创建唯一性约束的时候会自动创建一个约束和字段的普通索引,目的是为了保证字段的正确性,而普通索引只是为了提升数据检索的速度。
- 非空约束:对字段约束not null;
- default约束:表明字段的默认值;
- check约束:检查特定字段取值范围的有效性,check约束的检查结果不能是false。比如我们将身高字段进行check约束,比如小于180,大于160。
事务执行前,数据库符合这些约束,事务执行后,数据库同样需要符合这些约束,这就是事务的一致性。如果事务中的某个操作执行失败了,系统就会自动回滚,撤销当前执行的事务,返回事务操作之前的一致性状态。
持久性,持久性是通过事务日志来保证的。事务日志包括回滚日志和重做日志。当我们通过事务对数据进行修改的时候,首先会将数据库需要做的修改信息记录到重做日志中,然后再对数据库中相应的行做改变。这样的好处是,如果执行中途数据库发生了故障而崩溃了,数据库重启后也能找到重做日志,重新执行没有执行完的修改,从而使事务具有了持久性。(提交之后100%会被执行完毕,即使数据库崩了,重启之后照样给你写进去。
)
如何使用事务
首先你需要有一个支持事务的数据库引擎。
以MySQL举例,InnoDB引擎是支持事务的,但是MyISAM引擎就不支持事务。我们可以通过show engines
来查看当前的MySQL支持的存储引擎都有哪些,以及这些引擎是否支持事务。
常见的事务控制语句有:
- start transaction或者begin,作用是显式的开启一个事务;
- commit:显式的提交一个事务。当提交事务后,对数据库的修改是永久的。(持久性)
- rollback或者rollback to [savepoint],回滚事务,撤销当前正在进行的所有没有提交的修改,或者将事务回滚到某个保存点;
- savepoint:在事务中创建保存点,方便后续针对某个保存点进行回滚。一个事务中可以存在多个保存点。
- release savepoint:删除某个保存点;
- set transaction:设置事务的隔离级别。
使用事务有两种方式,分别为隐式事务和显式事务。
隐式事务实际上就是自动提交。Oracle中默认是不支持自动提交的,需要手写commit来显式提交。MySQL中默认自动提交(通过set autocommit =0或1来关闭开启自动提交)。
让我们看几个例子:
-- autocommit=1,即自动提交开启
CREATE TABLE test(name varchar(255), PRIMARY KEY (name)) ENGINE=InnoDB;
BEGIN;
INSERT INTO test SELECT '关羽';
COMMIT;
BEGIN;
INSERT INTO test SELECT '张飞';
INSERT INTO test SELECT '张飞';
ROLLBACK;
SELECT * FROM test;
上面其实执行了两个事务。第一个事务insert了关羽,然后正常commit了。第二个事务在第一个insert的时候正常,但是第二个insert插入张飞,打破了事务的主键约束,所以回滚整个事务。因此最终的表里只有一行“关羽”。
然后第二个例子:
-- autocommit=1,即自动提交开启
CREATE TABLE test(name varchar(255), PRIMARY KEY (name)) ENGINE=InnoDB;
BEGIN;
INSERT INTO test SELECT '关羽';
COMMIT;
INSERT INTO test SELECT '张飞';
INSERT INTO test SELECT '张飞';
ROLLBACK;
SELECT * FROM test;
由于自动commit的情况,上面实际上执行了三个事务。第一个是显式begin的那个事务,成功插入了一行关羽。第二个事务是第二个insert,因为自动提交,所以这个“张飞”也成功插入了。第三个事务是第三个insert,由于主键约束,insert失败,因此回滚了第三个事务。
事务在commit之后,就没法再回滚了!!! 那如果开了自动commit的话,除非事务出错,才会引起自动回滚,显式rollback是没用的?是这样理解吗?
第三个例子:
CREATE TABLE test(name varchar(255), PRIMARY KEY (name)) ENGINE=InnoDB;
SET @@completion_type = 1;
BEGIN;
INSERT INTO test SELECT '关羽';
COMMIT;
INSERT INTO test SELECT '张飞';
INSERT INTO test SELECT '张飞';
ROLLBACK;
SELECT * FROM test;
在MySQL中,如果是连续BEGIN,开启了第一个事务,还没有进行COMMIT提交,而直接进行第二个事务的BEGIN,数据库会隐式的帮助COMMIT第一个事务,然后进入到第二个事务
第三个例子跟第二个例子只有一点不同,就是多了一个@@completion_type
这个变量。这个参数的取值及作用有3种可能:
- completion_type = 1,当提交事务后,相当于执行了
commit;begin
,提交事务后会再开启一个相同隔离级别的链式事务。 - completion_type = 2,commit操作相当于
commit and release
,提交后会自动与服务器断开连接; - completion_type=0,默认情况,commit就是普通的提交事务,下一个事务还需要自己开。
所以上面的例子,实际上只插入了关羽一行,因为第一个commit = commit and begin,后面两个insert被自动包括进一个事务了。
当设置autocommit=0的时候,取消自动提交,那么不论是否采用begin的方式显示开启事务,都需要手动commit来提交,使得事务生效,使用rollback来对事务回滚。
不过,当设置autocommit=1的时候,允许自动提交,那当显式使用start transaction或者begin的方式开启事务时,只有在显式commit的时候才会生效,显式rollback的时候才会回滚
在MySQL中,当取消自动提交时,连续begin,第二个begin会自动commit第一个事务,保证每次只处理一个事务。
2021-7-4 17:15:01 事务这里还是很重要的,推荐后续大块时间走一遍
参考文献
- 14丨什么是事务处理,如何使用COMMIT和ROLLBACK进行操作?