MySQL事务及隔离级概述

191 阅读7分钟

事务

事务是一组原子性的 SQL查询语句,也可以被看做一个工作单元,不可再分割。如果数据库引擎能够成功地对数据库应用所有的查询语句,它就会执行所有查询。但是,如果任何一条查询语句因为崩溃或其他原因而无法执行,那么所有的语句就都不会执行。也就是说,事务内的语句要么全部执行,要么一句也不执行。

案例

银行转账是一个经典案例,可以用来解释事务应用的必要性。

假设:银行数据库有两张表,checking 表和 saving 表。

现在要从小明的支票账户 (Cheking Accoumn) 里转账200元到他的存款账户 (Savings Account)。那么,至少需要3步来完成这个操作:

  • 检查支票存款账户的余额是否大于200元

  • 从支票存款账户余额中减去200元

  • 在存款账户余额中增加200元

所有的操作被打包在一个事务里,如果某一步失败,就回滚所有已完成步骤。

可以用 START TRANSACTION 语句开始一个事务,用 COMMIT 语句提交整个事务,永久地修改数据,或者用 ROLLBACK 语句回滚整个事务,取消已做的修改。

事务SQL样本如下:

START TRANSACTION;
SELECT balance FROM checking WHERE customer_id = 10233276;
UPDATE checking SET balance = balansce - 200.00 WHERE customer_id = 10233276;
UPDATE saving SET balance = balance + 200.00 WHERE customer_id = 10233276;
COMMIT;

单纯的事务概念不是全部的目的。试想一下,如果数据库服务器在执行第4行语句时突然崩溃,会发生什么事? 没人知道,但用户可能因此损失200元。再假如,在执行到第3和第4行语句之间时,另一个进程同时运行,它的目的是要先清空支票存款账户的余额,那么结果可能是,银行根本不知道有这个逻辑先发事件,白白给用户增加了200元存款。

ACID

除非系统通过ACID测试,否则空谈事务概念是不够的。ACID代表了原子性(Atomicity)、一致性(Consistency)、隔离性(solation) 和特久性(Durability)。 这些概念与事务的处理标准密切关联,一个有效的事务处理系统必须满足相关标准。

原子性(Atomicaty)

一个事务必须被视为一个单独的、内部“不可分”的工作单元,以确保整个事务要么全部执行,要么全部回滚。当一个事务具有原子性时,该事务绝对不会被部分执行,要么完全执行,要么根本不执行。

一致性(Consistency)

数据库总是从一种一致性状态转换到另一种一致性状态。 在上述例子中,一致性确保了即使数据库系统在执行第3、4行语句时崩溃了,支票存款账户也不会损失200元。因为最终事务根本没有被提交,任何事务处理过程中所做的数据改变,也不会影响到数据库的内容。

隔离性(lsolation)

某个事务的结果只有在完成之后才对其他事务可见。在上述例子中,当数据库执行完第3行语句,还未执行第4行语句时,如果此时银行汇总程序也同时运行,它将仍视转账的200元仍在支票存款账户内。当后文讨论隔离级时,你就会理解为什么我们所说的通常是“不可见”(Invisible) 的。

持久性(Durability)

一旦一个事务提交,事务所做的数据改变将是永久的。这意味若数据改变已被记录,即使系统崩溃,数据也不会因此丢失。持久性是个有点模糊的概念,因为实际上持久性也分很多级别。有些持久性策略提供一种强壮的安全保证,另一些则未必。

ACID 事务确保了银行不会弄丢你的钱,而这种特性在应用逻辑设计中是很难实现的,甚至不可能实现。一个 ACID 兼容的数据库服务器,要为事务处理做大量的复杂工作,确保 ACID 特性的实现,而这也许是用户所察觉不到的。

正像锁粒度的增加会导致锁开销的增长一样, 这种事务处理中的额外安全措施,也将导致数据库服务器要完成更多的额外工作。通常,一个支持 ACID 特性的数据库,相对于不支持这种特性的数据库,需要更强的 CPU 处理能力、更大的内存和更多的磁盘空间。

用户可以根据自身系统是否需要事务处理,选择相应的存储引擎。如果对于某些类型的数据查询,用户不需要真正的事务处理,他可以选择一个非事务处理型的存储引擎来实现查询,以获得更高的处理性能。用户也可以使用 LOCK TABLES 语句,为应用提供某种级别的数据保护,而这些选择完全由用户自主决定。

隔离级

隔离的问题其实比想象的要复杂。SQL 标谁定义了4类隔离级,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的井发处理,并拥有更低的系统开销。

提示:每种存储引擎实现的隔离级略有不同,如果用过其他数据库产品,你可能发现它们未必完全满足自己的期望(因此,本文不会讨论更多的详尽细节)。读者可以根很据选择的存储引擎, 进一步阅读相关手册资料。 下面简单介绍四种隔离级:

READ INCOMITTED (读取未提交内容)

在READ UNOMITTED隔离级,所有事务都可以“看到" 木提交事务的执行结果。在这种级别上,可能会产生很多问题,除非用户真的知道自己在做什么,井有很好的理由选择这样做。本隔离级很少用于实际应用,因为它的性能也不比其他级别好多少,而别的级别还有其他更多的优点。读取未提交数据,也被称之为 “脏读”(Dity Read)

READ COMITTED (读取提交内容)

大多数数据库系统的默认陽离级是 READ COMMITTED (但这不是MySQL默认的!)。它满足了限离的早先简单定义:一个事务在开始时,只能“看见”已经提交事务历做的改变,一个事务从开始到提交前,所做的任何数据改变都是不可见的,除非已经提交。这种隔离级别也支持所谓的 “不可重复读”(NonrepeatableRead)。 这意味着用户运行同一语句两次,看到的结果是不同的。

REPEATABLE RSAD (可重读)

REPEATABLE READ 隔离级解决了 READ UNCOMNITTED 隔离级导致的问题。它确保同一事务的多个实例在并发读取数据时,会“看到同样的”数据行。不过理论上,这会导致另一个棘手问题:幻读(Phantom Read)。简单来说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插人了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” (Phantom) 行。ImoDB 和 Falco 存律引擎通过多版本并发控制 (Mutiersion Comcurency Conira) 机制解决了幻读问题。

REPEATABLE READ 是MySQL的默认事务隔离级。InnoDB 和 Falcon 存储引擎都遵循这种设置。可以通过 select @@global.tx_isolation;来查看当前隔离级别。可参考其他资料,了解如何改变这种设置。其他一些存储引擎也以此为默认设置,不过具体设置还要看相关引擎的具体规定。

SERIALIZABLE (可串行化)

SERIALIZABLE 是最高级别的隔离级,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,SERIALIZABLE 是在每个读的数据行上加锁。在这个级别,可能导致大量的超时 (Timeout) 现象和锁竞争 (Lock Contention) 现象。很少有用户选择这种隔离级。但如果用你的系统为了数据的稳定性,需要强制减少并发的话,也可以选择这种隔离级。

四类隔离级别存在的问题

[END]

参考《高性能MySQL》.如需PDF电子版书籍,关注【公众号@编程大道】获取。