MySQL读书笔记-事务,隔离级别,死锁

来源:互联网 发布:郑州北环淘宝城在哪里 编辑:程序博客网 时间:2024/05/01 18:33

事务

事务,就是一组原子性的SQL查询。如果数据库引擎能够成功地对数据库应用该组查询的全部语句,那么就执行该组查询;如果其中有任何一条语句因为崩溃或其他原因无法执行,那么所有的语句都不会执行。即,事务内的语句,要么全部执行成功,要么全部执行失败。

可以用START TRANSACTION语句开始一个事务,然后要么使用COMMIT提交事务将修改的数据持久保留,要么使用ROLLBACK撤销所有的修改。 事务SQL的样本如下:

START TRANSACTION;SELECT balance FROM checking WHERE customer_id = 'gerry';UPDATE checking SET balance = balance - 200.00 WHERE customer_id = 'gerry';UPDATE savings SET balance = balance + 200.00 WHERE customer_id = 'gerry';COMMIT;

ACID表示原子性,一致性,隔离性和持久性。一个运行良好的事务处理系统,必须具备这些标准特征。事务的ACID特性可以确保银行不会弄丢你的钱

  • 原子性(atomicity)
    对于一个事务来说,不可能只执行其中一部分操作,这就是事务的原子性。

  • 一致性(consistency)
    数据库总是从一个一致性的状态转换到另外一个一致性的状态。

  • 隔离性(isolation)
    通常来说(根据不同的隔离级别),一个事务所做的修改在最终提交以前,对其他事务是不可见的。

  • 持久性(durability)
    一旦事务提交,则其所做的修改就会永久保存在数据库中。

副作用
一个实现了ACID的数据库,相比没有实现ACID的数据库,通常会需要更强的CPU处理能力,更大的内存,更多的磁盘空间。用户可以根据业务是否需要事务处理,来选择合适的存储引擎。例如,对于一些不需要事务的查询类应用,选择一个非事务型的存储引擎,可以获得更高的性能。

隔离级别

在SQL标准中定义了四种隔离级别。每一种级别都规定了一个事务中所做的修改,哪些在事务内和事务间是可见的,哪些是不可见的。

较低级别的隔离通常可以执行更高的并发,系统的开销也更低。每种存储引擎实现的隔离级别不尽相同,请查阅具体相关手册。

  • READ UNCOMMITTED(未提交读)
    在此级别,事务中的修改即使没有提交,对其他事务也都是可见的,即,“赃读”。(实际应用中,很少使用)

  • READ COMMITTED(提交读,不可重复读)
    大多数数据的默认隔离级别都是此级别(但MySQL不是)。此级别满足隔离性的简单定义:一个事务开始时,只能看见已经提交的事务所做的修改(或者,一个事务从开始直到提交之前,所做的任何修改对其他事务都是不可见的)。此级别,也称为“不可重复读”,因为两次执行同样的查询,可能会得到不一样的结果。

  • REPEATABLE READ(可重复读)
    此级别保证了在同一个事务中,多次读取同样纪录的结果是一致的。此级别,是MySQL的默认事务隔离级别

但是理论上,此级别还是存在“幻读”(Phantom READ)的问题,即,当某个事务在读取某个范围内的纪录时,另外一个事务又在该范围内插入了新的纪录,当之前的事务再次读取该范围的纪录时,会产生幻行(Phantom Row)。InnoDB存储引擎通过多版本并发控制(MVCC, Multiversion Concurrency Control)解决了幻读的问题。

  • SERIALIZABLE(可串行化)
    此级别是最高的隔离级别。它通过强制事务串行执行,避免了“幻读”的问题。此级别,会在读取的每一行数据上都加锁,所以可能导致大量的超时和锁争用的问题。(实际应用中也很少使用这个隔离级别,只有在非常需要确保数据的一致性而且可以接受没有并发的情况下,才考虑使用此级别)

例子
事务A:在整个执行阶段,会将某数据项的值从1开始,加1操作,直到变成10之后进行事务提交。
事务B:查看此数据项的值,请问在不同的隔离级别下看到的值是多少?
事务C:执行和事务A类似的操作,将此数据项从10累加到20,然后进行提交。

pic

死锁

死锁是指,两个或者多个事务在同一个资源上互相占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。

注意:当多个事务试图以不同的顺序锁定资源时,就可能会产生死锁。

例子

  • 事务1
START TRANSACTION;UPDATE stock_price SET close = 45.50 WHERE stock_id = 4 and date = '2002-05-01';UPDATE stock_price SET close = 19.80 WHERE stock_id = 3 and date = '2002-05-02';COMMIT; 
  • 事务2
START TRANSACTION;UPDATE stock_price SET high = 20.12 WHERE stock_id = 3 and date = '2002-05-02';UPDATE stock_price SET high = 47.20 WHERE stock_id = 4 and date = '2002-05-01';COMMIT; 

如果凑巧,两个事务都执行了第一条UPDATE语句,更新了一行数据,同时也锁定了该行数据,接着每个事务都会尝试去执行第二条UPDATE语句,却发现该行已经被对方锁定,然后两个事务都等待对方释放锁,同时又持有对方需要的锁,则陷入死循环。(除非有外部因素介入,才可能解除死锁)

如何解决死锁的问题?

数据库实现了各种死锁检测(检测到死锁的循环依赖,立即返回错误)和死锁超时机制(当查询的时间达到锁等待超时的设定后放弃锁请求)。InnoDB目前处理死锁的方法是,将持有最少行级排他锁的事务进行回滚。

注意:锁的行为和顺序,是和存储引擎相关的。以同样的顺序执行语句,有些存储引擎会产生死锁,有些则不会。死锁问题,对于事务型系统是不可避免的,所以应用程序在设计时必须要考虑如何处理死锁(大多数情况,只需要重新执行因死锁回滚的事务即可)。

0 0
原创粉丝点击