一、事务
事务是一个原子操作,是一个最小执行单元,可以甶一个或多个SQL语句组成。在同一个事务当中,所有的SQL语句都成功执行时,整 个事务成功,有一个SQL语句执行失败,整个事务都执行失败。
1、事务特性
- 原子性:事务的整个过程如原子操作一样,最终要么全部成功,或者全部失败,从最终结果来看这个过程是不可分割的。
- 一致性:事务执行前和执行后的数据保持一致。
- 隔离性:一个事务的执行不能被其他事务干扰。
- 持久性:一个事务一旦提交,他对数据库中数据的改变就应该是永久性的。
2、Mysql中事务操作
mysql中事务默认是隐式事务,执行insert、update、delete操作的时候,数据库自动开启事务、提交或回滚事务。是否开启隐式事务是由变量autocommit控制的。
隐式事务
事务自动开启、提交或回滚,比如insert、update、delete语句,事务的开启、提交或回滚由mysql内部自动控制的。查看变量autocommit是否开启了自动提交。
显式事务
事务需要手动开启、提交或回滚,由开发者自己控制。有2种方式手动控制事务:
-- 方式1、
-- 设置不自动提交事务
set autocommit=0;
insert into class values(null,"java1903");
insert into class values(null,"java1906");
commit; -- 提交 或者 rollback回滚;
set autocommit=1; -- 改回自动提交事务
-- 方式2、
start transaction; -- 开启事务
insert into class values(null,"java1908");
insert into class values(null,"java1911");
commit; -- 提交 或者 rollback回滚;
3、savepoint关键字
在事务中我们执行了一大批操作,我们可以将一大批操作分为几个部分,然后指定回滚某个部分,可以使用savepoint来实现。例如下面的代码只有第一条添加语句被成功添加了。
start transaction; -- 开启事务
insert into class values(null,"java2002");
savepoint part1; -- 设置保存点
insert into class values(null,"java2005");
rollback to part1; -- 回滚至保存点
commit; -- 提交事务
savepoint需要结合rollback to sp1一起使用,可以将保存点sp1到rollback to sp1之间的操作回滚掉。
4、只读事务
表示在事务中执行的是一些只读操作,如查询,但是不会做insert、update、delete操作,数据库内部对只读事务可能会有一些性能上的优化。
二、mysql并发问题
1、脏读
一个事务在执行的过程中读取到了其他事务还没有提交的数据。两个事务同时操作同一数据,A事务对该数据进行了修改还没提交的时候,B事务访问了该条事务,并且使用了该数据,此时A事务回滚,那么B事务读到的就是脏数据。
2、不可重复读
在同一事务中,多次读取同一数据返回的结果有所不同,换句话说,后续读取可以读到另一事务已提交的更新数据。
这种情况发生在一个事务内多次读同一数据。A事务查询某条数据,该事务未结束时,B事务也访问同一数据并进行了修改。那么在A事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的的数据可能是不一样的。
3、幻读
当同一个查询在不同时间产生不同的行集时,就会出现所谓的虚影问题。例如,如果执行了两次SELECT,但是第二次返回了第一次没有返回的行,那么这行就是“幻影”行(幻读)。
假设在子表的id列上有一个索引,并且你想从表中读取和锁定标识符值大于100的所有行,目的是稍后更新所选行的某些列:
SELECT * FROM child WHERE id > 100 FOR UPDATE;
查询从id大于100的第一条记录开始扫描索引。让表包含id值为90和102的行。如果在扫描范围内的索引记录上设置的锁没有锁定在间隙(在本例中是90和102之间的间隙)中进行的插入,则另一个会话可以将id为101的新行插入到表中。如果要在同一事务中执行相同的SELECT,则会在查询返回的结果集中看到id为101的新行。
三、事务的隔离级别
事务隔离是数据库处理的基础之一。用于在多个事务同时进行更改和执行查询时微调性能与可靠性、一致性和结果可再现性之间的平衡。隔离级别分为4种:
1、读未提交( Read Uncommitted)
SELECT语句以非锁定方式执行。因此,使用这个隔离级别,这样的读取是不一致的。这也被称为脏读。
2、读已提交( Read Committed)
每个一致性读取,即使在同一个事务中,也会设置和读取自己的新快照。对于锁读(SELECT with For UPDATE或For SHARE)、UPDATE语句和DELETE语句,InnoDB只锁索引记录,而不锁它们之前的空白,因此允许在被锁的记录旁边自由插入新记录。间隙锁定仅用于外键约束检查和重复键检查。由于禁用了间隙锁定,因此可能会出现幻读问题,因为其他事务可以将新行插入间隙。
使用READ COMMITTED有额外的效果:
- 对于UPDATE或DELETE语句,InnoDB只对更新或删除的行持有锁。不匹配行的记录锁在MySQL计算完WHERE条件后释放。这大大降低了死锁的概率,但死锁仍然可能发生。
- 对于UPDATE语句,如果一行已经被锁定,InnoDB执行“半一致”读取,将最新提交的版本返回给MySQL,以便MySQL可以确定该行是否符合UPDATE的WHERE条件。如果行匹配(必须更新),MySQL再次读取该行,这一次InnoDB要么锁定它,要么等待锁定它。
3、可重复读( Repeatable Read)
这是InnoDB默认的隔离级别。同一事务中的一致性读取,读取由第一次读取建立的快照。这意味着,如果在同一个事务中发出几个普通(非锁定)SELECT语句,这些SELECT语句彼此之间也是一致的。
对于锁定读(SELECT with For UPDATE或For SHARE)、UPDATE和DELETE语句,锁定取决于语句是使用具有唯一搜索条件的唯一索引,还是使用具有唯一搜索条件的范围类型搜索条件。
- 对于具有唯一查询条件的唯一索引,InnoDB只锁定找到的索引记录,而不锁定之前的空白记录。
- 对于其他搜索条件,InnoDB锁定扫描的索引范围,使用间隙锁或下一键锁来阻止其他会话插入到范围所覆盖的间隙中。
4、串行化( Serializable)
Serializable 是最严格的隔离级别。在Serializable隔离级别下,所有事务按照次序依次执行,因此,脏读、不可重复读、幻读都不会出现。虽然 Serializable 隔离级别下的事务具有最高的安全性,但是,由于事务是串行执行,所以效率会大大下降,应用程序的性能会急剧降低。如果没有特别重要的情景,一般都不会使用Serializable隔离级别。