MySQL事务隔离级别及实现原理

一. MySQL数据库的事务隔离级别

  • 说起事务隔离级别之前,先了解一下几种数据的一致性问题:1. 脏读:a事务读取了b事务还未提交的更新数据,然后b事务回滚,导致数据不一致。2. 重复读:a事务多次读取同一条数据,b事务在a事务多次读取中间修改了数据,倒是a多次读取的数据不一致,出现不可重复读问题。3. a事务多次读取同一条数据,b事务在a事务多次读取中间插入一条了数据,a可能会出现突然读取到新的数据,也可能没有读到,然后插入的时候出现冲突,出现幻读。
  • MySQL数据库一共提供了四种数据中间的隔离级别,其中RU因为并发安全性太低一般都不会使用。而SER又因为不适用MVCC而退化为完全使用锁控制并发,导致性能非常差,一般也不会使用。
事务隔离级别 脏读 不可重复读 幻读 备注
read-uncommitted(RU)
read-committed(RC)
read-repeatable(RR)
serializable(SER)

二. 事务隔离级别的实现原理

  • MySQL事务的隔离级别依赖于MVCC的实现,因此在讲事务隔离原理之前,会先说一下我对MVCC的理解。MVCC((Multi-Version Concurrency Control)通过将数据存储为多个版本,根据不同的数据一致性要求,读取不同的版本。同时将数据库的读写锁分离,可以做到读不影响写,写不影响读,大大提高了数据库的并发性能。
  • MySQl是一个按行存储的数据库,同时每一行数据在经过多次修改之后会存储成多个版本,如下图:


    数据库行多个版本存储.png

    数据库中的数据会存在多个版本,Hbase也用这种方式存储Cell中的数据。同时版本和版本之间形成一个单向链表,每一行还存储了这个版本对应的事务id。这样做有两个好处:1. 数据可以根据数据一致性需求选择要展示的数据版本。2.在事务需要回滚时,数据库可以随意回滚到之前版本的数据。

  • 每当我们创建一个事务,MySQL都会把这个事务存储下来,如下图:


    事务存储数据结构.png

    其中我们比较关系的是read_view,我把它理解为查询视图。read_view里保存了当前数据库所有活跃的事务,并把这些事务按照提交顺序组织成已个单向链表,链表头为up_trx_id,链表尾是low_trx_id。因此,我们如果相判断某一行数据的某一个版本对当前事务是否可见,只需要将此行所在的事务id跟read_view中的事务id进行比对,根据数据一致性要求,选在是否展示。

  • 了解了数据库数据存储结构和事务存储结构之后,我们就可以聊聊四种隔离级别的实现方式:
  1. Read-Uncommited(RU)
    RU只添加了写写锁,保证修改数据和插入不会出现数据安全问题,会出现脏读,此隔离级别基本不会使用。

  2. Read-Conmmited(RC)
    RC,顾名思义,如果同时存在a和b两个事务。那么a中是没办法读到b事务未提交的修改数据的。RC修改和写入数据也是通过写写锁保证的。读操作通过MVCC实现。a事务每次在读取数据时,都会更新read_view,read_view中又存储了当前已经开启但是还未提交的事务id,因此要判断某一行数据是否可见,只需要判断数据版本对应的是事务tx-id是否在read_view中,或者tx-id>low_trx_id。RC可以避免出现脏读。

  3. Repeatable-Read(RR)
    RC数据库隔离级别下,事务中的每次查询都会更新read_view获得最新的读取视图,解决了脏读问题。但是额外带来的问题就是每次读取视图的变化,也可能导致同一个事务内对同一条数据的读取结果不一致。因此,在RR隔离级别下,每次开启事务后,read_view就不在变化,这样可以保证同一个事务内,多次读取的数据都保持一致。但是RR隔离级别仍然存在幻读现象,因为多次读的结果虽然一致,但是并不代表数据库实际的数据存储没发生变化。
    MVCC并发控制中,读操作主要分为两类:1. 快照读。2. 当前读。快照读,读取的是数据的历史版本,因为版本有多份,可以提高并发量。当前读,读取的是数据的最新版本,并且,当前读返回的记录,都会加上锁,保证其他事务不会再并发修改这条记录。
    以Inodb为例,哪些操作是快照读,哪些操作是当前读呢?
    快照读:
    select * from table where ?;
    当前读:
    select * from table where ? lock in share mode;
    select * from table where ? for update;
    insert into table values (…);
    update table set ? where ?;
    delete from table where ?;
    因此,在RC和RR数据隔离级别下,所有的update和insert操作都属于当前读,读取数据的最新版本。当然这样还不能解决幻读问题,RR隔离级别下,同时保证对读取的范围加锁,新的满足查询条件的记录不能够插入 (间隙锁),从而保证不存在幻读现象。

  4. Serializable
    Serializable隔离级别下,数据库不再使用MVCC并发控制,完全使用共享锁和排他锁保证数据一致性。Serializable隔离级别下,读写冲突,因此并发度急剧下降,在MySQL/InnoDB下不建议使用。

你可能感兴趣的:(MySQL事务隔离级别及实现原理)