MySQL事务机制!

ACID

原子性(atomicity):

  • 指事务是一个不可分割的工作单位,要么全部提交,要么全部失败回滚。

一致性(consistency):

  • 指事务执行前后,数据从一个合法性状态变换到另外一个合法性状态。
    • 这种状态是语义上的而不是语法上的,跟具体的业务有关。

隔离型(isolation):

  • 指一个事务的执行不能被其他事务干扰。
  • 即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。

持久性(durability):

  • 指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,其他操作和数据库故障不应该对其有任何影响。

数据并发产生的问题

丢失更新(Lost Update)

  • 两个事务 Session A、Session B,如果事务Session A 修改了另一个未提交事务Session B 修改过的数据,意味着发生了丢失更新。

脏读( Dirty Read ):

  • 两个事务 Session A、Session B,Session A 读取了已经被 Session B 更新但还没有被提交的字段。
    • 之后如果 Session B 回滚,那么Session A 读取的内容就是临时且无效的。

不可重复读( Non-Repeatable Read ):

  • 两个事务Session A、Session B,Session A 读取了一个字段,然后 Session B 更新了该字段。
    • 之后 Session A 再次读取同一个字段,该字段的值发生了变化。

幻读( Phantom ):

  • 两个事务Session A、Session B, Session A 从一个表中读取了一个字段, 然后 Session B 在该表中插入了一些新的行。
    • 如果 Session A 再次读取同一个表, 就会多出几行。

事务隔离级别

READ UNCOMMITTED(读未提交):

  • 所有事务都可以看到其他未提交事务的执行结果,不能避免脏读、不可重复读、幻读。

READ COMMITTED(读已提交):

  • 一个事务只能看见已经提交事务所做的改变。
  • 可以避免脏读,但不可重复读、幻读问题仍然存在。

REPEATABLE READ(重复读):

  • 事务A在读到一条数据之后,此时事务B对该数据进行了修改并提交,事务A再读该数据,读到的还是原来的内容。
  • 可以避免脏读、不可重复读,但幻读问题仍然存在,这是MySQL的默认隔离级别。

SERIALIZABLE(可串行化):

  • 确保事务可以从一个表中读取相同的行。
隔离级别 丢失更新 脏读 不可重复读 幻读
Read uncommitted ×
Read committed × ×
Repeatable read(默认) × × ×
Serializable × × × ×

MySQL默认隔离级别为什么是可重复读

MySQL在5.0这个版本以前,binlog只支持STATEMENT这种格式。

  • 而这种格式在读已提交(Read Commited)这个隔离级别下主从复制是有bug的。

因此MSQL将可重复读(Repeatable Read)作为默认的隔离级别。

STATEMENT主从复制的Bug?

  • 在master上执行的顺序为先删后插,而此时binlog为STATEMENT格式,它记录的顺序为先插后删。

  • 从(slave)同步的是binglog,如果从机执行的顺序和主机不一致,就会出现主从不一致。

隔离级别设为可重复读(Repeatable Read),在该隔离级别下引入间隙锁。

  • Session 1执行delete语句时,会锁住间隙。

  • 那么,Ssession 2执行插入语句就会阻塞住。

保证主从复制不出问题。

为什么大家将隔离级别设为读已提交(Read Commited)

在RR隔离级别下,存在间隙锁,导致出现死锁的几率比RC大的多。

在RR隔离级别下,条件列未命中索引会锁表,而在RC隔离级别下,只锁行。

在RC隔离级别下,半一致性读(semi-consistent)特性增加了update操作的并发性。

半一致性读:

一个update语句,如果读到一行已经加锁的记录,此时InnoDB返回记录最近提交的版本。

  • 由MySQL上层判断此版本是否满足update的where条件。

  • 若满足(需要更新),则MySQL会重新发起一次读操作,此时会读取行的最新版本(并加锁)。

有两个Session,Session1和Session2:

Session1执行:

1
update test set color = 'blue' where color = 'red'; 

先不Commit事务。

与此同时Ssession2执行:

1
update test set color = 'blue' where color = 'white';

session 2尝试加锁的时候,发现行上已经存在锁。

InnoDB会开启semi-consistent read,返回最新的committed版本(1,red),(2,white),(5,red),(7,white)。

MySQL会重新发起一次读操作,此时会读取行的最新版本(并加锁)。

而在RR隔离级别下,Session2只能等待。

互联网项目请用:读已提交(Read Commited)这个隔离级别。

事务实现原理

原子性:使用undo log(回滚日志),undo log记录了回滚需要的信息。

  • 当事务执行失败或调用了rollback,导致事务需要回滚,便可以利用undo log中的信息将数据回滚到修改之前的样子。

隔离性: 使用悲观锁乐观锁对事务处理。

持久性:

  • 使用redo log(重写日志)。

一致性:通过原子性、隔离性、持久性来保证一致性。

不同隔离级别,都使用了什么锁?

对于任何隔离级别,表级别的表锁、元数据锁、意向锁都是会使用的,但对于行级别的锁则会有些许差别。

在 读未提交 和 读已提交 隔离级别下,都只会使用记录锁,不会用间隙锁,当然也不会有 Next-Key 锁了。

可重复读 隔离级别,会使用记录锁、间隙锁和 Next-Key锁。