innodb 源碼分析 --鎖

innodb引擎中的鎖分兩種html

1)針對數據結構, 如鏈表 mysql

    互斥鎖sql

    讀寫鎖數組

http://mysqllover.com/?p=425數據結構

http://www.cnblogs.com/justfortaste/p/3668935.html函數

2)針對數據表中記錄this

    行鎖spa

    表鎖操作系統

 

innodb中的互斥鎖是依據操做系統中的spin lock自旋鎖,進行一些修改而成的線程

 修改方面:

  1.當得到不到鎖時, 一直在CPU高速緩衝區中讀取值,避免與內存打交道,以避免形成總線風暴

      2.當自旋超過20us後,將線程放入wait array,待時機成熟後再喚醒,而不是放入系統的等待隊列,避免上下文切換

詳情請見 http://www.cnblogs.com/taek/p/4809685.html

 

這裏主要分析下innodb的行鎖

當執行sql 如 update user set name='xx' where id=1;時

innodb引擎會針對id=1這條記錄加一個LOCK_REC行鎖

這個鎖並無附在物理記錄自己,而是存儲於單獨一個結構體中

 

對於行鎖,先嚐試加快鎖,若是不行,再加慢鎖

 

/*********************************************************************//** Tries to lock the specified record in the mode requested. If not immediately possible, enqueues a waiting lock request. This is a low-level function which does NOT look at implicit locks! Checks lock compatibility within explicit locks. This function sets a normal next-key lock, or in the case of a page supremum record, a gap type lock. @return DB_SUCCESS, DB_SUCCESS_LOCKED_REC, DB_LOCK_WAIT, DB_DEADLOCK, or DB_QUE_THR_SUSPENDED */

/**嘗試用某種請求的鎖,去鎖住指定的行記錄,若是不能當即上鎖,將等待的鎖入隊列。
該函數級別較低,不能看作隱式鎖,檢查該鎖與顯示鎖的相容性。該功能設置常規的next-key lock,
或爲頁面的supremum record設置gap lock
*/
static enum db_err lock_rec_lock( /*==========*/ ibool impl, /*!< in: if TRUE, no lock is set if no wait is necessary: we assume that the caller will set an implicit lock */ ulint mode, /*!< in: lock mode: LOCK_X or LOCK_S possibly ORed to either           LOCK_GAP or LOCK_REC_NOT_GAP */ const buf_block_t* block, /*!< in: buffer block containing the record */ //記錄所在的block塊 ulint heap_no, /*!< in: heap number of record */ dict_index_t* index, /*!< in: index of record */ que_thr_t* thr) /*!< in: query thread */ { /* We try a simplified and faster subroutine for the most common cases */ switch (lock_rec_lock_fast(impl, mode, block, heap_no, index, thr)) { case LOCK_REC_SUCCESS: return(DB_SUCCESS); case LOCK_REC_SUCCESS_CREATED: return(DB_SUCCESS_LOCKED_REC); case LOCK_REC_FAIL: return(lock_rec_lock_slow(impl, mode, block, heap_no, index, thr)); } return(DB_ERROR); }

 

 加快鎖

UNIV_INLINE enum lock_rec_req_status lock_rec_lock_fast( /*===============*/ ibool impl, /*!< in: if TRUE, no lock is set if no wait is necessary: we assume that the caller will set an implicit lock */ ulint mode, /*!< in: lock mode: LOCK_X or LOCK_S possibly ORed to either LOCK_GAP or LOCK_REC_NOT_GAP */
    const buf_block_t*    block,    /*!< in: buffer block containing the record */ ulint heap_no,/*!< in: heap number of record */ dict_index_t*        index,    /*!< in: index of record */ que_thr_t*        thr)    /*!< in: query thread */ { lock_t*    lock; //詳見 結構體 trx_t* trx; //詳見 結構體 
   lock
= lock_rec_get_first_on_page(block); //根據block,取出space,page_no構成hash,取出相應值,詳見 trx = thr_get_trx(thr); if (lock == NULL) { if (!impl) {//顯示鎖 lock_rec_create(mode, block, heap_no, index, trx); //若是爲空,則建立鎖, 並返回加鎖成功,詳見 } return(LOCK_REC_SUCCESS_CREATED); }
//不清楚爲何要取下一個lock記錄
if (lock_rec_get_next_on_page(lock)) { return(LOCK_REC_FAIL); }   
/**
*鎖的事務不是當前事務
*或者鎖的類型不是 mode|LOCK_REC (不包含行鎖)
*lock_rec_get_n_bits返回
lock->un_member.rec_lock.n_bits,這個n_bits能夠理解爲page中記錄個數,對應的byte是n_bits/8+1
*例如 page中只有5條記錄 5/8+1=1 只須要1+64=65字節便可表示這此記錄
 *若是heap_no大於上面的n_bits,意味着 heap_no不能存放到由n_bits構造的二維數組中(該數組實際上是n_bits/8+1)
*返回failed
*/
if (lock->trx != trx || lock->type_mode != (mode | LOCK_REC) || lock_rec_get_n_bits(lock) <= heap_no) { //詳見 return(LOCK_REC_FAIL); } if (!impl) { /* If the nth bit of the record lock is already set then we do not set a new lock bit, otherwise we do set */ if (!lock_rec_get_nth_bit(lock, heap_no)) { lock_rec_set_nth_bit(lock, heap_no); return(LOCK_REC_SUCCESS_CREATED); } } return(LOCK_REC_SUCCESS); }

 

 

 慢加鎖

static
enum db_err lock_rec_lock_slow( /*===============*/ ibool impl, /*!< in: if TRUE, no lock is set if no wait is necessary: we assume that the caller will set an implicit lock */ ulint mode, /*!< in: lock mode: LOCK_X or LOCK_S possibly ORed to either LOCK_GAP or LOCK_REC_NOT_GAP */
    const buf_block_t*    block,    /*!< in: buffer block containing the record */ ulint heap_no,/*!< in: heap number of record */ dict_index_t*        index,    /*!< in: index of record */ que_thr_t*        thr)    /*!< in: query thread */ { trx_t* trx; lock_t*    lock; trx = thr_get_trx(thr);  /**
*根據block計算出space_id和page_no,再根據這兩個值計算出hash值
*經過hash值在lock_sys->rec_hash這個HashTable中查詢,當找到的頭結點不符號條件,則遍歷
*若是HashTable中某一個鎖權限大於預加鎖的權限時,直接返回

*
*若是HashTable中的某一個鎖權限小於預加鎖權限時,再判斷兼容性
*例如在HashTable中找到一個lock,其mode爲S
*預加鎖的mode爲X,S強度要小於X
*
*鎖之間兼容性判斷
*預加鎖的權限 是否 兼容 HashTable中根據條件找到的lock的權限
*例如:
*預加鎖權限 LOCK_X
*找到的lock 權限 LOCK_S
*經查找兼容矩陣:
* 不兼容:新建lock,其mode爲 LOCK_X|LOCK_WAIT
* 兼容: 新建lock,其mode爲LOCK_X|LOCK_REC

*/
lock = lock_rec_has_expl(mode, block, heap_no, trx); //函數實現  if (lock) { if (lock->type_mode & LOCK_CONV_BY_OTHER) { //#define LOCK_CONV_BY_OTHER 4096 /* This lock or lock waiting was created by the other transaction, not by the transaction (trx) itself. So, the transaction (trx) should treat it collectly according as whether granted or not. */

            if (lock->type_mode & LOCK_WAIT) { //#define LOCK_WAIT 256 /* This lock request was not granted yet. Should wait for granted. */

                goto enqueue_waiting; } else { /* This lock request was already granted. Just clearing the flag. */

                lock->type_mode &= ~LOCK_CONV_BY_OTHER; } } /* The trx already has a strong enough lock on rec: do nothing */ } else if (lock_rec_other_has_conflicting(mode, block, heap_no, trx)) { //若是出現衝突,即不兼容,則建立一個新lock,且鎖模式爲 預加鎖模式+ lock_wait /* If another transaction has a non-gap conflicting request in the queue, as this transaction does not have a lock strong enough already granted on the record, we have to wait. */ ut_ad(lock == NULL); enqueue_waiting: return(lock_rec_enqueue_waiting(mode, block, heap_no,ock, index, thr)); //函數實現 } else if (!impl) { /* Set the requested lock on the record */ lock_rec_add_to_queue(LOCK_REC | mode, block,heap_no, index, trx); //鎖模式加上行鎖 函數實現     return(DB_SUCCESS_LOCKED_REC); } return(DB_SUCCESS); }

 

 

 

 

 

 

檢查已存在的lock的模式是否與欲加鎖的模式有衝突,返回true 說明有衝突

static
lock_t*
lock_rec_other_has_conflicting(
/*===========================*/
    enum lock_mode        mode,    /*!< in: LOCK_S or LOCK_X,
                    possibly ORed to LOCK_GAP or
                    LOC_REC_NOT_GAP,
                    LOCK_INSERT_INTENTION */
    const buf_block_t*    block,    /*!< in: buffer block containing
                    the record */
    ulint            heap_no,/*!< in: heap number of the record */
    trx_t*            trx)    /*!< in: our transaction */
{
    lock_t*    lock;lock = lock_rec_get_first(block, heap_no);
    if (UNIV_LIKELY_NULL(lock)) {
        if (UNIV_UNLIKELY(heap_no == PAGE_HEAP_NO_SUPREMUM)) {
            do {
                if (lock_rec_has_to_wait(trx, mode, lock,
                             TRUE)) {
                    return(lock);
                }
                lock = lock_rec_get_next(heap_no, lock);
            } while (lock);
        } else {
            do {
                if (lock_rec_has_to_wait(trx, mode, lock,
                             FALSE)) {
                    return(lock);
                }
                lock = lock_rec_get_next(heap_no, lock);
            } while (lock);
        }
    }

    return(NULL);
}

 

 

ibool
lock_rec_has_to_wait(
/*=================*/
    const trx_t*    trx,    /*!< in: trx of new lock */
    ulint        type_mode,/*!< in: precise mode of the new lock
                to set: LOCK_S or LOCK_X, possibly
                ORed to LOCK_GAP or LOCK_REC_NOT_GAP,
                LOCK_INSERT_INTENTION */
    const lock_t*    lock2,    /*!< in: another record lock; NOTE that
                it is assumed that this has a lock bit
                set on the same record as in the new
                lock we are setting */
    ibool lock_is_on_supremum)  /*!< in: TRUE if we are setting the
                lock on the 'supremum' record of an
                index page: we know then that the lock
                request is really for a 'gap' type lock */
{
    ut_ad(trx && lock2);
    ut_ad(lock_get_type_low(lock2) == LOCK_REC);

    if (trx != lock2->trx
        && !lock_mode_compatible(LOCK_MODE_MASK & type_mode,lock_get_mode(lock2))) { //檢查鎖與鎖之間的兼容性, 詳情 /* We have somewhat complex rules when gap type record locks
        cause waits */

        if ((lock_is_on_supremum || (type_mode & LOCK_GAP))&& !(type_mode & LOCK_INSERT_INTENTION)) {

            /* Gap type locks without LOCK_INSERT_INTENTION flag
            do not need to wait for anything. This is because
            different users can have conflicting lock types
            on gaps. */

            return(FALSE);
        }

        if (!(type_mode & LOCK_INSERT_INTENTION)
            && lock_rec_get_gap(lock2)) {

            /* Record lock (LOCK_ORDINARY or LOCK_REC_NOT_GAP
            does not need to wait for a gap type lock */

            return(FALSE);
        }

        if ((type_mode & LOCK_GAP)
            && lock_rec_get_rec_not_gap(lock2)) {

            /* Lock on gap does not need to wait for
            a LOCK_REC_NOT_GAP type lock */

            return(FALSE);
        }

        if (lock_rec_get_insert_intention(lock2)) {

            /* No lock request needs to wait for an insert
            intention lock to be removed. This is ok since our
            rules allow conflicting locks on gaps. This eliminates
            a spurious deadlock caused by a next-key lock waiting
            for an insert intention lock; when the insert
            intention lock was granted, the insert deadlocked on
            the waiting next-key lock.

            Also, insert intention locks do not disturb each
            other. */

            return(FALSE);
        }

        return(TRUE);
    }

    return(FALSE);
}
相關文章
相關標籤/搜索