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,之后就读取第一次生成的即可