元数据锁
悲观者从机会中看到困难。乐观者从困难中看到机会。
——温斯顿·丘吉尔
我们不需要显示的使用 MDL,因为当我们对数据库表进行操作时,会自动给这个表加上 MDL:
- 对一张表进行 CRUD 操作时,加的是 MDL 读锁;
- 对一张表做结构变更操作的时候,加的是 MDL写锁:
MDL是为了保证当用户对表执行 CRUD 操作时,防止其他线程对这个表结构做了变更。
当有线程在执行 select 语句 (加 MDL 读锁)的期间,如果有其他线程要更改该表的结构 (申请 MDL 写锁),那么将会被阻塞,直到执行完 select 语句(释放 MDL 读锁)。
反之,当有线程对表结构进行变更 (加 MDL 写锁)的期间,如果有其他线程执行了 CRUD 操作 (申请MDL 读锁),那么就会被阻塞,直到表结构变更完成(释放 MDL 写锁)。
MDL不需要显示调用,那它是在什么时候释放的?
MDL是在事务提交后才会释放,这意味着事务执行期间,MDL是一直持有的
那如果数据库有一个长事务(所谓的长事务,就是开启了事务,但是一直还没提交),那在对表结构做变更操作的时候,可能会发生意想不到的事情,比如下面这个顺序的场景:
- 1.首先,线程 A先启用了事务(但是一直不提交),然后执行一条 select 语句,此时就先对该表加上MDL 读锁;
- 2.然后,线程B也执行了同样的 select 语句,此时并不会阻塞,因为「读读」并不冲突;
- 3.接着,线程C修改了表字段,此时由于线程A的事务并没有提交,也就是 MDL 读锁还在占用着,这时线程C 就无法申请到 MDL写锁,就会被阻塞;
那么在线程C 阻塞后,后续有对该表的 select 语句,就都会被阻塞,如果此时有大量该表的 select 语句的请求到来,就会有大量的线程被阻塞住,这时数据库的线程很快就会爆满了。
为什么线程 C 因为申请不到 MDL 写锁,而导致后续的申请读锁的查询操作也会被阻塞?
这是因为申请 MDL 锁的操作会形成一个队列,队列中写锁获取优先级高于读锁,一旦出现 MDL 写锁等待,会阻塞后续该表的所有 CRUD 操作。
所以为了能安全的对表结构进行变更,在对表结构变更前,先要看看数据库中的长事务,是否有事务已经对表加上了 MDL 读锁,如果可以考虑 kill 掉这个长事务,然后再做表结构的变更。
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号