事务
数据库事务(Database Transaction) :就是一组DML语句组成,这些语句在逻辑上存在相关性,这一组DML语句要么全部成功,要么全部失败,是一个整体。MySQL提供一种机制,保证我们可以达到这种效果,事务还规定不用的客户端看到的数据是不相同的。
- 通过案例来理解事务:
A同学像B同学转账1000元
1)A同学余额被扣款1000元
2)B同学余额增加1000元
上面两个步骤,如果中间任何环节没有出错,那么转账成功。但是如果第一步扣款完成后,第二步B同学没有增加1000元,此时A同学余额岂不是白扣了?所以出现了数据库事务
事务的基本操作
案例:银行转账的例子就是需要考虑事务的问题
创建测试表
mysql> create table account(
-> id int primary key,
-> name varchar(50) not null default'',
-> balance decimal(10,2) not null default 0.0
-> );
- 开始一个事务
start transaction;
- 创建一个保存点
savepoint 保存点名;
- 回到保存点
rollback to 保存点名;
mysql> start transaction; --开启事务
Query OK, 0 rows affected (0.00 sec)
mysql> savepoint aa; --设置保存点aa
Query OK, 0 rows affected (0.00 sec)
mysql> insert into account value(1,'zs',10);--插入一条记录
Query OK, 1 row affected (0.02 sec)
mysql> savepoint bb; --设置保存点bb
Query OK, 0 rows affected (0.00 sec)
mysql> insert into account value(2,'ls',100000);--再插入一条记录
Query OK, 1 row affected (0.00 sec)
mysql> select * from account;--查看记录,两条都在
+----+------+-----------+
| id | name | balance |
+----+------+-----------+
| 1 | zs | 10.00 |
| 2 | ls | 100000.00 |
+----+------+-----------+
2 rows in set (0.01 sec)
mysql> rollback to bb;--回滚到bb状态
Query OK, 0 rows affected (0.01 sec)
mysql> select * from account;--此时第2条记录没有了
+----+------+---------+
| id | name | balance |
+----+------+---------+
| 1 | zs | 10.00 |
+----+------+---------+
1 row in set (0.00 sec)
mysql> rollback to aa;--回滚到aa状态
Query OK, 0 rows affected (0.00 sec)
mysql> select * from account;--此时第一条记录也没有了
Empty set (0.00 sec)
事务操作的注意事项
- 如果没有设置保存点,也可以回滚,只能回滚事务的开始。直接使用rollback(前提是事务还没有提交)
- 如果一个事务被提交了(commit),则不可以回滚(rollback)
- 可以选择回退到哪个保存点
- InnoDB支持事务,MyISAM不支持事务
- 开始事务使用start transaction
事务的隔离级别
我们怎么解决隔离性的问题呢?那就是上锁,其实和JAVA的线程是一样的,多个线程调用同一个资源的时候,一但某一个线程调用到该资源,那么别的线程就不能在调用这个资源,也是加锁。
MySQL提供了一种机制,可以让不同的事务在操作数据时,具有隔离性。从而保证数据的一致性。
无隔离性的问题
- 脏读
是指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也来访问这个数据,然后使用了这个数据。
举个例子:A的原工资为1000,财务人员将A的工资改为8000(但未提交事务);A此时读取自己的工资,发现变为8000;而财务发现是自己操作有误,回滚了事务,将A的工资又变为1000。这种情况A的工资8000就是一个脏数据。
- 不可重复读
是指在一个事务内,多次读同一数据,在这个事务还没有结束时,另外一个事务也访问该数据。那么在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的数据可能就是不一样的。这样就发生了在一个事务中两次读到的数据是不一样的,因此成为不可重复读。(即不能读到相同的数据内容)
举个例子:在事务1中,A读取了自己的工资为1000,操作并没有完成;在事务2中,这时财务人员修改了A的工资为2000,并提交了事务;在事务1中,A再次读取自己的工资,此时工资变为2000。
解决方法:如果只有在修改事务完全提交之后才可以读取数据,则可以避免该问题。
-
幻读
是指当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,这种修改涉及到表中的全部数据行。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入一行新数据。那么,以后就会发生操作第一事务的用户发现表中还有没有修改的数据行,就好像发生了幻觉一样。
举个例子:目前工资为1000的员工有10人;事务1,读取所有工资为1000的员工;此时事务2向employee表中插入了一条员工记录,工资也是1000;事务1再次都区所有工资为1000的员工,共有11条记录。
解决方法:如果在操作事务完成数据处理之前,任何其他事务都不可以添加新数据,则可避免该问题 -
设置事务的隔离级别
语法:
set session transaction isolation level read uncommitted;
读未提交:read uncommitted
读已提交:read commited
可重复读:repeatable read
可串行化:serializable
- 查看当前事务的隔离级别
语法:select @@tx_isolation;
mysql> select @@tx_isolation;
+------------------+
| @@tx_isolation |
+------------------+
| READ-UNCOMMITTED |
+------------------+
MySQL默认的隔离级别是可重复读,一般情况下不要修改
事务的ACID特性
- 原子性:
事务是应用中最小的执行单位,就像原子是自然界的最小颗粒一样,具有不可再分的特征一样,事务是在应用中不可在分的最小逻辑执行体 - 一致性
事务执行的结果,必须使数据库从一个一致性状态,变到另一个一致性状态。当数据库只包含事务成功提交的结果时,数据库处于一致性状态。如果系统运行发生中断,某个事务尚未完成而被迫中断,而改未完成的事务对数据库所做的修改已被写入数据库,此时数据库就处于一种不正确(不一致)的状态。因此一致性是通过原子性来保证的。 - 隔离性
同一时间,只允许一个事务请求同一数据,不同的事务之间彼此没有任何干扰。比如A正在从一张银行卡中取钱,在A取钱的过程结束前,B不能向这张卡转账。 - 持久性
持久性是指一个事务一旦被提交,它对数据库所做的改变都要记录到永久存储其中