MySQL事务及隔离级别概述

Java基础

浏览数:192

2019-7-28

事务的四大ACID 属性

Atomicity 原子性、Consistency 一致性、Isolation 隔离性、Durability 持久性。

原子性: 事务是最小的执行单位不可分割,强调事务的不可分割

一致性 事务执行前后,数据的完整性保持一致。

隔离性: 一个事务执行的前后不应该受到其他事务的干扰。

持久性 一旦事务执行结束,数据就保存到数据库。

事务

在事务的概念产生之前的很长一段时间内.人们都无法利用到数据库的更多先进待征。事务是一组原子性的SQL查询语句,也可以被看做一个工作单元。如果数据库引擎能够成功地对数据库应用所有的查询语句,它就会执行所有查询,但是,如果任何一条查询语句因为崩溃或其他原因而无法执行,那么所有的语句就都不会执行。也就是说,事务内的语句要么全部执行,要么一句也不执行。

银行应用是一个经典实例,可以解释事务应用的必要性。假设:银行数据库有两张表,checking 表和saving表。
现在要从Jane的支票账户(Cheking Accoumn)里转账200美元到她的存款账户 (Savings Account)。那么,需要至少完成3步操作:
1检查支票存款账户的余额是否大于200$。

2.从支票存款账户余额中减去200.

3.在存款账户余额中增加200$

所有的操作被打包在一个事务里,如果某一步失败,就回滚所有已完成步骤。
可以用START TRANSACTION 语句开始一个事务,用COOMIT语句提交整个事务,永久地修改数据,或者用ROLLBACK语句回滚整个事务,取消已做的修改。

事务SQL样本如下:

  • START TRANSACTION;
  • SELECT balance FROM checking WHERE customer_id = 10233276;
  • UPDATE checking SET balance=balanse – 200.00 WHERE customer_id = 10233276;
  • UPDATE savinge SET balance = balaice + 200.00 WHERE customer_id = 10233276;
  • COMMIT;

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

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

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

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

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

持久性(Durability)
一旦一个事务提交,事务所做的数据改变将是永久的。这意味若数据改变已被记录,即使系统崩溃,数据也不会因此丢失。持久性是个有点模糊的概念,因为实际上持久性也分很多级别。有些持久性策略提供一种强壮的安全保证,另一些则未必。 另外,也没有什么东西是100%永远持久的。在本章的后续章节,将会讨论MySQL中持久性的真正含义,特别是在第283页”InoDBIO调优”一节。

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

正像锁粒度的增加会导致锁开销的增长一样, 这种事务处理中的额外安全措施,也导致数据库服务器要完成更多的额外工作。通常,一个支持ACID特性的数据库,相对于不支持这种特性的数据库,需要更强的CPU处理能力、更大的内存和更多的磁盘空间。正如本章不断重复的,这正是选用MySQL存储引擎架构的有利之处。用户可以根据应用是否需要事务处理,选择相应的存储引擎。如果对于某些类型的数据查询,用户不需要真正的事务处理,他可以选择一个非事务处理型的存储引擎来实现查询,以获得更高的处理性能。用户也可以使用LOCK TABLES 语句,为应用提供某种级别的数据保护,而这些选择完全由用户自主决定。——《高性能MySQL》

MySQL的事务隔离级别

有四种:未提交读、已提交读、可重复读和串行化。

未提交读: 事务A可以读取到事务B还未提交的数据。比如事务B将小明的账户余额从100改到110,事务B还没提交,在这个隔离级别下事务A就读取到的数据就是110。如果说事务B回滚了,那数据库里小明的账户余额又变成100了,可是事务A返回的数据却是110。这就造成了脏读

已提交读: 事务A读取到数据是事务B提交后的数据,即一个事务提交后,其变更才会被另一个事务读取到。比如事务B将小明的账户余额从100改到110,事务B还没提交的时候,事务A读取到的数据仍然是100。因为在这个隔离级别下一个事务只能读取到另一个事务修改后且提交事务后的数据。但是这种情况会造成一个问题就是,事务A在事务B隐式提交数据后读取到的数据是110,这是没问题的,但是在事务A还没提交前,事务B又将小明的账户余额改为120并隐式提交,然后事务A再来读取的时候就读到了120,所以事务A的两次读取结果不一致。这就造成了不可重复读

可重复读: 事务A只能读到事务B修改的已提交了事务的数据,但是第一次读取的数据,即使别的事务修改了这个值,这个事务再读取这条数据的时候还是和第一次获取的一样,不会随着别的事务的修改而改变。这和已提交读的区别就在于,它重复读取的值是不变的。所以叫可重复读,很贴切的名字。拿刚才的例子就是,事务A在自己的事务里多次读取小明的账户余额时,如果第一次读取的是100,那么不管在这之后事务B对它进行如何的修改,对于事务A来说多次读取的结果都是100。可重复读解决了不可重复读(听起来像是废话),但是存在幻读的情况。
幻读:当事务A查询账户余额=100的客户个数时,假如返回的是1万,这时事务B将小明的账户余额由110改为100并提交事务,事务A再次查询账户余额=100的客户个数时返回的统计个数变成了1万零1。这就是幻读

幻读和不可重复读有区别。不可重复读侧重点在于读取同一条数据,数据被修改,如读小明的账户余额;幻读的侧重点在于新增或者删除 (数据条数变化),同样的条件两次查询记录数不一样。

串行化: 上面三个隔离级别对同一条记录的读和写都可以并发进行,但是串行化的隔离级别下就只能进行读-读并发。只要有一个事务操作一条记录的写,那么其他事务要读这条记录的事务都得等着。例如,事务B修改小明的账户还未提交前事务A来读,那么事务A就只能等着事务B提交之后才能读。串行化的隔离级别一半没人用,因为性能比较低,常用的是已提交读和可重复读。

隔离级

隔离的问题比想象的要复杂。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现象。作者很少看到有用户选择这种隔离级。但如果用户的应用为了数据的稳定性,需要强制减少并发的话,也可以选择这种隔离级。《高性能MySQL》

四种隔离级别存在的问题

是否会造成脏读,不可重复读或幻读,是否加锁读

事务隔离级别 脏读 不可重复读 幻读 加锁
读未提交
读已提交
可重复读
串行化

【end】

作者:为何不是梦