MDL--元数据锁的锁请求与锁等待+元数据锁类对象

Posted 那海蓝蓝

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MDL--元数据锁的锁请求与锁等待+元数据锁类对象相关的知识,希望对你有一定的参考价值。

1 元数据锁的锁请求与锁等待

    元数据锁在mysql Server层,按照锁的状态被细分为两种,一种是已经施加的锁,一种是等待施加的锁即锁请求,这样被区分的原因,如MySQL对“class MDL_request”的代码注释作了解释:

/**

  A pending metadata lock request.

 

  A lock request and a granted metadata lock are represented by

  different classes because they have different allocation

  sites and hence different lifetimes. The allocation of lock requests is     //注意这里给出的原因是从工程实践中对锁的实现的角度区分的

  controlled from outside of the MDL subsystem, while allocation of granted   //体现了工程与理论的不同

  locks (tickets) is controlled within the MDL subsystem.

 

  MDL_request is a C structure, you don‘t need to call a constructor

  or destructor for it.

*/

class MDL_request{...}  //锁请求

    所以,元数据锁在使用的过程中又被细分为“lock granted metadata” (代码中使用“class MDL_ticket”表示。ticket,入场卷,即要被授予的锁)和“lock request metadata”(代码中使用“class MDL_request”表示加锁的请求)。这样,MySQL Server分别使用两个类来表示这两种被细分的锁。

    当锁请求要求施加锁不成功的时候,则产生锁等待,而锁等待则用MDL_wait表示。

/**A reliable way to wait on an MDL lock. */

class MDL_wait{...  //锁等待。在获取锁的过程中,需要为是否获得锁做标志,采用的就是锁等待的状态值

enum enum_wait_status { //锁等待的状态,在锁等待在生命周期内因不同操作产生不同结果

    EMPTY = 0, //空状态,初始值

    GRANTED,   //锁被授予的状态

    VICTIM,    //某个会话被选为了受害者

    TIMEOUT,   //超时状态,申请加锁却发生超时

    KILLED };  //killed状态,发起锁请求的会话被killed掉,所以不仅是发起加锁请求的事务应终止,事务的属主会话也被终止

...}

2 元数据锁类对象

    保存每一MDL_key对应的所有已经授予的MDL锁信息,因为MDL_key标识了不同的数据库对象类,不同的数据库对象类上所施加的锁之间的兼容性因对象存在差别,所以定义了不同的策略来区分这些差别。请注意,MDL_lock不是一个具体的锁,而是一类锁的集合GLOBALCOMMIT类的锁,是全局唯一的,除此之外的其他类型的锁,可以有多个。

/**

The lock context. Created internally for an acquired lock.

 For a given name, there exists only one MDL_lock instance,and it exists only when the lock has been granted. 

Can be seen as an MDL subsystem‘s version of TABLE_SHARE.  //能够被存储层的TABLE_SHARE使用这个锁对象

 

This is an abstract class which lacks information aboutcompatibility rules for lock types.

 They should be specifiedin its descendants. 

*/

class MDL_lock  //当需要施加元数据锁的时候,生成一个MDL_lock锁对象

{...

    class Ticket_list{...}  //MDL_ticket封装为一个 List对象,用以存放多个MDL_ticket锁对象(MDL_ticket参见下一小节)

 

    /**  //对于锁的操作,又存在两种策略,这是根据被加锁的对象的特定区分的。每一种策略分别有各自的锁兼容规则

      Helper struct which defines how different types of locks are handledfor a specific MDL_lock.

      In practice we use only two strategies:

      "scoped"lock strategy for locks in GLOBAL, COMMIT, TABLESPACE and SCHEMA namespaces  //范围锁策略:范围带有空间的意味

      and "object" lock strategy for all other namespaces.                                 //对象锁策略:数据库内部的各种对象

     */

    struct MDL_lock_strategy //锁的施加策略。如上所述,锁被分为两种类型,所以统一用策略数据结构来管理和描述这两类锁的特性

    {

        /**Compatibility (or rather "incompatibility") matrices for lock types. //新申请的锁向已经授予的锁进行锁的相容性判断

            Array of bitmaps which elements specify which granted locks areincompatible with the type of lock being requested.*/   

        bitmap_tm_granted_incompatible[MDL_TYPE_END];  //已经被授予的锁的数组中保存有不兼容的、准备申请此对象的请求锁,位图数组结构

 

        //新申请的锁向已经正在等待的锁进行锁的相容性(优先级)判断。此数组的作用有两个:

        //一是通过m_waiting_incompatible[0]

        //二是防止产生锁饥饿现象,

        //所以增加了对低优先级锁的申请次数的计数,当计数到一定程度时,唤醒低优先级的尚没获得锁的会话。

        //可以关注MDL_lock::reschedule_waiters()函数,查看对等待的锁的处理方式;看其调用者查看唤醒条件。

        //另外看此函数中封锁粒度较强的锁释放而封锁粒度较弱的锁得以有机会被授予的时候,

        //m_hog_lock_count/m_piglet_lock_count有机会被重置

        //(注意强弱是相对的,取决于11.4.1节中第3小节中定义的enum_mdl_type中的锁的粒度值,据这些值比较大小)

         /** Arrays of bitmaps which elements specify which waiting locks areincompatible with the type of lock being requested.

            Basically, eacharray defines priorities between lock types.

           We need 4 separate arrays since in order to prevent starvation for some of lock request types, we use different priority matrices:

          0) in "normal" situation.  //正常优先级

          1) in situation when the number of successively granted "piglet" requestsexceeds the max_write_lock_count limit. //小猪优先级

          2) in situation when the number of successively granted "hog" requestsexceeds the max_write_lock_count limit. //猪优先级

          3) in situation when both "piglet" and "hog" counters exceed limit.*/  //小猪和猪之和

        //这个矩阵是某个锁请求与处于等待状态的锁的优先级比较表

         //第一个数组是正常情况,其他三个数组是为解决锁饥饿而设置的

         //m_piglet_lock_count的值大于了max_write_lock_count,则m_waiting_incompatible[1][x]被置位

        //m_hog_lock_count的值大于了max_write_lock_count,则m_waiting_incompatible[2][x]被置位

        //在等待的锁的数组中保存有不兼容的、准备申请此对象的请求锁,二维位图数组结构

        bitmap_tm_waiting_incompatible[4][MDL_TYPE_END];//piglet,会申请SW锁;hog,会申请XSNRWSNW这三者其一

 

        /**Array of increments for "unobtrusive" types of lock requests for locks.

           @sa MDL_lock::get_unobtrusive_lock_increment().*/

        //“unobtrusive”类型相关的锁粒度包括: SSHSR SW,对应“Fast path”的访问方式,主要用于DML类操作

        //obtrusive” 类型相关的锁粒度包括:SUSROSNWSNRWX,对应“slow path”的访问方式,主要用于非DML类操作

        fast_path_state_tm_unobtrusive_lock_increment[MDL_TYPE_END]; //快速访问“unobtrusive”类型的锁

 

        /**Indicates that locks of this type are affected bythe max_write_lock_count limit.*/

        bool m_is_affected_by_max_write_lock_count;

 

        /**Pointer to a static method which determines if the type of lockrequested requires notification of conflicting locks.

           NULL if thereare no lock types requiring notification.*/

        //当有冲突锁的时候,是否要被通知。“scopedlock”不要求被通知,而“object lock”施加排它锁时才需要被通知

        bool (*m_needs_notification)(const MDL_ticket *ticket);

 

        /**Pointer to a static method which allows notification of owners ofconflicting locksabout the fact

           that a type of lock requiringnotification was requested.*/

        //对于“object lock”,通知持有SSH类锁的会话线程(可能与待定的X锁冲突,pending lock

        void (*m_notify_conflicting_locks)(MDL_context *ctx, MDL_lock *lock); //发出通知的函数,含义类似上面

 

        /**Pointer to a static method which converts information aboutlocks granted using "fast" path

           from fast_path_state_trepresentation to bitmap of lock types.*/

        //SSRSW粒度的锁可以被使用“fast path”方式快速处理

        bitmap_t (*m_fast_path_granted_bitmap)(const MDL_lock &lock);

 

         /**Pointer to a static method which determines if waiting for the lockshould be aborted

           when connection is lost. NULL if locks ofthis type don‘t require such aborts.*/ //当连接断开的时候,锁是否应该被撤销。

        //LOCKING_SERVICEUSER_LEVEL_LOCK加锁的情况可被撤销,如通过GET_LOCK()施加的锁

        bool (*m_needs_connection_check)(const MDL_lock *lock);

    };

 

public:

    /** The key of the object (data) being protected. */

     MDL_key key;   //元数据锁所属的类型(在MDL_key中把元数据这样的对象分为了几类,给每类定义一个keyenum_mdl_namespace枚举中)

    /** List of granted tickets for this lock. */

    Ticket_list m_granted;  //对于本锁而言,在系统内部存在的已经被授予的所有锁list

    /** Tickets for contexts waiting to acquire a lock. */

Ticket_list m_waiting; //对于本锁而言,在系统内部存在的正在等待被授予的所有锁list

//如上两个list,配合使用:

//当要获取一个锁(入通过acquire_lock()函数)不成功时,把新生成的一个MDL_ticket对象放入等待队列;获取成功,则放入m_granted

//当一个处于等待状态的锁可以被授予的时候(can_grant_lock()判断是否可以被授予),就从m_waitingremove掉,然后加入到m_granted中,

//这样的事情,当获取锁或释放锁时,或因ALTER TABLE等操作需要降级锁时,通过reschedule_waiters()函数进行

...

    /** Pointer to strategy object which defines how different types of lock

        requests should be handled for the namespace to which this lock belongs.

        @sa MDL_lock::m_scoped_lock_strategy and MDL_lock:m_object_lock_strategy. */

    const MDL_lock_strategy *m_strategy; //注意这是一个指针,执行哪个类型的策略就表示使用被指向的策略对象之策略(指向下面两个策略对象之一)

...

    static const MDL_lock_strategy m_scoped_lock_strategy; //范围锁对应的策略

    static const MDL_lock_strategy m_object_lock_strategy; //对象锁对应的策略

};

以上是关于MDL--元数据锁的锁请求与锁等待+元数据锁类对象的主要内容,如果未能解决你的问题,请参考以下文章

深入理解MDL元数据锁

Mysql中的锁:表MDL意向锁行锁

Java多线程---同步与锁

MySQL的事务与锁 转

Mysql全局锁和表级锁

(转)java并发对象锁类锁私有锁