(一)对事务的理解
- 事务是一组DML(数据操纵语言).
- 它们在逻辑上存在相关性,这一组语句是一个整体,要么全部成功,要么全部失败。
- 事务规定不同的客户端看到的数据是不相同的。
(二)使用事务的实例理解
例如银行转账中,
张三存入了100块钱,李四存入了200块钱;
现在张三要给李四转50块钱;
正常情况下转账结束以后张三还有50块钱,李四有250块钱。
首先,先用一条语句更新张三的账户信息,从张三的账户中减50块钱。
接下来使用一条语句更新李四的账户信息,在李四的账户中加50块钱。
但是由于操作人员的失误,给李四的账户中加了 55 块钱,即给李四的账户多加了5块钱。
此时一种方法是再使用一条语句在李四的账户中减去五块钱,但是此方法风险性太高,不建议使用。
另外一种方法就是返回到给张三账户减去50语句成功之后,怎样回到上一个状态呢,这时就可以使用事务。使用事务的话可以不断设置保存点,当之后的语句出现错误时,可以直接回到任意保存点,也可以选择回到初始状态。
(三)事务的基本操作
- start transaction——开始事务
- savapoint 保存点名——设置保存点
- rollback to 保存点名——返回保存点
- rollback——返回初始状态
- commit——提交事务
(四)对银行转账实例的应用
把转账的过程看成一次事务。
银行转账过程如下:
- 创建一个银行账户表
- 向表中添加纪录
- 进行银行转账——开启事务
3.1 更新张三的账户数据(减去50)
3.2 设置保存点zs
3.3 更新李四的账户数据(出错,加了55)
3.4 回滚至保存点zs
3.5 重新更新李四的账户数据 - 提交事务
//1. 创建一个银行账户表
mysql> create table account(
-> id int unsigned auto_increment primary key,
-> name varchar(20),
-> balance int
-> );
Query OK, 0 rows affected (0.04 sec)
//2. 向表中添加纪录
mysql> insert into account(name,balance) values
->('张三','100'),
->('李四','200');
//查看表中信息
mysql> select * from account;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | 张三 | 100 |
| 2 | 李四 | 200 |
+----+------+---------+
//3. 进行银行转账——开启事务
start transaction;
//3.1 更新张三的账户数据(减去50)
update account set balance=balance-50 where id=1;
//查看此时表中信息
mysql> select * from account;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | 张三 | 50 |
| 2 | 李四 | 200 |
+----+------+---------+
//3.2 设置保存点zs
savepoint zs;
//3.3 更新李四的账户数据(出错,加了55)
update account set balance=balance+55 where id=2;
//查看此时表中信息
mysql> select * from account;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | 张三 | 50 |
| 2 | 李四 | 255 |
+----+------+---------+
//3.4 回滚至保存点zs
rollback to zs;
//查看此时表中信息
mysql> select * from account;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | 张三 | 50 |
| 2 | 李四 | 200 |
+----+------+---------+
//3.5 重新更新李四的账户数据
update account set balance=balance+50 where id=2;
//查看表中数据信息
mysql> select * from account;
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | 张三 | 50 |
| 2 | 李四 | 250 |
+----+------+---------+
//4. 提交事务
commit;
(五)使用事务的注意事项
- 如果没有设置保存点,也可以回滚,只能回滚到事务的初始状态(直接使用rollback);
- 如果一个事务已经被提交了,则不可以回滚。
- 可以在事务中设置多个保存点,选择回退到哪个保存点。
- 开始事务使用start transaction;
- InnoDB引擎支持事务,MyISAM引擎不支持事务。
(六)事务的隔离级别
不隔离容易产生的三个问题:
- 脏读
(1)一个事务读取了另一个事务修改但未提交的数据。
即一个事务对数据进行了修改,但是还没有提交,此时另一个事务读取了这个数据,这时读到的数据即为脏读。
(2)解决办法:在一个事务修改数据但未提交的时候不允许其他事务读取该数据。 - 不可重复读
(1) 在一个事务中,读取了一条数据,此时,又有一个新的事务访问了该数据并且修改了数据,则原来的事务再次读取这条数据的话读到的结果是不一样的(即不能读到相同的数据),称为不可重复读。
(2)解决方法:只有在修改事务提交之后才可以读取数据。 - 幻读
(1)如第一个事务对表中所有数据进行修改,此时另一个事务向表中重新插入一个记录,则第一个事务再次读取数据的时候发现表中还有未修改的数据,好像发生了幻觉一样。
(2)解决方法:在操作事务完成处理之前,其他事务不可以添加事务。
事务的隔离级别
隔离级别 | 脏读 | 不可重复读 | 幻读 | 加锁读 |
---|---|---|---|---|
读未提交(read uncommited) | ✔ | ✔ | ✔ | 不加锁 |
读已提交(read commited) | ✘ | ✔ | ✔ | 不加锁 |
可重复读(repeated read) | ✘ | ✘ | ✔ | 不加锁 |
可串行化(serializable) | ✘ | ✘ | ✘ | 加锁 |
注:
- ✔会发生该问题,✘不会发生该问题。
- mysql默认为可重复读
- 加锁:同一时间只能有一个事务访问数据表。
(1)设置事务的隔离级别
注意:修改的只是当前事务的隔离级别。
set session transaction isolation level 隔离级别;
(2)查看当前的隔离级别
select @@tx_isolation;
(七)事务的ACID特性
- 原子性(Atomicity)
事务是应用中最小的执行单位,如原子一样不可再分,事务是应用中不可再分的最小逻辑执行体。 - 隔离型(IsolationConsistency)
各个事务的执行互不干扰,任意一个事务的内部操作对其他并发事务都是隔离的。即并发执行的事务不能看到对方的中间状态,不能互相影响。 - 一致性(Consistency)
数据执行的结果,必须使数据库从一个一致性状态变到另一个一致性状态。 - 持久性(Durability)
事务一旦被提交,它对数据库所做的改变都要记录到永久存储中。