ACID

原子性(Atomiclty)

操作不可分割,多个操作要么成功,要么全部失败,不存在中间状态。

隔离性(Isolation)

多个操作之间不能相互影响

一致性(Consistency)

保证数据是统一的,不会多,也不会少(不满足原子性和隔离性,可能就破坏了一致性,即原子性和隔离性都是为了保证一致性)

持久性(Durability)

数据可以持久化保存

查看事务状态
show variables like 'autocommit';

关闭事务
set autocommit = OFF;

开启事务
begin;

提交事务
commit;

设置保存点
savepoint 保存点名称;

删除保存点
release 保存点名称

回滚
rollback to 保存点名称

隔离级别

查看隔离级别
SELECT @@transaction_isolation;

修改隔离级别(基于 session)
set session transaction isolation level read uncommitted;

脏读

一个事务读到了其他事务没有提交的数据,就是脏读

幻读

如果一个事务根据某些条件查询到一些记录,之后另一个事务又向表中插入了新的数据,原先的事务再次根据原来的条件查询时,能把另一个事务插入的数据也读出来,这就是幻读

读未提交 READ UNCOMMITTED

一个事务可以读到其他事务没有提交的数据,会出现脏读

读已提交 READ COMMITTED

一个事务只能读到其他事务已经提交的数据,并且其他事务对该数据每一个修改后的提交,该事务都可以读到最新的数据,会出现不可重复读,幻读

可重复读 REPEATABLE READ

一个事务第一次读过某条记录后,即使其他事务修改了该记录的值并进行了提交,该事务之后再读取该条记录,读到的仍是第一次读取的值。而不是每次都看到的不同数据,这就是可重复度,解决了脏读,不可以重复读,但是还可能会出现幻读**(MySQL 解决了幻读问题)**

串行化(SERIALIZABLE)

以上 3 种隔离级别都允许对同一条记录进行读-读,读-写,写-读的并发操作,如果我们不允许读-写,写-读的并发操作(当写发生的时候,阻塞其他操作),这就是 SERIALIZABLE 隔离级别,这种隔离级别因为对同一条记录的操作都是串行的,所以不会出现脏读,幻读,不可重复读的现象

总结

  • READ UNCOMMITTED 隔离级别下:可能发生“脏读”,“不可重复读”,“幻读”问题
  • READ COMMITTED 隔离级别下:可能发生“不可重复读”,“幻读”问题
  • REPEATABLE READ 隔离级别下:可能发生“幻读”问题
  • SERIALIZABLE 隔离级别下:各种问题都不会发生

注意:这四种隔离级别是 SQL 的标准定义,不同的数据库会有不同的实现。特别需要注意的是 MySQL 在 REPEATABLE READ 隔离级别下解决了幻读的问题
**

MVCC

MVCC 可控制读写分离,并发高

版本链

对于 InnoDB 存储引擎来说,它的聚簇索引记录表中都包含两个必要的隐藏列

  • transaction_id:每次对某条记录进行改动时,都会把对应的事务 ID 给 transaction_id
  • roll_pointer:每次对某条记录进行改动时,这个隐藏列会存一个指针。可以通过这个指针找到之前记录修改的值
隐藏列 是否必须 占用空间 作用
row_id 6 字节 行 ID,唯一标识一条记录。如果存在主键,则无需该列
transaction_id 6 字节 事务 ID
roll_pointer 7 字节 回滚指针

ReadView

对于使用 Read Uncommitted 隔离级别来说,直接读取最新的数据即可
对于使用 Serializable 隔离级别来说使用加锁的方式来控制就好
对于 Read Committed 和 Repeatable Read 来说就需要用到版本链来控制了,核心问题是:判断版本链中的哪一个版本对当前事务可见

  • Read Committed:每次读取前都生成一个最难的 ReadView
  • Repeatable Read:在第一次读取时生成 ReadView,之后不在生成

MVCC 总结

MVCC(Mulit-Version Concurrency Control)多版本并发控制,其实就是只针对 Read Committed 和 Repeatable Read 两种隔离方式,他们之间唯一不同的就是 Read Committed 在每次读取前都会生成 ReadView,而 Repeatable Read 只会在第一次普通的查询的时候生成 ReadView,之后就读取第一次生成的即可