理解MySQL事务

理解MySQL事务

事务是什么

百度百科是这么定义的:

事务(Transaction),一般是指要做的或所做的事情。在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元。在关系数据库中,一个事务可以是一条SQL语句,一组SQL语句或整个程序。

维基百科:

数据库事务表示在数据库管理系统内针对数据库执行的工作单元,该工作单元以独立于其他事务的连贯可靠的方式进行处理。事务通常表示数据库中的任何更改。数据库环境中的事务有两个主要目的:

  1. 提供可靠的工作单元,允许从故障中正确恢复,即使在系统故障的情况下也能保持数据库的一致性。

    例如,当执行过早和意外停止(完全或部分)时,在这种情况下,对数据库的许多操作仍未完成,状态不明确。

  2. 在并发访问数据库的程序之间提供隔离。如果不提供这种隔离,则程序的结果可能是错误的。在数据库中以一致模式完成的任何逻辑计算都称为事务。一个例子是从一个银行账户转移到另一个银行账户:完整的交易需要减去从一个账户转账的金额,然后将相同的金额添加到另一个账户。

对比来看,维基百科中的阐述更值得分析一下:

  1. 数据库事务是什么?

    数据库事务表示在数据库管理系统内针对数据库执行的工作单元;事务通常表示数据库中的任何更改。

也就是说,数据库事务是针对于数据库中的一组操作。这一组操作根据应用场景的不同,可能一个SQL搞定,也可能需要两个,甚至N个。这多个SQL操作,被当成一个工作单元,它是个整体。维基百科还举了一个我们很熟的例子:银行转账,A给B转账,对应了两个SQL操作,A的钱减少,B的钱增加,但这个是一件事,不能有中间态,即使转账失败了,A、B的钱还是原样。

  1. 事务的作用是什么?

    提供可靠的工作单元,允许从故障中正确恢复。

    在并发访问数据库的程序之间提供隔离。

    在数据库中以一致模式完成的任何逻辑计算都称为事务。

事务要让工作单元可靠,即使发生故障,也能正确恢复;如果有多个线程访问数据库时,每个线程之间互不影响,他们各自的操作在自己看来都是正常的;因此,事务的目的就是要维持工作单元的一致性。

可以总结出事务的四个特性,我们把它们称作ACID:

  1. 原子性(Atomicity):原子性保证每个事务都被视为一个“单元”,它要么完全成功,要么完全失败。如果事务中一个SQL执行失败,则其他已执行的sql语句都会回滚,数据回退到事务前的状态。
  2. 一致性(Consistency):一致性确保事务只能将数据库从一个一致状态带到另一个一致状态,保持数据库不变性。也就是所有写入数据库的数据在既定的规则下,都是有效的。
  3. 隔离性(Isolation):隔离性是指,事务内部的操作与其他事务是隔离的,并发执行的各个事务之间不能互相干扰。
  4. 持久性(Durability):事务一旦提交,数据能存入硬盘,永久保存。

一句话总结下事务,事务将数据库中的一组操作看成一个工作单元,它要么全部成功,要么全部失败,目的是为保证数据的一致性。

按照严格的标准,只有同时满足ACID特性才是事务;但是在各大数据库厂商的实现中,真正满足ACID的事务少之又少。例如MySQL的NDB Cluster事务不满足持久性和隔离性;InnoDB默认事务隔离级别是可重复读,不满足隔离性;Oracle默认的事务隔离级别为READ COMMITTED,不满足隔离性......因此,与其说ACID是事务必须满足的条件,不如说它们是衡量事务的四个维度。

开启事务

MySQL默认是自动提交事务的,可以用sql查询:

select @@autocommit; 

理解MySQL事务

也就是说,每执行完一条sql数据库就会自动帮我们提交事务,但在实际情况中,一个业务操作会对应多条sql,不可能每执行完一条sql就提交,事务有多条sql时,需要执行完最后一条才能提交或者回滚。

MySQL中手动开启事务有2种方式:

start transaction; -- 使用begin也可以 

或者

set autocommit = 0; 

手动开启事务或者设置事务手动提交后,每次执行完一组sql都需要手动commit或者rollback才能生效。如果你一直没有提交,或者一直没回滚会怎么样?那肯定是,你后面的所有sql操作就跟之前的操作在一个事务里,事务没有提交,根据事务不同的隔离级别,数据的查询会呈现不一样的结果。

事务隔离级别

如果说ACID是事务的标准,那数据库的隔离级别就是为了达到这个标准而采用的策略。MySQL事务隔离级别有四种,而每一种隔离级别又存在不同的事务问题。严格的说,只有serializable这种事务隔离级别,才真正满足ACID,但是serializable会强制事务串行执行(类似Java的synchronized串行锁),就是如果一个线程在还没有提交事务,另一个线程就会一直等待,直到前一个线程提交事务,它才能继续执行。它并发效率很低,实际使用的很少。而其他事务隔离级别,又存在着各自不同的事务问题。

隔离级别 脏读 不可重复度 幻读
read uncommitted(读未提交)
read committed(读已提交) ×
repeatable read(可重复读) × ×
serializable(串行化) × × ×

查看事务隔离级别:

 select @@transaction_isolation; 

设置事务隔离级别:

set [session|global] transaction isolation level {read uncommitted | read committed | repeatable read | serializable} 

session代表当前会话,global代表全局会话。设置全局事务隔离级别:

set global transaction isolation level read uncommitted; 

脏读

事务A读到事务B未提交的数据(脏数据),这种现象叫脏读。

不可重复度

在事务A中先后两次读取同一个数据,两次读取的结果不一样,这种现象称为不可重复读。脏读与不可重复读的区别在于:前者读到的是其他事务未提交的数据,后者读到的是其他事务已提交的数据。

幻读

在事务A中按照某个条件先后两次查询数据库,两次查询结果的条数不同,这种现象称为幻读。不可重复读与幻读的区别可以通俗的理解为:前者是数据变了,后者是数据的行数变了。

总结

事务具有ACID属性,数据库不同的事务隔离级别存在各自的事务问题,这也是面试常问的问题。

参考资料

深入学习MySQL事务:ACID特性的实现原理 - 编程迷思 - 博客园 (cnblogs.com)

发表评论

相关文章

当前内容话题