global read lock
Global_read_lock类实现global
read lock。它有两个私有成员,分别在global和commit域保护global read lock。
1. m_mdl_global_shared_lock
MDL_ticket类型,用于”GLOBAL”
namespace内保护global read lock,阻止所有DDL语句。
实现方法是会在GLOBAL namespace层级获取MDL shared锁。我们知道,对一个object 修改时,需要获取GLOBAL级别的MDL IX(Intension Exclusive)锁。
由于MDL_shared 与MDL_INTENSION_EXCLUSIVE的排斥,所以就达到阻止其它DDL的目的。
2. m_mdl_blocks_commits_lock
DML_ticket类型,用于”COMMIT”
namespace,阻止所有事务commit,即DML操作。
上面两者的结合,完整实现了global read lock的意义。
通常来讲,global read lock的实现也是分两步的,首先是lock_global_read_lock(),其次是make_global_read_lock_block_commit。但是第二步是可选的,如果没有第二步,commit事务将被允许。
Flush tables with read lock
Flush tables with read lock也是获取global read lock的方法之一。但是flush
tables with read lock在上面两步中插入了另外一步,就是close_cached_tables()。这一步的作用是,关闭已经打开的table。
但是这一步本来也会被堵,原因想必大家都非常清楚,那就是之前还有被操作的并持续打开的表。
所以这里会有一个问题:为什么m_mdl_global_shared_lock()和m_mdl_blocks_commits_lock()不能合到一起,作为第一步,然后再close_cached_tables()?
原因很简单,这样会导致死锁。假设这样一个场景:
线程T1: select *
from tab1 for update
线程T2: update tab1
set b=3 #此时被row-level
lock堵住
线程T3: flush
tables with read lock #被线程T2堵住,因为需要关闭表
接下来T1 commit事务,这将被T3堵住。T1堵住T2,T2堵住T3,T3堵住T1。
由此,死锁产生。所以read
lock和commit lock分开到第1和第3步实现才是安全的。
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/30088583/viewspace-1447289/
,如需转载,请注明出处,否则将追究法律责任。