数据库事务概述
事务时数据库区别于文件系统的重要特性之一,当我们有了事务就会让数据库始终保持一致性
,同时我们还能通过事务的机制恢复到某个时间点
,这样我们就可以保证已提交到数据库的修改不会因为系统崩溃而丢失。mysql 数据库目前只有 InnoDB 存储引擎是支持事务的。
事务:一组逻辑操作单元,使数据库从一种状态变换到另一种状态。
事务处理的原则:保证所有操作都作为一个工作单元来执行,即使出现了故障,都不能改变这种执行方式。当在一个事务中执行多个操作时,要么所有的操作都被提交(commit),那么这些修改就永久保存下来;要么数据库管理系统将放弃所有修改,整个事务回滚(rollback)到最初状态。
事务的 ACID 特性
- 原子性(atomicity):原子性是指事务是一个不可分割的工作单位,要么全部提交,要么全部失败回滚。
- 一致性(consistency):一致性是指事务执行前后,从一个合法性状态(满足预定的约束的状态)变换到另一个合法性状态。这种状态是语义上的而不是语法上的,跟具体的业务有关。
- 隔离性(isolation):隔离性是指事务的执行不能被其他事务干扰,即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
- 持久性(durability):持久性是指一个事务一旦被提交,他对数据库的改变就是永久性的,接下来的其他操作和数据库故障不应对其有任何影响。持久性是通过
事务日志
来保证的,包括了重做日志
和回滚日志
。
ACID 是事务的四大特性,在这四个特性中,原子性是基础,隔离性是手段,一致性是约束条件,而持久性是我们的目的。
事务的状态
我们知道事务是一个抽象的概念,它其实对应着一个或多个数据库操作,mysql 根据这些操作所执行的不同阶段把事务大致划分为几个状态:
- 活动的(active):事务对应的数据库操作正在执行过程中,我们就说该事物处在活动的状态。
- 部分提交的(partially committed):当事务中的最后一个操作执行完成,但由于操作都在内存中执行,所造成的影响并没有刷新到磁盘时,我们就说该事务处在部分提交的状态。
- 失败的(failed):当事务处在活动的或部分提交的状态时,可能遇到了某些错误(数据库自身错误、操作系统错误或直接断电等)而无法继续执行,或者人为的停止当前事务的执行,我们就说该事务处在失败的状态。
- 中止的(aborted):如果事务执行了一部分操作后变为了失败的状态,那么就需要把已经修改的事务中的操作还原到事务执行前的状态。我们把这个撤销的过程称之为回滚。当回滚操作完毕时,也就是数据库恢复到了执行事务之前的状态,我们就说该事务处在了中止的状态。
- 提交的(committed):当一个处在部分提交的状态的事务将修改过的数据都同步到磁盘上之后,我们就可以说该事务处在了提交的状态。
开启事务
显式事务
使用关键字start transction
或begin
开启事务,使用commit
提交事务,使用rollback
回滚事务。例如:
-- 开启事务
begin;
-- 插入数据 1
insert into demo_table values(1);
-- 提交事务,事务结束
commit;
-- 开启另一个事务
start transtion;
-- 插入数据 2
insert into demo_table values(2);
-- 回滚事务(回滚到上次提交事务的状态),此时数据 2 并不会写入数据库,事务结束
rollback;
-- 开启事务
begin;
-- 插入数据 3
insert into demo_table values(3);
-- 设置保存点 s1
savepoint s1;
-- 插入数据 4
insert into demo_table values(4);
-- 回滚到保存点 s1,此时数据 3 会被保存(内存),而数据 4 并不会保存(内存),事务并没有结束
rollback to s1;
-- 提交事务,此时数据 3 才真正写入磁盘,事务结束
commit;
start transction 可以跟 read only / read write / with consistent snapshot
- read only:表示当前事务是一个只读事务,也就是属于该事务的数据库操作只能读取数据,而不能修改数据。(只读事务只是不允许修改那些其他事务也能访问到的表中的数据,对于临时[使用 create tmeporary table 创建的表]表来说,由于它们只能在当前会话中可见,所以只读事务也是可以对临时表进行增、删、改操作的)
- read write:表示当前事务是一个读写事务,也就是属于该事务的数据库操作既可以读取数据,也可以修改数据。
- with consistent snapshop:启动一致性读。
隐式事务
mysql 中有一个系统变量autocommit
(默认值 ON),默认请情况下,如果我们不显式使用 start transaction 或 begin 语句开启一个事务,那么每一条 DML(数据库操作语言) 语句都算是一个独立的事务,这种特性称之为事务的自动提交
。如果我们想关闭这种自动提交的功能可以使用显式事务或把 autocommit
的值改为 OFF。这样的话,我们写入多条语句就算是属于同一个事务了,直到我们显式写出 commit 语句提交事务或写出 rollback 回滚事务才可以真正修改数据。
事务的隔离级别
mysql 是客户端/服务器架构的软件,对于一个服务器来说,可以有若干个客户端与之连接,每个客户端与服务器连接之后,就可以称之为一个会话(session)。每个客户端都可以在自己的会话中向服务器发出请求语句,一个请求语句可能是某个事务的一部分,可就是对于服务器来说可能同时处理多个事务。事务有隔离性的特性,理论上在某个事务对某个数据进行访问时,其他事物应该进行排队,当事务提交之后,其他事务才可以继续访问这个数据。但是这样对性能的影响太大,我们既想保持事务的隔离性,又想让服务器在处理访问同一数据的多个事务时性能尽量高些,因此就需要采用适当的隔离级别。
数据并发问题(严重级别从高到低)
- 脏写(Dirty Write):对于两个事务 SessionA、SessionB,如果事务 SessionA 修改了另一个未提交事务 SessionB 修改过的数据,那就意味着发生了脏写。
- 脏读(Dirty Read):对于两个事务 SessionA、SessionB,SessionA 读取了已经被 SessionB 更新但还没与被提交的数据。若之后 SessionB 回滚,SessionA 读取的内容就是临时且无效的,称为脏读。
- 不可重复读(Non-Repeatable Read):对于两个事务 SessionA、SessionB,SessionA 读取了一个字段,然后 SessionB 更新了该字段。之后 SessionA 再次读取同一个字段,值读到的值就不同了。就意味着发生了不可重复读。
- 幻读(Phantom):对于两个事务 SessionA、SessionB,SessionA 从一个表中读取了一个字段,然后 SessionB 在该表中插入了一些新的行。之后如果 SessionA 再次读取同一个表,就会多出几行,我们把多出来的行称之为幻影记录,该现象称为幻读。
SQL 的隔离级别(从低到高)
- READ UNCOMMITTED:读未提交,在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。仅能避免脏写,不能避免脏读、不可重复读、幻读。
- READ COMMITTED:读已提交,它满足了隔离级别的简单定义(一个事务只能看见已提交事务所做的改变)。可以避免脏写、脏读,但不能避免不可重复读、幻读。
- REPEATABLE READ:可重复读,事务 A 在读到一条数据之后,此时事务 B 对该数据进行了修改并提交,那么事务 A 再读改数据,读到的还是原来的内容。可以避免脏写、脏读、不可重复读,但幻读问题仍然存在。mysql 默认的隔离级别
- SERIALIZABLE:可串行化,确保事务可以从一个表中读取相同的行。在这个事务持续期间,禁止其他事务对该表执行插入、更新和删除操作。该级别所有并发问题都可以避免,但性能十分低下。
mysql 查看隔离级别
-- mysql 5.7.20 之前查看隔离级别
show variables like 'tx_isolation';
-- mysql 5.7.20 之后查看隔离级别
show variables like 'transaction_isolation';
-- 查看隔离级别(各版本都可以使用)
select @@transaction_isolation;
mysql 设置隔离级别
/*
隔离级别:READ UNCOMMITTED、READ COMMITTED、REPEATABLE READ、SERIALIZABLE
*/
set [global | session] transaction isolation level 隔离级别;
或者
/*
隔离级别:READ-UNCOMMITTED、READ-COMMITTED、REPEATABLE-READ、SERIALIZABLE
*/
set [global | session] transaction_isolation='隔离级别';