深入理解数据库隔离级别 isolation level

深入理解数据库隔离级别 isolation level

深入理解数据库隔离级别 isolation level

隔离级别

SQL 92 定义了四个隔离级别 (Isolation (database systems) - Wikipedia), 其隔离程度由高到低是:

* 串行化(Serializable)

* 可重复读(Repeatable reads)

* 提交读(Read committed)

* 未提交读(Read uncommitted)

首先是「隔离级别解决什么问题?」, 用通俗的语言描述就是:加一个针对数据资源的锁,从而保证数据操作过程中的一致性。

这是最简单的实现方式,过于粗暴的隔离性将大幅降低性能, 多种隔离级别就是是为了取得两者的平衡。

性能和数据一致性上的权衡。

Read Uncommitted

Read Uncommitted 这个隔离级别是最低粒度的隔离级别, 如同它的名字一般,它允许在操作过程中不上锁,从而让当前事务读取到其他事务的数据。

深入理解数据库隔离级别 isolation level

如上图所示,在 Transaction 2 查询时候,Transaction 1 未提交的数据就已经对外暴露。 如果 Transaction 1 最后 Rollback 了,那么 Transaction 读取的数据就是错误的。

「读到了其他事务修改了但是未提交的数据」即是脏读。

Read Committed

想要避免脏读,最简单的方式就是在事务更新操作上加一把写锁, 其他事务需要读取数据时候,需要等待这把写锁释放。

深入理解数据库隔离级别 isolation level

如上图所示,Transaction 1 在写操作时候,对数据 A 加了写锁, 那么 Transaction 2 想要读取 A,就必须等待这把锁释放。 这样就避免当前事务读取其他事务的未提交数据。

但是除了脏读,一致性的要求还需要「可重复读」,即 「在一个事务内,多次读取的特定数据都必须是一致的 (即便在这过程中该数据被其他事务修改)」。

深入理解数据库隔离级别 isolation level

上图就是没能保证「可重复度」,Transaction 2 第一次读取到了数据 A, 然后 Transaction 1 对数据 A 更新到 A',那么当 Tranction 2 再次读取 A 时候, 它本来期望读到 A,但是却读到了 A',这和它的预期不相符了。 解决这个问题,就需要提升隔离级别到「Repeatable Read」。

Repeatable Read

这个名字非常容易理解,即保障在一个事务内重复读取时, 始终能够读取到相同的内容。来看图:

深入理解数据库隔离级别 isolation level

如上所示,当 Transation 2 读取 A 时候,会同时加上一把 Read Lock, 这把锁会阻止 Transaction 1 将 A 更新为 A',Transaction 1 要么选择等待, 要么就选择结束。

当我们将隔离级别升到这里是,似乎已经完美无缺了。 不管是写入还是读取,我们都可以保证数据的一致性不被破坏。 但是其实还有漏洞:新增数据的一致性!

上述的三个隔离级别,都是对特定的一行数据进行加锁, 那假如将要更新的数据还没有写入数据库,如何进行加锁呢? 比如自增表的新键,或者现有数据内的空缺 Key?

深入理解数据库隔离级别 isolation level

如图所示,在上述操作中,Transaction 2 查询了一个范围 Range 之后,Transaction 1 在这个范围内插入了一条新的数据。此时 Transaction 2 再次进行范围查询时候, 会发现查询到的 Range 和上次已经不一样了,多了一个 newA。

这就是最高隔离级别才能解决的「幻影读」: 当两个完全相同的查询语句执行得到不同的结果集, 这常常在范围查询中出现。

Serializable

从字面意思看,该隔离级别需要将被操作的数据加锁加一把锁。 任何读写操作都需要先获得这把锁才能进行。如果操作中带 WHERE 条件, 还需要将 WHERE 条件相关的范围全部加锁。

深入理解数据库隔离级别 isolation level

如图所示,在 Transaction 2 操作过程中,会对 Range 进行加锁, 此时其他事务无法操作其中的数据,只能等待或者放弃。

DB 的默认隔离级别

现在我们已经理解了隔离级别,那么「SQL Server 默认使用的隔离级别是什么呢?」 根据 Customizing Transaction Isolation Level 这个文档描述,SQL Server 默认隔离级别是 READ COMMITTED

MySQL InnoDB 的默认隔离级别可以在 MySQL :: MySQL 5.7 Reference Manual :: 14.5.2.1 Transaction Isolation Levels 查询到,是 Read-Repeatable

隔离级别并没有最好之说,越高隔离级别会导致性能降低。 隔离级别的设定需要考虑业务场景。

查看事务隔离级别及设置

SELECT @@global.tx_isolation, @@session.tx_isolation, @@tx_isolation;

深入理解数据库隔离级别 isolation level

1.查看当前会话隔离级别

select @@session.tx_isolation;

2.查看系统当前隔离级别

select @@global.tx_isolation;

3.设置当前会话隔离级别

set session transaction isolatin level repeatable read;

4.设置系统当前隔离级别

set global transaction isolation level repeatable read;

@@tx_isolation -- 默认的行为(不带session和global)是为下一个(未开始)事务设置隔离级别。

相关推荐