事务概念
一个事务是由一条或者多条对数据库操作的SQL语句所组成的一个不可分割的单元,只有当事务中所有操作都正常执行完了,整个事务才会被提交给数据库;如果有部分事务处理失败,那么事务就要回退到最初状态,因此事务要么全部执行成功,要么全部失败。
所以技术事务几个基本概念,如下:
- 事务是由一组SQL语句执行,要么全部成功要么全部失败,不能出现部分成功,部分失败的结果,保证事务执行的原子操作
- 事务的所有SQL语句全部执行成功,才能提交(commit)事务,把结果写回磁盘。
- 事务执行过程中,由SQL出现错误,那么事务必须要回滚(rollback)到最初的状态
ACID特性
每一个事务都满足以下特性:
原子性(Atomic):
事务是一个不可分割的整体,事务必须具有原子性,及当数据修改时,要么全执行,要么全不执行,即不允许事务部分完成。
一致性(Consistency):
一个事务执行前和执行后,数据库必须保持一致性状态。数据库的一致性状态必须由用户来负责,由并发控制机制实现。就拿网上购物来说,只有让商品出库,又让商品进入顾客的购物车才能构成一个完整的事务。
隔离性(Isolation):
当两个或者多个事务并发执行的时候,为了保证数据库的安全性,将一个事务的内部操作与其他事务的操作隔离起来,不被其他正在执行的事务看到,使得并发执行的各个事务之间不能相互影响。
持久性(Duration):
事务完成以后,DBMS保证对它的数据库中的修改是永久性的,即使数据库因为故障出错,也应该能恢复数据。
事务并发存在的问题
事务处理不经隔离,并发执行事务时通常会发生以下问题:
脏读(Dirty Read):一个事务读取了另一个未提交的数据。例如当事务A和事务B并发执行时,当事务A更新后,事务B查询读取到A尚未提交到的数据,此时事务A回滚,则事务B独到的数据就是无效的脏数据。(事务B读取了事务A尚未提交的数据)
不可重复读(NonRepeatable Read):一个事务的操作导致另一个事务前后两次读取到不同的数据。例如事务A和事务B并发执行,当B查询读取数据后,事务A更新操作更改事务B查询的数据,此时事务B再去读该数据,发现前后两次读的数据不一致。(事务B读取了事务A已提交的数据)
幻读(Phantom Read):一个事务的操作导致另一个事务前后两次查询的结果数据量不同。例如事务A和事务B并发执行时,当事务B查询读取数据后,事务A新增或者删除一条满足事务B查询条件的记录,此时事务B再去查询,发现查询到前一次不存在的记录,或者前一次查询的一些记录不见了。(事务B读取了事务A新增的数据或者读不到事务A删除的数据)
事务隔离级别
MySQL支持四种隔离级别是:
1.READ_UNCOMMITTED。 未提交读。说明在提交前一个事务可以看到另一个事务的变化。这样脏读数据,不可重复读和幻读是被允许的。
2.READ_COMMITTED。 已提交读,说明读取未提交的任务是不被允许的。这个级别仍然允许不可重复读和幻读产生。
3.REPEATABLE_READ。可重复读。说明事务保证能够再次读取相同的数据而不会失败,但幻读仍然会出现。
4.SERIALIZABLE。串行化。是最高的事务级别,他防止脏读数据,不可重复读和幻读。
| 隔离级别 | 脏读 | 不可重复读 | 幻读 |
|---|---|---|---|
| 未提交读 | 可以 | 可以 | 可以 |
| 已提交读 | 不可以 | 可以 | 可以 |
| 可重复读 | 不可以 | 不可以 | 可以 |
| 串行化 | 不可以 | 不可以 | 不可以 |
注:
- 事务隔离级别越高,为避免冲突所花费的性能就越高
- 在“可重复读”级别,实际上可以解决部分幻读问题,但是不能防止update更新产生的幻读问题,要禁止幻读的产生,还是需要设置串行化隔离级别。
MySQL的事务处理命令
打开MySQL的command命令行窗口,测试以下命令:
select @@AUTOCOMMIT; #查看MySQL是否自动提交事务
mysql> select @@autocommit;
+--------------+
| @@autocommit |
+--------------+
| 1 |
+--------------+
1 row in set (0.00 sec)
0表示手动提交事务,1表示自动提交事务,设置事务提交方式为手动提交方式:
set autocommit=0;
BEGIN; 开启一个事务
COMMIT; 提交一个事务
ROLLBACK; 回滚一个事务到初始位置
SAVEPOINT point1; 设置一个名字为point1的保存节点
ROLLBACK TO point1; 事务回滚到保存点point1,而不是回滚到初始状态
SET TX_ISOLATION='REPEATABLE-READ'; 设置事务的隔离级别
SELECT @@TX_IOSLATION; 查询事务的隔离级别
#在 MySQL 5.7 及更早版本中,用于查询事务隔离级别的变量名为 tx_isolation。
#在 MySQL 8.0 及更新版本中,这个变量被更名为 transaction_isolation。
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ | 可以看到MySQL默认的隔离级别默认为可重复读
+-------------------------+
1 row in set (0.00 sec)
场景一:读未提交
我们已经知道MySQL默认隔离级别是可重复读( REPEATABLE-READ ),我们可以修改隔离级别来模拟读未提交的隔离级别。
# 以user表为例
mysql> select * from user;
+----+-----------+-----+-----+
| id | name | age | sex |
+----+-----------+-----+-----+
| 1 | zhang san | 22 | M |
| 2 | wang wei | 22 | M |
| 3 | xi shi | 20 | W |
+----+-----------+-----+-----+
3 rows in set (0.00 sec)
# 将隔离级别设置为READ-UNCOMMITTED
mysql> set transaction_isolation='READ-UNCOMMITTED';
Query OK, 0 rows affected (0.00 sec)
# 查看修改是否成功
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-UNCOMMITTED |
+-------------------------+
1 row in set (0.00 sec)
出现脏读。
场景二:已提交读
将隔离级别调为已提交读:
mysql> set transaction_isolation='READ-COMMITTED';
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| READ-COMMITTED |
+-------------------------+
1 row in set (0.00 sec)
可以发现脏读问题避免了,修改的数据只有提交之后才可以被访问。
场景三:可重复读
将隔离几倍设置为可重复读
mysql> set transaction_isolation='REPEATABLE-READ';
Query OK, 0 rows affected (0.00 sec)
mysql> select @@transaction_isolation;
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ |
+-------------------------+
1 row in set (0.00 sec)
可以发现事务A修改之后提交,事务B再次访问时并没有出现任何变化。
可重复读在一定意义上可以防止幻读的出现。
也就是说,可重复读的隔离级别下,出现update操作,并不能解决幻读操作。
场景四:串行化
串行化如果出现并发执行,那另一个事务的操作将会阻塞。