作为开发人员对数据库事务应该都不陌生,可是若是知其然而不知其因此然的话,在开发中不免写出来的代码存在bug,本文主要介绍mysql中的事务,重点讲解事务的隔离级别。mysql
原子性是指事务是一个不可分割的工做单位,事务中的操做要么所有执行,要么所有都不执行。
例如:
begin // 开启事务
A:update user set account=account+1 where id =1;
B:update user set account=account+1 where id =1;
commit
这个事务,执行commit时,在么两条语句都执行成功,若是出错,执行rollback时,两条语句的操做都会回滚到原始状态;sql
undo log保证原子性
在操做任何数据以前,首先将数据备份到一个地方(这个存储数据的地方就是undo log)。而后进行数据的修改,若是用户出现了错误或者用户执行了rollback语句,系统可用利用undo log中的备份的数据恢复到事务开始以前的状态。
注意:undo log是逻辑日志
能够理解为:数据库
事务执行前和事务执行后,数据库的完整性约束不被破坏。即事务的执行是从一个有效状态转移到另外一个有效状态;
例如:
tom给jack转帐50元,若是从tom帐户减小后系统故障或其它缘由没有给jack加上50元,而事务尚未执行完毕,数据库会将整个转帐过程回滚,保证数据的一致性;并发
隔离性是指在并发操做中,不一样事务之间应该隔离开来,使每一个并发中的事务不会互相干扰;ide
一旦事务提交成功,事务中全部的数据操做都必须被持久化保存到数据库中,即便提交事务后,数据库崩溃,在数据库重启时,也必须能保证经过某种机制恢复数据。
redo log保证持久性
redo log记录的是新数据的备份,在事务提交前,只要将redo log持久化便可,不须要将数据持久化。当系统崩溃时,虽然数据没有持久化,可是redo log已经持久化,系统能够根据redo log的内容,将全部数据恢复到最新的状态。.net
上面介绍了数据库的事务特性,其中隔离性,是咱们本文中重点须要解说的,设置不一样的数据库事务之间的隔离级别,会解决相应的事务并发问题。那么下面说一下事务都会引发哪些问题:3d
事务A读取到了事务B已经修改但还没有提交的数据,若是事务B回滚,A读取的数据与上次不一致;不符合事务的一致性要求;
例如:A(图左)和B(图右)同时开启事务,A执行以下命令:
用户A并未提交事务,可见在事务B中两次查询的结果已经发生变化,读取了事务A中未提交的数据;日志
事务A读取到了事务B已经提交的修改数据,不符合隔离性;
上图可见,事务B读取到了事务A所修改的数据;code
事务A读取到了事务B提交的新增数据,不符合隔离性;
事务B读取到了事务A提交的新增数据;blog
隔离级别等级:
查看隔离级别:
show variables like '%isolation%'; SELECT @@GLOBAL.transaction_isolation, @@transaction_isolation;
设置隔离级别:
全局:SET GLOBAL TRANSACTION ISOLATION LEVEL REPEATABLE READ; 会话:SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
读未提交是数据库事务隔离级别最低的级别,它任何问题都没有解决;
设置数据库事务隔离级别为读未提交
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
读已提交它主要解决脏读的问题;
设置数据库事务隔离级别为读已交
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
能够看出,读已提交已经解决了脏读的问题;
设置事务隔离级别:
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;
能够看到,就算事务A已经提交,事务B也不会再读到事务A提交的数据;
那么可重复读是否解决了幻读呢?
咱们试验一下:
这是怎么回事呢?不是说好的可重复读不会解决幻读的问题吗?为何这里新插入的数据就是没有在事务B中读取到呢? (下节解答)
串行化就很少说了,它实际上是将事务按排队的处理方式,可是这样会使事务很是低率的。
咱们先来看个示例:
从上面的执行过程能够看出,在A事务提交以后,B事务虽然执行查询时没有 问题,可是在B事务执行更新以后,再查询时,帐户直接少了100;这是为何呢?
InnoDB的MVCC经过每行记录后面的两个隐藏字段来实现的,建立时的版本号和删除时的版本号。每一个事务开始版本号都会递增。
SELECT:
快照读:
读取的是快照版本,也就是历史版本;普通的select就是快照读
当前读:
读取的是最新版本,update、delete、insert、select...lock in share mode、select ... for update是当前读;
3.3节疑惑解答
事务A执行更新以后,提交了事务,而事务B再执行更新的时候,它实际上是一个当前读,可以读取到最新的已经被事务A修改后的数据(前提事务A已经提交)。
这块内容比较复杂,能够参考:
https://blog.csdn.net/shenchaohao12321/article/details/92801779