死锁
死锁是指两个或多个事务在同一资源上相互占用,并请求锁定对方占用的资源,从而导致恶性循环的现象。当多个事务试图以不同的顺序锁定资源时,也会长生死锁。例如,设想下面两个事务同事处理StockPrice表:
事务1
START TRANSACTION;
UPDATE StockPrice SET close = 45.50 WHERE stock_id = 4 and date = '2002-05-01';
UPDATE StockPrice SET close = 19.80 WHERE stock_id = 3 and date = '2002-05-02';
COMMIT;
事务2
START TRANSACTION;
UPDATE StockPrice SET high = 20.12 WHERE stock_id = 3 and date= '2002-05-02';
UPDATE StockPrice SET high = 47.20 WHERE stock_id = 4 and date = '2002-05-01;'
如果凑巧,两个事务都执行了第一条UPDATE语句,更新了一行数据,同时也锁定了该行数据,接着每个事务都尝试去执行第二条UPDATE语句,却发现该行已经被对方锁定,然后两个事务都等待对方释放锁,同时又持有对方需要的锁,则陷入死循环。除非有外部因素介入才可能解除死锁。
解决这种问题:
- 1.数据库系统实现了各种死锁检测和死锁超时机制。越复杂的系统,比如InnoDB存储引擎,越能检测到死锁的循环依赖,并立即返回一个错误。这种解决方式很有效,否则死锁会导致出现非常慢的查询。
- 2.当查询的时间达到锁等待超时的设定后放弃锁请求,这种方式通常来说不太好。
InnoDB目前处理死锁的方法是:将持有最少行级排他锁的事务进行回滚(这是相对比较简单的死锁回滚算法)
事务日志
事务日志可以帮助提高事务的效率。使用事务日志,存储引擎在修改表的数据时只需要修改其内存拷贝,再把该修改行为记录到持久在硬盘上的事务日志中,而不用每次都将修改的数据本身持久到磁盘。事务日志采用的是追加的方式,因此写日志的操作是磁盘上一小块区域的顺序I/O,而不像随机I/O需要在磁盘的多个地方移动磁头,所以采用事务日志的方式来说要快得多。事务日志持久以后,内存中被修改的数据在后台可以慢慢地刷回磁盘。目前大多数存储引擎都是这样实现的,我们通常称之为预写式日志,修改数据需要些两次磁盘。
如果数据的修改已经记录到事务日志并持久化,但数据本身没有写回到磁盘,此时系统崩溃,存储引擎在重启时能够恢复这部分修改的数据,具体的恢复方式则视存储引擎而定。
Mysql中的事务
提供了两种事务型的存储引:InnodDB和 NDB Cluster。
自动提交
Mysql默认采用自动提交模式。也就是说,如果不是显式地开始一个事务,则每个查询都被当做一个事务执行操作。在当前连接中,可以通过设置AUTOCOMMIT变量来启用或者禁用自动提交模式:
1或者ON表示启用,0或者OFF表示禁用。当AUTOCOMMIT=0时,所有的查询都是在一个事务中,直到显示地执行COMMIT提交或者ROLLBACK回滚,该事物结束,同时又开启了另一个新事务。修改AUTOCOMMIT对非事务的表,比如MyISAL或者内存表,不会有任何影响。
在执行之前会强制执行COMMITt提交当前的活动事务的命令:
- 1.会导致大量数据改变的操作( ALTER TABLE)
- 2.LOCK TABLES等其他语句
MVCC是行级锁的一个变种,但是它在很多情况下避免了加锁操作,因此开销更低。
MVCC的实现,是通过保存数据在某个时间点的快照来实现的。也就是说,不管需要执行多长时间,每个事务看到的数据都是一致的。根据事务开始的时间不同,每个事务对同一张表,同一时刻看到的数据可能是不一样的。
不同存储引擎的MVCC实现是不同的,典型的有乐观(optimist)并发控制和悲观(pessimistic)并发控制。
InnoDB的MVCC,是通过在每行记录后面保存两个隐藏的列来实现的。这俩个列,一个保存了行的创建时间,一个保存行的过期时间(或删除时间)。当然存储的并不是实际的时间值,而是系统版本号。每开始一个新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询到的每行记录的版本号进行比较。
image.png
网友评论