Skip to content

Instantly share code, notes, and snippets.

@ruiqiliu
Last active August 13, 2019 08:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ruiqiliu/0b79320725b358f4f460b85812d47354 to your computer and use it in GitHub Desktop.
Save ruiqiliu/0b79320725b358f4f460b85812d47354 to your computer and use it in GitHub Desktop.
理解MySQL 隔离级别:可重复读(译)

在MySQL文档中,隔离级别是个谈论比较少的话题。 文档只提供了简要的描述,主要关注锁这个主题,并没有讨论每个隔离级别的语义。 这不只是MySQL文档的问题,SQL标准本身也是。 文档的缺失、SQL标准深入讨论的缺失,使得DBA和developers对隔离级别这个话题很陌生。在这篇博客中,我主要介绍MySQL的默认隔离级别是怎样工作的,并给出一些两人惊奇的例子。 首先来看下在标准中,隔离级别是怎么描述的:“The transaction isolation level of a SQL-transaction defines the degree to which the operations on SQL-data, or schemas in that SQL-transaction are affected by the effects of and can affect operations on SQL-data or schemas in concurrent SQL-transactions”。 简单来说,就是隔离级别定义了并发事务在修改数据时,是如何相互影响的。 MySQL使用可重复读作为默认隔离级别。在标准中,这个级别禁止脏读(non-committed data)和不可重复读(执行相同的查询,应该返回相同的结果),允许幻读(新增行可见)。但是MySQL使用一个不同的方式实现。下面看几个具体的例子。

MySQL 可重复读测试

创建两个连接,并分别叫Session Blue (sb)和 Session Res(sr),创建对应的db和表repeatable_readTALBE

No Phantom Reads... Only Phantom Writes!

首先确认隔离级别: tx_isolation,然后查询表,这样我们就为整个事务创建了repeatable_read表的一个快照。 TX_ISOLATION

现在我们到sr中插入两条数据,并commit。 SR-INSERT

然后我们看下,在sb中的查询情况: SB-SELECT

如上图所示,可重复读避免了Phantom Reads,新增的行没有查询到。这比标准描述的隔离级别更严格。但是当我们尝试修改表的时候会怎么样呢?直觉是,我们不应该修改任一行。

SB-change 惊讶:! 上图告诉我们,有一行别修改了。让我们看下表内容: SB-SELECT2 我们只看到了一行,该行就是我们刚刚修改的那一行。 这个就很反直觉,很不可思意,因为这张表从来没有单独提交过一样,我们只单独插入了两行。 我们得到了一个从来不存在的视图。 像预期的那样,当我们commit之后,我们看到了两行。 SB-commit

更多的Phantom Writes!

首先创建新的事务,并获取table所有行。这样我们就获得了table的一个快照(后者可以叫它一个版本)。

SB-SELECT3

回到sr,我们创建新的事务,并更新一行,然后提交。 “Note: Phantom reads only affect new rows, not the ones already existing.”

SR-UPDATE2

回到sb,我们查询后结果不变,看看更新时发生了什么。

SB-UPDATE2

当我们试图根据查询结果去更新id=1的行时,匹配为空。 我们可以查询到id=1时,text=‘modified’,但是当去根据text去更新时,并未匹配。 当我们根据未修改的字段去更新时,可以成功修改行。 可以看到修改后的最新结果。

What You See Is Not What You Write

为了新的测试,首先回复数据,并新建一张表。

SB-SLECT5

创建新的事务,并获取快照。

SB-SLECT6

然后到SR中,修改整张表。 B-SLECT7

回到SB,我们把repeatalbe_read表复制到copy表中。

SB-SLECT8

可以看到,使用insert into...as select 获得的内容和普通select不同。当我们commit事务,再select时也可以看到表中修改后的内容了。

SB-SLECT9

结论

通过测试,我们对MySQL的可重复读级别有如下认识:

  • 当只使用select时,可以避免幻读(phantom reads)
  • 当事务修改数据时,MySQL的行为是Repeatable-read(rows not modified are not visible) 和Read commited (modified rows are visible)的混合体
  • 当事务根据查询到的结果去修改数据时,MySQL使用的committed的数据,而不是快照。这类似于read committed, 对于修改和新增的行都适用。

MySQL实现可重复读不是很直观, 当并发事务进行数据修改,并转换数据到其他表时可能导致问题。 如果你的应用有这样的问题,可以修改你的查询语句为 select...for update,以此在数据库中增加更多的锁。

原文链接:https://blog.pythian.com/understanding-mysql-isolation-levels-repeatable-read/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment