mysql的事务隔离机制

https://segmentfault.com/blog/lant
1.事务的ACID特性
{
Atomicity 原子性
Consistency 一致性:数据库总是从一个一致性的状态 转换到 另一个一致性的状态;
Isolation 隔离性:通常来说, 一个事务所做的修改在最终提交以前, 对其他事务是不可见的;
Durability 持久性:这里所说的`永久`应该可以理解为 被事务修改的数据 是真正存放到了表中, 而不是存放在了诸如临时表之类的地方
}
2.事务隔离性的隔离级别
{
    最低隔离级别 READ UNCOMMITTED, 对其他事务的可见就造成了脏读问题的出现
    事务有四种隔离级别(从低到高: 
    READ UNCOMMITTED,   一般不会用,任何操作都不会加锁
    READ COMMITTED, 
    REPEATABLE READ,   解决:脏读问题 不可重复读问题
    SERIALIZABLE)
查看隔离级别
SELECT @@SESSION.tx_isolation;
}

3.问题
{
3.1脏读   READ COMMITED或以上隔离级别
{
一个事务A读取了另一个并行事务B未最终提交的写数据, 那事务A的这次读取就是脏读(因为事务A读取的是'脏数据', 是'非持久性'的数据)

解决办法:使用隔离性的 -- READ COMMITED或以上隔离级别
**`READ COMMITED`级别保证了, 只要是当前语句执行前已经提交的数据都是可见的**。注意和`REPEATABLE READ`级别的区!!!

解决了脏读问题, 只是能保证你在事务中每次读到的数据都是持久性的数据而已!!!!
}

3.2不可重复读 REPEATABLE READ
{
如果在一个事务中多次读取同一个数据, 正好在两次读取之间, 另外一个事务确实已经完成了对该数据的修改并提交, 那问题就来了: 可能会出现多次读取结果不一致的现象。

REPEATABLE READ级别保证了, 只要是当前事务执行前已经提交的数据都是可见的。注意和READ COMMITED级别的区!!!

保证同一事务中间的多次读取同一数据结果一致    
}


3.3 幻读/间隙锁      InnoDB引擎默认的RR级别已经通过MVCC自动帮我们解决
{
主要是说多次读取一个范围内的记录(包括直接查询所有记录结果或者做聚合统计), 发现结果不一致(标准档案一般指记录增多, 记录的减少应该也算是幻读)。

MySQL的InnoDB引擎默认的RR级别已经通过MVCC自动帮我们解决了, 所以该级别下, 你也模拟不出幻读的场景; 
退回到 RC 隔离级别的话, 你又容易把幻读和不可重复读搞混淆
}

3.4 一致性非阻塞读
{
数据库状态的快照适用于事务中的SELECT语句, 而不一定适用于所有DML语句。 
如果您插入或修改某些行, 然后提交该事务, 则从另一个并发REPEATABLE READ事务发出的DELETE或UPDATE语句就可能会影响那些刚刚提交的行, 即使该事务无法查询它们。 
如果事务更新或删除由不同事务提交的行, 则这些更改对当前事务变得可见。    
}

3.5 不少资料将MVCC并发控制中的读操作可以分成两类: 快照读 (snapshot read) 与 当前读 (current read)。
{
快照读 
    读取专门的快照 (对于RC,快照(ReadView)会在每个语句中创建。对于RR,快照是在事务启动时创建的)
    ```
    简单的select操作即可(不需要加锁,如: select ... lock in share mode, select ... for update)
    ```
    针对的也是select操作
当前读
    读取最新版本的记录, 没有快照。 在InnoDB中,当前读取根本不会创建任何快照。    
    ```
    select ... lock in share mode
    select ... for update
    ```
    针对如下操作, 会让如下操作阻塞:    
    ```
    insert
    update
    delete
    ```
    - 在RR级别下, 
    快照读是通过MVVC(多版本控制)和undo log来实现的, 
    当前读是通过手动加record lock(记录锁)和gap lock(间隙锁)来实现的。
    所以从上面的显示来看,如果需要实时显示数据,还是需要通过加锁来实现。这个时候会使用next-key技术来实现。
        
    }

3.6 更新丢失
{
第一类丢失更新: A事务撤销时, 把已经提交的B事务的更新数据覆盖了。
    这类更新丢失问题是不会出现的, 因为InnoDB存储引擎的隔离级别都使用了排他锁, 即使是 MVCC也不是纯MVCC, 也用到了排他锁! 这样的话事务A在未完成的时候, 其他事务是无法对事务A涉及到的数据做修改并提交的。

第二类丢失更新: A事务覆盖B事务已经提交的数据,造成B事务所做操作丢失。
    此类更新丢失问题, 无法依靠前三种隔离级别来解决, 只能用最高隔离级别 Serializable 或者手动使用乐观锁, 悲观锁来解决。    (最高隔离级别Serializable在实际应用场景中并不被采用)
}

3.n 使用隔离性的最高隔离级别SERIALIZABLE也可以解决幻读, 但该隔离级别在实际中很少使用!
    
}

4.MVCC多版本并发控制
{
MySQL的大多数事务型存储引擎实现的其实都不是简单的行级锁。基于提升并发性能的考虑, 它们一般都同时实现了多版本并发控制(MVCC)。不仅是MySQL, 包括Oracle,PostgreSQL等其他数据库系统也都实现了MVCC, 但各自的实现机制不尽相同, 因为MVCC没有一个统一的实现标准。

MVCC的实现方式有多种, 典型的有乐观(optimistic)并发控制 和 悲观(pessimistic)并发控制。

MVCC只在 READ COMMITTED 和 REPEATABLE READ 两个隔离级别下工作。
其他两个隔离级别够和MVCC不兼容, 因为 READ UNCOMMITTED 总是读取最新的数据行, 而不是符合当前事务版本的数据行。而 SERIALIZABLE 则会对所有读取的行都加锁。

MVCC是被Mysql中 事务型存储引擎InnoDB 所支持的;
应对高并发事务, MVCC比单纯的加锁更高效;
MVCC只在 READ COMMITTED 和 REPEATABLE READ 两个隔离级别下工作;
MVCC可以使用 乐观(optimistic)锁 和 悲观(pessimistic)锁来实现;
各数据库中MVCC实现并不统一
InnoDB的MVCC是通过在每行记录后面保存两个隐藏的列来实现的

    
当前读和快照读
MySQL的InnoDB存储引擎默认事务隔离级别是RR(可重复读), 是通过 "行排他锁+MVCC" 一起实现的, 不仅可以保证可重复读, 还可以部分防止幻读, 而非完全防止;

3.快照读(snapshot read)
简单的select操作(当然不包括 select ... lock in share mode, select ... for update)

4.当前读(current read)
select ... lock in share mode
select ... for update
insert
update
delete
在RR级别下,快照读是通过MVVC(多版本控制)和undo log来实现的,当前读是通过加record lock(记录锁)和gap lock(间隙锁)来实现的。
innodb在快照读的情况下并没有真正的避免幻读, 但是在当前读的情况下避免了不可重复读和幻读!!!

}
也正是因为InnoDB使用的MVCC中结合了排他锁, 不是纯的MVCC, 所以第一类更新丢失是不会出现了, 一般说更新丢失都是指第二类丢失更新。


5.锁和事务隔离级别
{
死锁, 锁冲突, 行锁,表锁, 读锁, 写锁, 乐观锁, 悲观锁

脏读,不可重复读,幻读可以直接使用事务的隔离级别避免
事务隔离级别的核心就是锁, 各隔离级别使用了不同的加锁策略,在分析之前的几个高并发事务问题的时候, 隔离级别(锁)自然是不能作为前置知识点的, 而是最终问题的解决方案!    

InnoDB的做法是: 读不影响写,写不影响读。
    读不影响写: 当数据正在执行读操作时,其他事务的写操作不会因此去等待当前事务行上S锁的释放,而是会去读取行的一个快照数据。
    写不影响读:当数据正在执行写操作时,其他事务的读操作不会因此去等待当前事务行上X锁的释放,而是会去读取行的一个快照数据。
}

猜你喜欢

转载自blog.csdn.net/Edu_enth/article/details/93849118