MySql的事务机制

概述

数据库事务( transaction)是访问并可能操作数据库中数据项的一个操作序列,这些操作要么全部执行,要么全部不执行,是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。

事务的基本属性(ACID)

ACID代表atomicity(原子性), consistency(一致性),isolation(隔离性),durability(持久性)这四个单词的首字母简写。这些属性都是数据库系统所必需的,通常都是与数据库事务紧密关联的,mysql严格遵守了ACID原则。

原子性(atomicity)

事务是数据库能够执行操作的最小工作单位,也就是说一个事务对数据库所做的更改要么在事务提交后全部成功,要么回滚后撤销所有的更改。

一致性(consistency)

在事务每次提交之后、回滚之后、执行期间,数据库的数据始终保持统一状态。即如果跨多个表更新了相关数据,则查询将看到的是所有旧值或所有新值,而不是新旧值的混合。例如A账户转账给B账户50元,最终的结果必定是A账户减少50元,B账户增加50元,不可能A扣了钱B没有增加钱,保证数据的一致性。

隔离性(isolation)

隔离性是针对多个事务并发执行的问题。多个事务在执行过程中相互保护(隔离),他们不能互相干扰,也不能看到彼此的未提交数据。这种隔离是通过锁定机制实现的。例如一个事务T1正在操作B账户存款50元,此刻另一个事务T2就不能从B账户中取钱,必须等到T1执行完毕后才能执行,否则很有可能T2事务会读到T1事务未提交50元。当然有经验的用户在确定事务确实不会互相干扰时,可以调整隔离级别,减少保护措施,从而提高性能和并发性

持久性(durability)

事务所导致的数据变更是持久化的。也就是说如果事务成功提交了,那么即便此刻发生了系统崩溃、电源故障等许多非数据库应用程序所导致的问题,数据库最终在恢复之后也能保证将数据写入磁盘,保证数据的持久化。mysql是通过doublewrite buffer(双写缓冲区)这种方式来保证数据的持久化的,有兴趣的同学可以自行查阅相关资料。

并发事务所带来的问题

在程序实际运行的过程中往往是n个事务并发执行的,如果两个事务操作的不是同一张表还好,但如果发生两个事务同时对一张表进行insert、delete、update、select操作,而我们有没有设置正确的事务隔离级别,最终就会导致程序发生错误,通常情况下并发事务会带来以下三种问题:

  1. 脏读

    事务T1读取了事务T2未提交的数据,然后T2回滚,那么T1读到的就是脏数据。

  2. 不可重复读

    事务T1执行期间对数据A进行了多次读取,事务T2同时也对数据A进行了多次修改并提交,导致T1在事务执行过程中多次读取的数据A不一致。(针对的是另一个已提交事务的update操作导致本次事务多次读取到的同一批数据值不一样

  3. 幻读

    事务T1执行期间执行了多次读取满足某个条件的查询,在第一次执行查询时返回了5条记录,然后事务T2此刻执行新增了一条满足条件的记录然后提交。紧接着T1再次执行满足条件的查询返回6条记录,这个多出来的一条记录就好像突然凭空生成一样。(针对的是另一个已提交事务的insert和delete操作导致本次事务多次读取到的同一批数据的数量不一样

MySQL事务隔离级别

事务隔离级别从高到低,mysql的InnoDB支持的隔离级别为:

SERIALIZABLE

最高隔离级别,相当于每个事务都是按照执行的顺序串行化执行。

REPEATABLE READ

mysql的默认事务隔离级别,确保了一次事务中同一个查询返回的结果都是一样的。解决了脏读、不可重复读的问题。

READ COMMITTED

满足了数据库事务最简单的隔离定义,即一个事务只能读取另一个事务已提交的修改。解决了脏读的问题,但是由于其是能够读取其它事务提交的数据,所以也就会发生不可重复读的问题。

READ UNCOMMITTED

相当于两个事务之间没有做任何隔离,任何事务未提交的数据都能够被其它事务读取到,也就是我们所说的脏读。

mysql不同的事务隔离级别在多个事务并发执行下解决的数据问题如下表所示:

事务隔离级别 是否会发生脏读 是否会发生不可重读读 是否会发生幻读
读未提交(READ UNCOMMITTED)
读已提交(READ COMMITTED)
可重复读(REEATABLE READ)
串行化(SERIALIZABLE)