1.为什么要使用事务
准备测试用例
mysql> drop table if exists accout;
Query OK, 0 rows affected, 1 warning (0.00 sec)
mysql> create table accout(
-> id int primary key auto_increment,
-> name varchar(20) comment '账户名称',
-> money decimal(11,2) comment '金额'
-> );
Query OK, 0 rows affected (0.05 sec)
mysql> insert into accout(name, money) values
-> ('小白', 5000),
-> ('小黑', 1000);
Query OK, 2 rows affected (0.01 sec)
Records: 2 Duplicates: 0 Warnings: 0
比如说,小白打两千块钱到小黑的账户上去,对应的操作应该是小白账户上金额少2000,小黑账户上金额多2000。
-- 小白账户减少2000
update accout set money=money-2000 where name = '小白';
-- 四十大盗账户增加2000
update accout set money=money+2000 where name = '小黑'
假如在执行以上第一句SQL时,出现网络错误,或是数据库挂掉了,小白的账户会减少2000,但是 小黑的账户上就没有了增加的金额。
解决方案:使用事务来控制,保证以上两句SQL要么全部执行成功,要么全部执行失败。
但是计算机底层并不可能是真的不执行,只能是执行了,执行了一半发现了错误,恢复现场,把数据还原成未执行之前的状态。
这种机制叫做“回滚”
2.事物的概念
事务指逻辑上的一组操作,组成这组操作的各个单元,要么全部成功,要么全部失败。 在不同的环境中,都可以有事务。对应在数据库中,就是数据库事务。
3.事物的使用
(1)开启事务:start transaction;
(2)执行多条SQL语句
(3)回滚或提交:rollback/commit;
说明:rollback即是全部失败,commit即是全部成功。
示例:
mysql> start transaction;
Query OK, 0 rows affected (0.00 sec)
mysql> update accout set money = money-2000 where name = "小白";
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
**mysql> update accout set money = money+2000 where name = "小黑";
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)
4.事务的四个关键特性
- 原子性,这个是最核心的性质。这个性质就是之前篇幅里面强调的打钱两个账户的money列的数据变动必须是一气呵成的。
- 一致性,执行事务前后数据是靠谱的。比如说转账前小白账户5000 小黑账户1000,转账后小白账户3000 小黑账户3000,这个数据是正确的一致的,不能转账后小黑数据突然变成45000了。
- 持久性,事务修改的内容的写到硬盘的,持久存在的,重启也不会丢失。
- 隔离性,隔离性是为了解决并发执行事务引起的问题
4.1 隔离性
我们可以这样理解隔离
一个餐馆(服务器),同一时刻要给多个顾客(客户端)提供服务这些顾客提出的请求是可能是一个接一个来的,也可能是一股脑一起来了一波。
此时,服务器同时处理多个客户端的请求,就称为“并发”(齐头并进的感觉)
数据库也是服务器,就有可能多个客户端都给服务器提交事务,数据库就需要并发的处理多个事务。
如果并发的这些事务,是修改不同的表/不同的数据,基本不会出现问题,但是如果修改的是同一个表/同一个数据, 可能会带来一定的问题的,比如多个客户端一起尝试对同一个账户进行转账操作,此时就可能会把。这个数据打乱了。
事务的隔离性存在的意义就是为了 在数据库并发处理事务的时候不会有问题(即使有问题, 问题也不大)。那么并发事务究竟会产生什么问题呢?
4.2并发事务所产生的问题
- 脏读问题:
一个事务A正在对数据进行修改的过程中,还没提交之前,另一个事务B,也对同一个数据进行了读取,此时B的读操作就称为“脏读”,读到的数据也称之为"脏数据";
为了解决脏问题,mysql引入"写操作加锁"(学过操作系统的同学有么有颤抖)。
加锁的操作降低了并发程度,提高了隔离性。 - 不可重复读
事物1已经提交了数据,此时事务2开始读取数据,在读取数据的过程中,事务3又提交了新的数据,此时意味着同一事务(事务2),多次读入数据时,数据读出的结果会不同。
为了解决不可重复读问题,mysql引入"读操作加锁"(学过操作系统的同学有么有又一次颤抖)。 - 幻读问题
在读加锁和写加锁的前提下,一个事务两次读取同一个数据,发现读取的数据值是一样的,但是结果集不一样,这种就称之为幻读。
为了解决这个问题,数据库使用“串行化”这样的方式来解决幻读,彻底放弃并发的处理事务,一个接一个地串行处理事务;这样做并发的效率是最低的,但是事务之前的隔离性是最高的。
4.3MySQL隔离级别
针对上述的问题,MySQL提供四个隔离级别应对上述情况
- read uncommitted 没有任何锁限制,并发最高,隔离性最差;
- read committed 给写加锁,并发程度降低,隔离性提高了;
- repeatable read 给写和读都加锁,并发程度再次降低,隔离性再次提高
- serializable 串行化,并发程度最低,隔离性最高。
以上是MySQL的内置机制,可以通过修改配置文件,来设置当前MySQL工作在哪种状态下。
至此MySQL的事务就简单介绍到这里