跳至主要內容

MySQL中的事务

maijunxuan约 1708 字大约 6 分钟

MySQL中的事务

如何使用

MySQL的服务层不管理事务,而是由下层的存储引擎实现。比如InnoDB。

MySQL支持本地事务的语句:

START TRANSACTION | BEGIN [WORK] 
COMMIT [WORK] [AND [NO] CHAIN] [[NO] RELEASE] 
ROLLBACK [WORK] [AND [NO] CHAIN] [[NO] RELEASE] 
SET AUTOCOMMIT = {0 | 1}
  • START TRANSACTION 或 BEGIN 语句:开始一项新的事务。
  • COMMIT 和 ROLLBACK:用来提交或者回滚事务。
  • CHAIN 和 RELEASE 子句:分别用来定义在事务提交或者回滚之后的操作,CHAIN 会立即启动一个新事物,并且和刚才的事务具有相同的隔离级别,RELEASE 则会断开和客户端的连接。
  • SET AUTOCOMMIT 可以修改当前连接的提交方式, 如果设置了 SET AUTOCOMMIT=0,则设置之后的所有事务都需要通过明确的命令进行提交或者回滚

事务使用注意点:

  • 如果在锁表期间,用 start transaction 命令开始一个新事务,会造成一个隐含的 unlock tables 被执行。
  • 在同一个事务中,最好不使用不同存储引擎的表,否则 ROLLBACK 时需要对非事 务类型的表进行特别的处理,因为 COMMIT、ROLLBACK 只能对事务类型的表进行提交和回滚。
  • 和 Oracle 的事务管理相同,所有的 DDL 语句是不能回滚的,并且部分的 DDL 语句会造成隐式的提交。
  • 在事务中可以通过定义 SAVEPOINT(例如:mysql> savepoint test; 定义 savepoint,名称为 test),指定回滚事务的一个部分,但是不能指定提交事务的一个部分。对于复杂的应用,可以定义多个不同的 SAVEPOINT,满足不同的条件时,回滚 不同的 SAVEPOINT。需要注意的是,如果定义了相同名字的 SAVEPOINT,则后面定义的SAVEPOINT 会覆盖之前的定义。对于不再需要使用的 SAVEPOINT,可以通过 RELEASE SAVEPOINT 命令删除 SAVEPOINT, 删除后的 SAVEPOINT, 不能再执行 ROLLBACK TO SAVEPOINT命令。

自动提交(autocommit): Mysql默认采用自动提交模式,可以通过设置autocommit变量来启用或禁用自动提交模式

  • 隐式锁定

InnoDB在事务执行过程中,使用两阶段锁协议:

随时都可以执行锁定,InnoDB会根据隔离级别在需要的时候自动加锁;

锁只有在执行commit或者rollback的时候才会释放,并且所有的锁都是在同一时刻被释放。

  • 显式锁定

InnoDB也支持通过特定的语句进行显示锁定(存储引擎层):

select ... lock in share mode //共享锁 
select ... for update //排他锁 

MySQL Server层的显示锁定:

lock table和unlock table

事务隔离级别

SQL 标准定义了四种隔离级别,MySQL 全都支持。这四种隔离级别分别是:

  1. 读未提交(READ UNCOMMITTED)
  2. 读提交 (READ COMMITTED)
  3. 可重复读 (REPEATABLE READ)
  4. 串行化 (SERIALIZABLE)

从上往下,隔离强度逐渐增强,性能逐渐变差。采用哪种隔离级别要根据系统需求权衡决定,其中,可重复读是 MySQL 的默认级别。

事务隔离其实就是为了解决上面提到的脏读、不可重复读、幻读这几个问题,下面展示了 4 种隔离级别对这三个问题的解决程度。

img

什么是ACID

一个事务有四个基本特性,也就是我们常说的(ACID):

  1. Atomicity(原子性):事务是一个不可分割的整体,事务内所有操作要么全做成功,要么全失败。
  2. Consistency(一致性):务执行前后,数据从一个状态到另一个状态必须是一致的(A向B转账,不能出现A扣了钱,B却没收到)。
  3. Isolation(隔离性a): 多个并发事务之间相互隔离,不能互相干扰。
  4. Durability(持久性):事务完成后,对数据库的更改是永久保存的,不能回滚。

ACID靠什么保证的呢?

以MySQL为例:

A原子性由undo log日志保证,它记录了需要回滚的日志信息,事务回滚时撤销已经执行成功的sql

C一致性一般由代码层面来保证

I隔离性由锁和MVCC来保证

D持久性由内存+redo log来保证,mysql修改数据同时在内存和redo log记录这次操作,事务提交的时候通过redo log刷盘,宕机的时候可以从redo log恢复

并发事务带来的问题

  • 更新丢失(Lost Update):当两个或多个事务选择同一行,然后基于最初选定的值更新该行时,由于每个事务都不知道其他事务的存在,就会发生丢失更新问题 --最后的更新覆盖了由其他事务所做的更新。例如,两个编辑人员制作了同一 文档的电子副本。每个编辑人员独立地更改其副本,然后保存更改后的副本,这样就覆盖了原始文档。 最后保存其更改副本的编辑人员覆盖另一个编辑人员所做的更改。如果在一个编辑人员完成并提交事务之前,另一个编辑人员不能访问同 一文件,则可避免此问题。
  • 脏读(Dirty Reads):一个事务正在对一条记录做修改,在这个事务完成并提交前, 这条记录的数据就处于不一致状态; 这时, 另一个事务也来读取同一条记录,如果不加控制,第二个事务读取了这些“脏”数据,并据此做进一步的处理,就会产生未提交的数据依赖关系。这种现象被形象地叫做"脏读"。
  • 不可重复读(Non-Repeatable Reads):一个事务在读取某些数据后的某个时间,再次读取以前读过的数据,却发现其读出的数据已经发生了改变、或某些记录已经被删除了!这种现象就叫做“不可重复读” 。
  • 幻读 (Phantom Reads): 一个事务按相同的查询条件重新读取以前检索过的数据,却发现其他事务插入了满足其查询条件的新数据,这种现象就称为“幻读” 。

幻读和不可重复读的区别:

  • 不可重复读的重点是修改:在同一事务中,同样的条件,第一次读的数据和第二次读的数据不一样。(因为中间有其他事务提交了修改)
  • 幻读的重点在于新增或者删除:在同一事务中,同样的条件,,第一次和第二次读出来的记录数不一样。(因为中间有其他事务提交了插入/删除)
上次编辑于:
贡献者: MJX