事务概念
一个事务是由一条或者多条对数据库操作的SQL语句所组成的一个不可分割的单元,只有当事务中的所有操作都正常执行完了,整个事务才会被提交给数据库;如果有部分事务处理失败,那么事务就要回退到最初状态,因此,事务要么全部执行成功,要么全部失败。
事务的几个概念如下:
- 事务是一组SQL语句的执行,要么全部成功,要么全部失败,不能出现部分成功,部分失败的结果,保证事务执行的原子操作。
- 事务的所有SQL语句全部执行成功,才能提交(commit)事务,把结果写回磁盘上。
- 事务执行过程中,有的SQL出现错误,那么事务必须要回滚(rollback)到最初的状态。
MyISAM:不支持事务
InnoDB:支持事务、支持行锁
比如银行转账就要用到事务。总不能让钱越转越少吧hhh。
ACID特征
原子性(Atomic):不允许事务部分完成。
一致性(Consistency):一个事务执行之前和执行之后,数据库数据必须保持一致性的状态。数据库的一致性必须由用户来负责,由并发控制机实现。维持总状态不变。
隔离性(Isolation):当两个或多个事务并发执行时,为了保证数据的安全性,将一个事务内部的操作与其它事务的操作隔离起来,不被其它正在执行的事务所看到,使得并发执行的各个事务之间不能互相影响。
持久性(Durability):事务完成后,DBMS保证它对数据库中的数据的修改是永久的,即使数据库因为故障出错,也应该能够恢复数据。
db写数据 =》cache缓存 =》磁盘I/O 有日志做保障。
事务并发存在的问题
事务处理不经隔离,并发执行通常会有以下问题:
脏读(Dirty Read):事务B读取了事务A尚未提交的数据。
不可重复读(NonRepeatable Read):事务B两次读之间事务A更新了数据。B读取了A已提交的数据。
虚读(Phantom Read)幻读:事务B先查询,A新增或删除了一条满足B查询条件的记录,B再查询。(B读了A新增的;或没读到A删除的)
事务处理命令
SELECT @@AUTOCOMMIT; 查看MySQL是否自动提交事务
0表示手动提交事务,1表示自动提交事务,设置事务提交方式为手动提交方式:
mysql> set autocommit=0;
Query OK, 0 rows affected (0.00 sec)
BEGIN; 开启一个事务
COMMIT; 提交一个事务
ROLLBACK; 回滚一个事务到初始的位置
SAVEPOINT point1; 设置一个名字为point1的保存点
ROLLBACK TO point1; 事务回滚到保存点point1,而不是回滚到初始状态
SET TX_ISOLATION='REPEATABLE-READ'; 设置事务的隔离级别
SELECT @@ TX_ISOLATION; 查询事务的隔离级别
事务的隔离级别
简要概述
TRANSACTION_READ_UNCOMMITTED 未提交读
TRANSACTION_READ_COMMITTED 已提交读
TRANSACTION_REPEATABLE_READ 可重复读
TRANSACTION_SERIALIZABLE 串行化
隔离级别 | 脏读 | 不可重复读 | 幻读 |
---|---|---|---|
未提交读 | 可以 | 可以 | 可以 |
已提交读 | 不可以 | 可以 | 可以 |
可重复读 | 不可以 | 不可以 | 可以 |
串行化 | 不可以 | 不可以 | 不可以 |
把手动提交事务的设置打开:
set autocommit=0;
查询事务的隔离级别,看到MySQL默认是在可重复读级别(只允许幻读)
mysql> select @@tx_isolation;
+-----------------+
| @@tx_isolation |
+-----------------+
| REPEATABLE-READ |
+-----------------+
1 row in set, 1 warning (0.00 sec)
实验
未提交读
开两个窗口,同时使用user表。都设置成最低级别:
set tx_isolation='READ-UNCOMMITTED';
A 回滚了
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> update user set age=21 where name='zhang san';
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> rollback;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from user where name='zhang san';
+----+-----------+-----+-----+
| id | name | age | sex |
+----+-----------+-----+-----+
| 4 | zhang san | 20 | M |
+----+-----------+-----+-----+
1 row in set (0.00 sec)
B 读到了A未提交的数据,并把其拿去做业务了 脏读
mysql> select * from user where name='zhang san';
+----+-----------+-----+-----+
| id | name | age | sex |
+----+-----------+-----+-----+
| 4 | zhang san | 21 | M |
+----+-----------+-----+-----+
1 row in set (0.00 sec)
已提交读
set tx_isolation='READ-COMMITTED';
禁止了脏读
A 的过程
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> update user set age=20 where name='zhang san';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
B在A未提交时不会产生脏读,会产生可重复读
mysql> select * from user where name='zhang san';
+----+-----------+-----+-----+
| id | name | age | sex |
+----+-----------+-----+-----+
| 4 | zhang san | 21 | M |
+----+-----------+-----+-----+
1 row in set (0.00 sec)
A提交后,B查询到了新的数据
mysql> select * from user where name='zhang san';
+----+-----------+-----+-----+
| id | name | age | sex |
+----+-----------+-----+-----+
| 4 | zhang san | 20 | M |
+----+-----------+-----+-----+
1 row in set (0.00 sec)
可重复读
A
mysql> begin;
Query OK, 0 rows affected (0.00 sec)
mysql> update user set age=21 where name='zhang san';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
B 在A commit后任然查不到更新的数据,禁止了不可重复读
mysql> select * from user where name='zhang san';
+----+-----------+-----+-----+
| id | name | age | sex |
+----+-----------+-----+-----+
| 4 | zhang san | 20 | M |
+----+-----------+-----+-----+
1 row in set (0.00 sec)
幻读
表面上幻读不存在,但是有幻读。
A 往表里插入一条数据
mysql> insert into user(name,age,sex) values('tt',21,'W');
Query OK, 1 row affected (0.00 sec)
B 表面上查询不到
mysql> select * from user where age=21;
+----+-----------+-----+-----+
| id | name | age | sex |
+----+-----------+-----+-----+
| 4 | zhang san | 21 | M |
| 5 | li si | 21 | W |
| 7 | zhang ai | 21 | W |
| 8 | wu liu | 21 | M |
+----+-----------+-----+-----+
4 rows in set (0.00 sec)
但是 B 能够修改这条数据
mysql> update user set age=22 where name='tt';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0