目录
1.事务的概念
所谓的事务就是针对数据库的一组操作,它可以由一条或多条SQL语句组成,同一个事务的操作具备同步的特点,即事务中的语句要么都执行,要么都不执行。
举例:
转账可以分为两部分来完成:转入和转出。只有这两个部分都完成才认为转账成功。
A账户给B账户转账100元:
-
A账户减少100元,
-
B账户增加100元。
在数据库中,这个过程是使用两条SQL语句来完成的。
# A 账户减少100元
UPDATE account SET money = money - 100 WHERE name = 'A';
# B 账户增加100元
UPDATE account SET money = money + 100 WHERE name = 'B';
如果其中任意一条语句出现异常没有执行,则会导致两个账户的金额不同步,造成错误。
为了防止这种情况发生,就需要使用MySQL中的事务(Transaction)。
2.事务的基本操作
在默认情况下,用户执行的每一条SQL语句都会被当成单独的事务自动提交。
如果要将一组SQL语句作为一个事务,则需要先执行以下语句显式地开启一个事务。
START TRANSACTION;
此时,每一条SQL语句不再自动提交,用户需要手动提交,操作才会生效。
COMMIT;
如果不想提交当前事务,可以取消事务(即回滚)。
ROLLBACK;
准备数据
CREATE DATABASE mahaiwuji;
USE mahaiwuji;
CREATE TABLE student(
id INT(4) PRIMARY KEY,
sname VARCHAR(36),
money INT(4)
) ENGINE = INNODB DEFAULT CHARSET = utf8;
INSERT INTO student VALUES(1,'A',1000);
INSERT INTO student VALUES(2,'B',1000);
提交实例演示
# 开启事务
START TRANSACTION;
# A减少100元
UPDATE student SET money = money - 100 WHERE sname = 'A';
# B增加100元
UPDATE student SET money = money + 100 WHERE sname = 'B';
# 提交事务
COMMIT;
回滚实例演示
# 开启事务
START TRANSACTION;
# B扣除100元
UPDATE student SET money = money - 100 WHERE sname = 'B';
# 回滚事务
ROLLBACK;
事务的执行要么成功,要么就返回到事务开始前的状态,这就保证了同一事务操作的同步性和数据的完整性。
MySQL中的事务必须满足A、C、I、D这4个基本特性:
-
Atomicity:原子性,一个事务必须被视为一个不可分割的最小工作单元,只有事务中所有的数据库操作都执行成功,才算整个事务执行成功。事务中如果有任何一个SQL语句执行失败,已经执行成功的SQL语句也必须撤销,数据库的状态退回到执行事务前的状态。
-
Consistency:一致性,是指在事务处理时,无论执行成功还是失败,都要保证数据库系统处于一致的状态,保证数据库系统从不返回到一个未处理的事务中。MySQL中的一致性主要由日志机制实现的,通过日志记录数据库的所有变化,为事务恢复提供了跟踪记录。
-
Isolation:隔离性,是指当一个事务在执行时,不会受到其他事务的影响。保证了未完成事务的所有操作与数据库系统的隔离,直到事务完成为止,才能看到事务的执行结果。隔离性相关的技术有并发控制、可串行化、锁等。当多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
-
Durability:持久性,是指事务一旦提交,其对数据库的修改就是永久性的。事务的持久性不能做到百分百的持久,只能从事务本身的角度来保证永久性,而一些外部原因导致数据库发生故障,如硬盘损坏,那么所有提交的数据可能都会丢失。
注意:
-
MySQL中的事务不允许嵌套,若在执行START TRANSACTION语句前上一个事务还未提交,会隐式地执行提交操作。
-
事务处理主要是针对数据表中数据的处理,不包括创建或删除数据库、数据表,修改表结构等操作,而且执行这类操作时会隐式地提交事务。例如:在使用START TRANSACTION开启事务后执行ALTER TABLE修改表结构,会隐式提交事务。
-
当执行COMMIT或ROLLBACK后,当前事务就会自动结束。ROLLBACK只能针对未提交的事务回滚,已提交的事务无法回滚。
-
MySQL 5.7默认的存储引擎为InnoDB,该存储引擎支持事务,而另一个常见的存储引擎MyISAM不支持事务。对于MyISAM存储引擎的数据表,无论事务是否提交,对数据的操作都会立即生效,不能回滚。
-
在MySQL中,还可以使用START TRANSACTION的别名BEGIN或BEGIN WORK来显式地开启一个事务。但由于BEGIN与MySQL编程中的BEGIN...END冲突,因此不推荐使用BEGIN。
事务的自动提交:
MySQL默认是自动提交模式。
如果没有显式开启事务(START TRANSACTION),每一条SQL语句都会自动提交(COMMIT)。
查看事务是否自动提交:
SELECT @@autocommit;
关闭自动提交:
SET AUTOCOMMIT = 0;
关闭后:用户需要手动执行提交(COMMIT)操作。若直接终止MySQL会话,MySQL会自动进行回滚。
开启自动提交:
SET AUTOCOMMIT = 1;
3.事务的保存点
在回滚事务时,若希望只撤销一部分,可以用保存点来实现:
SAVEPOINT 保存点名;
在设置保存点后,可以将事务回滚到指定保存点:
ROLLBACK TO SAVEPOINT 保存点名;
若不再需要一个保存点,使用如下语句删除:
RELEASE SAVEPOINT 保存点名;
实例演示:
# 查询A的金额
SELECT sname, money FROM student WHERE sname = 'A';
# 开启事务
START TRANSACTION;
# A扣除100元
UPDATE student SET money = money - 100 WHERE sname = 'A';
# 查询A的金额
SELECT sname, money FROM student WHERE sname = 'A';
# 创建保存点s1
SAVEPOINT s1;
# A再扣除50元
UPDATE student SET money = money - 50 WHERE sname = 'A';
# 查询A的金额
SELECT sname, money FROM student WHERE sname = 'A';
# 回滚到保存点s1
ROLLBACK TO SAVEPOINT s1;
# 查询A的金额
SELECT sname, money FROM student WHERE sname = 'A';
# 回滚事务
ROLLBACK;
# 查询A的金额
SELECT sname, money FROM student WHERE sname = 'A';
一个事务中可以创建多个保存点,在提交事务后,事务中的保存点就会被删除。
在回滚到某个保存点后,在该保存点之后创建过的保存点也会消失。
4.事务隔离级别
事务隔离级的意义:
数据库是一个多用户的共享资源,MySQL允许多线程并发访问,用户可以通过不同的线程执行不同的事务。为了保证这些事务之间不受影响,对事务设置隔离级是十分必要的。
查看隔离级别:
# 查看全局隔离级
SELECT @@global.transaction_isolation;
# 查看当前会话中的隔离级
SELECT @@session.transaction_isolation;
# 查看下一个事务的隔离级
SELECT @@transaction_isolation;
全局的隔离级:影响所有连接MySQL用户。
当前会话隔离级:只影响当前正在登录MySQL服务器的用户。(不会影响其他用户)
下一个事务的隔离级:仅对当前用户的下一个事务操作有影响。
默认情况下3种方式的结果都是REPEATABLE READ
MySQL中事务的隔离级别:
-
REPEATABLE READ:可重复读
-
READ UNCOMMITTED:读取未提交
-
READ COMMITTED:读取提交
-
SERIALIZABLE:可串行化
设置事务的隔离级别:
SET [SESSION | GLOBAL] TRANSACTION ISOLATION LEVEL 参数值
-
SESSION:当前会话
-
GLOBAL:全局
-
直接省略:下一个事务的隔离级
-
TRANSACTION:事务
-
ISOLATION:隔离
-
LEVEL:级别
修改为READ UNCOMMITTED
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
修改为默认REPEATABLE READ
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
事务的默认访问模式为 READ WRITE(读/写模式),事务可以执行:读(查询)或 写(更改、插入、删除等)操作。
若开发需要,可以将事务的访问模式设置为READ ONLY(只读模式),禁止对表进行更改。
# 设置只读事务
SET [SESSION | GLOBAL] TRANSACTION READ ONLY
# 恢复成读写事务
SET [SESSION | GLOBAL] TRANSACTION READ WRITE
5.MySQL的4种隔离级别
1.READ UNCOMMITTED(读取未提交):
事务中最低的级别,可以读取到其他事务中未提交的数据。
也称为脏读(Dirty Read):一个事务读取了另外一个事务未提交的数据。
情景模拟:
A给B转账100元购买商品。
A开启事务后转账,但不提交事务,通知B来查询,
如果B的隔离级别较低,就会读取到A的事务中未提交的数据,
发现A确实给自己转了100元,就给A发货。
等B发货成功后,A将事务回滚,B就会受到损失。
2.READ COMMITTED(读取提交):
大多数DBMS(如SQL Server、Oracle)的默认隔离级,但不包括MySQL。只能读取其他事务已经提交的数据,避免了脏读问题。
但是会出现不可重复读(NON-REPEATABLE READ)问题。
不可重复读:一个事务中多次查询的结果不一致,原因是查询的过程中数据发生了改变。
情景模拟:
在网站后台统计所有用户的总金额。
第1次查询A有900元,
第2次查询A有800元。
问题:在同一个事务中,同样的两次查询结果不同。
原因:第2次查询前A取出了100元。
3.REPEATABLE READ(可重复读):
MySQL的默认事务隔离级,它解决了脏读和不可重复读的问题,确保了同一事务的多个实例在并发读取数据时,会看到同样的结果。
该级别理论上会出现幻读(PHANTOM READ)问题。
幻读又被称为虚读,是指在一个事务内两次查询中数据条数不一致,
如:其他事务做了插入记录的操作,导致记录数有所增加。
不过,MySQL的InnoDB存储引擎已经解决了幻读问题。
情景模拟:
在网站后台统计所有用户的总金额时,
当前只有两个用户,总金额为2000元,
此时新增一个用户,并且存入1000元。
再次统计会发现总金额变为3000元,造成了幻读的情况。
4.SERIALIZABLE(可串行化):
隔离级的最高级别,它在每个读的数据行上加锁,使之不会发生冲突,
解决了脏读、不可重复读和幻读的问题。
由于加锁可能导致超时(Timeout)和锁竞争(Lock Contention)现象,
性能是4种隔离级中最低的。
除非为了数据的稳定性,需要强制减少并发的情况时,才会选择此种隔离级。