视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
MySQL中的四种事务隔离级别实例分析
2020-11-09 08:44:52 责编:小采
文档

在数据库操作中,为了有效保证并发读取数据的正确性,提出的事务隔离级别。数据库事务的隔离级别有4个,SQL标准定义了4种隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。下面这篇文章通过实例详细的给大家分析了关于MySQL中的四种事务隔离级别的相关资料,需要的朋友可以参考下。

前言

下面话不多说了,来一起看看详细的介绍吧。

数据库事务有四种隔离级别:

  • 未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据。

  • 提交读(Read Committed):只能读取到已经提交的数据,Oracle等多数数据库默认都是该级别。

  • 可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻读。

  • 串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞。

  • 上面这样的教科书式定义第一次接触事务隔离概念的朋友看了可能会一脸懵逼,下面我们就通过具体的实例来解释四个隔离级别。

    首先我们创建一个user表:

    CREATE TABLE user (
     `id` int(11) NOT NULL AUTO_INCREMENT,
     `name` varchar(255) NOT NULL,
     PRIMARY KEY (`id`),
     UNIQUE `uniq_name` USING BTREE (name)
    ) ENGINE=`InnoDB` AUTO_INCREMENT=10 DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

    读未提交隔离级别

    我们先将事务的隔离级别设置为read committed:

    mysql> set session transaction isolation level read uncommitted;
    Query OK, 0 rows affected (0.00 sec)
    mysql> select @@session.tx_isolation;
    +------------------------+
    | @@session.tx_isolation |
    +------------------------+
    | READ-UNCOMMITTED |
    +------------------------+
    1 row in set (0.00 sec)

    在下面我们开了两个终端分别用来模拟事务一和事务二,p.s: 操作一和操作二的意思是按照时间顺序来执行的。

    事务1

    mysql> start transaction; # 操作1
    Query OK, 0 rows affected (0.00 sec)
    mysql> insert into user(name) values('ziwenxie'); # 操作3
    Query OK, 1 row affected (0.05 sec)

    事务2

    mysql> start transaction; # 操作2
    Query OK, 0 rows affected (0.00 sec)
    mysql> select * from user; # 操作4
    +----+----------+
    | id | name |
    +----+----------+
    | 10 | ziwenxie |
    +----+----------+
    1 row in set (0.00 sec)

    从上面的执行结果可以很清晰的看出来,在read uncommited级别下面我们在事务一中可能会读取到事务二中没有commit的数据,这就是脏读。

    读提交隔离级别

    通过设置隔离级别为committed可以解决上面的脏读问题。

    mysql> set session transaction isolation level read committed;

    事务一

    mysql> start transaction; # 操作一
    Query OK, 0 rows affected (0.00 sec)
    mysql> select * from user; # 操作三
    +----+----------+
    | id | name |
    +----+----------+
    | 10 | ziwenxie |
    +----+----------+
    1 row in set (0.00 sec)
    mysql> select * from user; # 操作五,操作四的修改并没有影响到事务一
    +----+----------+
    | id | name |
    +----+----------+
    | 10 | ziwenxie |
    +----+----------+
    1 row in set (0.00 sec)
    mysql> select * from user; # 操作七
    +----+------+
    | id | name |
    +----+------+
    | 10 | lisi |
    +----+------+
    1 row in set (0.00 sec)
    mysql> commit; # 操作八
    Query OK, 0 rows affected (0.00 sec)

    事务二

    mysql> start transaction; # 操作二
    Query OK, 0 rows affected (0.00 sec)
    mysql> update user set name='lisi' where id=10; # 操作四
    Query OK, 1 row affected (0.06 sec)
    Rows matched: 1 Changed: 1 Warnings: 0
    mysql> commit; # 操作六
    Query OK, 0 rows affected (0.08 sec)

    虽然脏读的问题解决了,但是注意在事务一的操作七中,事务二在操作六commit后会造成事务一在同一个transaction中两次读取到的数据不同,这就是不可重复读问题,使用第三个事务隔离级别repeatable read可以解决这个问题。

    可重复读隔离级别

    MySQL的Innodb存储引擎默认的事务隔离级别就是可重复读隔离级别,所以我们不用进行多余的设置。

    事务一

    mysql> start tansactoin; # 操作一
    mysql> select * from user; # 操作五
    +----+----------+
    | id | name |
    +----+----------+
    | 10 | ziwenxie |
    +----+----------+
    1 row in set (0.00 sec)
    mysql> commit; # 操作六
    Query OK, 0 rows affected (0.00 sec)
    mysql> select * from user; # 操作七
    +----+------+
    | id | name |
    +----+------+
    | 10 | lisi |
    +----+------+
    1 row in set (0.00 sec)

    事务二

    mysql> start tansactoin; # 操作二
    mysql> update user set name='lisi' where id=10; # 操作三
    Query OK, 1 row affected (0.00 sec)
    Rows matched: 1 Changed: 1 Warnings: 0
    mysql> commit; # 操作四

    在事务一的操作五中我们并没有读取到事务二在操作三中的update,只有在commit之后才能读到更新后的数据。

    Innodb解决了幻读么

    实际上RR级别是可能产生幻读,InnoDB引擎官方称中利用MVCC多版本并发控制解决了这个问题,下面我们验证一下Innodb真的解决了幻读了么?

    为了方便展示,我修改了一下上面的user表:

    mysql> alter table user add salary int(11);
    Query OK, 0 rows affected (0.51 sec)
    Records: 0 Duplicates: 0 Warnings: 0
    mysql> delete from user;
    Query OK, 1 rows affected (0.07 sec)
    mysql> insert into user(name, salary) value('ziwenxie', 88888888);
    Query OK, 1 row affected (0.07 sec)
    mysql> select * from user;
    +----+----------+----------+
    | id | name | salary |
    +----+----------+----------+
    | 10 | ziwenxie | 88888888 |
    +----+----------+----------+
    1 row in set (0.00 sec)

    事务一

    mysql> start transaction; # 操作一
    Query OK, 0 rows affected (0.00 sec)
    mysql> update user set salary='4444'; # 操作六,竟然影响了两行,不是说解决了幻读么?
    Query OK, 2 rows affected (0.00 sec)
    Rows matched: 2 Changed: 2 Warnings: 0
    mysql> select * from user; # 操作七, Innodb并没有完全解决幻读
    +----+----------+--------+
    | id | name | salary |
    +----+----------+--------+
    | 10 | ziwenxie | 4444 |
    | 11 | zhangsan | 4444 |
    +----+----------+--------+
    2 rows in set (0.00 sec)
    mysql> commit; # 操作八
    Query OK, 0 rows affected (0.04 sec)

    事务二

    mysql> start transaction; # 操作二
    Query OK, 0 rows affected (0.00 sec)
    mysql> insert into user(name, salary) value('zhangsan', '666666'); # 操作四
    Query OK, 1 row affected (0.00 sec)
    mysql> commit; # 操作五
    Query OK, 0 rows affected (0.04 sec)

    从上面的例子可以看出,Innodb并没有如官方所说解决幻读,不过上面这样的场景中也不是很常见不用过多的担心。

    串行化隔离级别

    所有事务串行执行,最高隔离级别,不会出现幻读性能会很差,实际开发中很少使用到。

    下载本文
    显示全文
    专题