亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Mysql鎖內(nèi)部實(shí)現(xiàn)機(jī)制之C源碼解析

 更新時(shí)間:2022年08月22日 16:21:00   作者:π大星的日常  
數(shù)據(jù)庫(kù)之所以要加鎖,因?yàn)閿?shù)據(jù)庫(kù)是一個(gè)多用戶使用的共享資源。當(dāng)多個(gè)用戶并發(fā)地存取數(shù)據(jù)時(shí),在數(shù)據(jù)庫(kù)中就會(huì)產(chǎn)生多個(gè)事務(wù)同時(shí)存取同一數(shù)據(jù)的情況。若對(duì)并發(fā)操作不加控制就可能會(huì)讀取和存儲(chǔ)不正確的數(shù)據(jù),破壞數(shù)據(jù)庫(kù)的一致性

概述

雖然現(xiàn)在關(guān)系型數(shù)據(jù)庫(kù)越來(lái)越相似,但其背后的實(shí)現(xiàn)機(jī)制可能大相徑庭。實(shí)際使用方面,因?yàn)镾QL語(yǔ)法規(guī)范的存在使得我們熟悉多種關(guān)系型數(shù)據(jù)庫(kù)并非難事,但是有多少種數(shù)據(jù)庫(kù)可能就有多少種鎖的實(shí)現(xiàn)方法。

Microsoft Sql Server2005之前只提供頁(yè)鎖,直到2005版本才開(kāi)始支持樂(lè)觀并發(fā)、悲觀并發(fā),樂(lè)觀模式下允許實(shí)現(xiàn)行級(jí)別鎖,在Sql Server的設(shè)計(jì)中鎖是一種稀缺資源,鎖的數(shù)量越多,開(kāi)銷(xiāo)就越大,為了避免因?yàn)殒i的數(shù)量快速攀升導(dǎo)致性能斷崖式下跌,其支持一種稱(chēng)為鎖升級(jí)的機(jī)制,一旦行鎖升級(jí)為頁(yè)鎖,并發(fā)性能就又回到原點(diǎn)。

事實(shí)上,即使在同一個(gè)數(shù)據(jù)庫(kù),不同的執(zhí)行引擎對(duì)鎖這一功能的詮釋依然是百家爭(zhēng)鳴。對(duì)于MyISAM而言僅僅支持表鎖,并發(fā)讀取尚可,并發(fā)修改可就捉襟見(jiàn)肘了。Innodb則和Oracle非常相似,提供非鎖定一致性讀取、行鎖支持,與Sql Server明顯不同的是隨著鎖總數(shù)的上升,Innodb僅僅只需要付出一點(diǎn)點(diǎn)代價(jià)。

行鎖結(jié)構(gòu)

Innodb支持行鎖,且對(duì)于鎖的描述并不會(huì)存在特別大的開(kāi)銷(xiāo)。因此不需要鎖升級(jí)這一機(jī)制作為大量鎖導(dǎo)致性能下降之后的搶救措施。

摘自lock0priv.h文件,Innodb對(duì)于行鎖的定義如下:

/** Record lock for a page */
struct lock_rec_t {
    /* space id */
    ulint  space;	
    /* page number */
    ulint  page_no;
    /**
     * number of bits in the lock bitmap; 
     * NOTE: the lock bitmap is placed immediately after the lock struct 
     */
    ulint  n_bits;			
};

不難看出雖然并發(fā)控制可以細(xì)化到行級(jí)別,但是鎖以頁(yè)的粒度組織管理。Innodb的設(shè)計(jì)中通過(guò)space id、page number兩個(gè)必要條件就可以確定唯一一個(gè)數(shù)據(jù)頁(yè),n_bits表示描述該頁(yè)行鎖信息需要多少bit位。

同一數(shù)據(jù)頁(yè)中每條記錄都分配唯一的連續(xù)的遞增序號(hào):heap_no,若要知道某一行記錄是否上鎖,則只需要判斷位圖heap_no位置的數(shù)字是否為一即可。由于lock bitmap根據(jù)數(shù)據(jù)頁(yè)的記錄數(shù)量進(jìn)行內(nèi)存空間分配的,因此沒(méi)有顯式定義,且該頁(yè)記錄可能還會(huì)繼續(xù)增加,因此預(yù)留了LOCK_PAGE_BITMAP_MARGIN大小的空間。

/** 
 * Safety margin when creating a new record lock: this many extra records
 * can be inserted to the page without need to create a lock with 
 * a bigger bitmap
 */
#define LOCK_PAGE_BITMAP_MARGIN	 64

假設(shè)space id = 20,page number = 100的數(shù)據(jù)頁(yè)目前有160條記錄,heap_no為2、3、4的記錄已經(jīng)被鎖,則對(duì)應(yīng)的lock_rec_t結(jié)構(gòu)與數(shù)據(jù)頁(yè)應(yīng)該被這樣刻畫(huà):

注:

  • 內(nèi)存中的lock bitmap應(yīng)該是線性分布的,圖中所示二維結(jié)構(gòu)是為了方便描述
  • bitmap與lock_rec_t結(jié)構(gòu)是一塊連續(xù)內(nèi)存,圖中引用關(guān)系也是繪圖需要

可以看到該頁(yè)對(duì)應(yīng)的bitmap第二三四位置全部置一,描述一個(gè)數(shù)據(jù)頁(yè)行鎖所消耗內(nèi)存從感官上相當(dāng)有限,那具體占用多少呢?我們可以計(jì)算一下:

160 / 8 + 8 + 1 = 29byte。

  • 160條記錄對(duì)應(yīng)160bit
  • +8是因?yàn)樾枰A(yù)留出64bit
  • +1是因?yàn)樵创a中還預(yù)留了1字節(jié)

這里還額外+1,應(yīng)該是為了避免因?yàn)檎龑?dǎo)致的結(jié)果數(shù)值偏小的問(wèn)題。假如是161條記錄如果不+1則計(jì)算出來(lái)的20byte不夠描述所有記錄的鎖信息(不動(dòng)用預(yù)留位)。

摘自lock0priv.h文件:

/* lock_rec_create函數(shù)代碼片段 */
n_bits = page_dir_get_n_heap(page) + LOCK_PAGE_BITMAP_MARGIN;
n_bytes = 1 + n_bits / 8;
/* 注意這里是分配的連續(xù)內(nèi)存 */
lock = static_cast<lock_t*>(
    mem_heap_alloc(trx->lock.lock_heap, sizeof(lock_t) + n_bytes)
);
/**
 * Gets the number of records in the heap.
 * @return number of user records 
 */
UNIV_INLINE ulint page_dir_get_n_heap(const page_t* page)	
{
    return(page_header_get_field(page, PAGE_N_HEAP) & 0x7fff);
}

表鎖結(jié)構(gòu)

Innodb還支持表鎖,表鎖可分為兩大類(lèi):意向鎖,自增鎖其數(shù)據(jù)結(jié)構(gòu)定義如下:

摘自lock0priv.h文件

struct lock_table_t {
    /* database table in dictionary cache */
    dict_table_t*  table;
    /* list of locks on the same table */
    UT_LIST_NODE_T(lock_t)  locks;
};

摘自u(píng)t0lst.h文件

struct ut_list_node {
    /* pointer to the previous node, NULL if start of list */
    TYPE*  prev;
    /* pointer to next node, NULL if end of list */
    TYPE*  next;
};
#define UT_LIST_NODE_T(TYPE)  ut_list_node<TYPE>

事務(wù)中鎖的描述

上述lock_rec_t、lock_table_t結(jié)構(gòu)只是單獨(dú)的定義,鎖產(chǎn)生于事務(wù)之中,因此每個(gè)事務(wù)對(duì)應(yīng)的行鎖、表鎖會(huì)有一個(gè)相應(yīng)的鎖的結(jié)構(gòu),其定義如下:

摘自lock0priv.h文件

/** Lock struct; protected by lock_sys->mutex */
struct lock_t {
    /* transaction owning the lock */
    trx_t*  trx;
    /* list of the locks of the transaction */
    UT_LIST_NODE_T(lock_t)  trx_locks;	
    /** 
     * lock type, mode, LOCK_GAP or LOCK_REC_NOT_GAP,
     * LOCK_INSERT_INTENTION, wait flag, ORed 
     */
    ulint  type_mode;
    /* hash chain node for a record lock */
    hash_node_t  hash;	
    /*!< index for a record lock */
    dict_index_t*  index;
    /* lock details */
    union {
        /* table lock */
        lock_table_t  tab_lock;
        /* record lock */
        lock_rec_t  rec_lock;
    } un_member;
};

lock_t是根據(jù)每個(gè)事務(wù)每個(gè)頁(yè)(或表)來(lái)定義的,但是一個(gè)事務(wù)往往涉及到多個(gè)頁(yè),因此需要鏈表trx_locks串聯(lián)起一個(gè)事務(wù)相關(guān)的所有鎖信息。除了需要根據(jù)事務(wù)查詢到所有鎖信息,實(shí)際場(chǎng)景還要求系統(tǒng)必須能夠快速高效的檢測(cè)出某個(gè)行記錄是否已經(jīng)上鎖。因此必須有一個(gè)全局變量支持對(duì)行記錄進(jìn)行鎖信息的查詢。Innodb選擇了哈希表,其定義如下:

摘自lock0lock.h文件

/** The lock system struct */
struct lock_sys_t {
    /* Mutex protecting the locks */
    ib_mutex_t  mutex;		
    /* 就是這里: hash table of the record locks */
    hash_table_t*  rec_hash;	
    /* Mutex protecting the next two fields */
    ib_mutex_t  wait_mutex;
    /** 
     * Array  of user threads suspended while waiting forlocks within InnoDB,
     * protected by the lock_sys->wait_mutex 
     */
    srv_slot_t*  waiting_threads;
    /*
     * highest slot ever used in the waiting_threads array,
     * protected by lock_sys->wait_mutex 
     */
    srv_slot_t*  last_slot;
    /** 
     * TRUE if rollback of all recovered transactions is complete. 
     * Protected by lock_sys->mutex 
     */
    ibool  rollback_complete;
    /* Max wait time */
    ulint  n_lock_max_wait_time;
    /**
     * Set to the event that is created in the lock wait monitor thread.
     * A value of 0 means the thread is not active
     */
    os_event_t	timeout_event;		
    /* True if the timeout thread is running */
    bool  timeout_thread_active;
};

函數(shù)lock_sys_create在database start之際負(fù)責(zé)初始化lock_sys_t結(jié)構(gòu)。rec_hash的hash slot數(shù)量由srv_lock_table_size變量決定。rec_hash哈希表的key值通過(guò)頁(yè)的space id,page number計(jì)算得出。

摘自lock0lock.ic、ut0rnd.ic 文件

/**
 * Calculates the fold value of a page file address: used in inserting or
 * searching for a lock in the hash table.
 *
 * @return folded value 
 */
UNIV_INLINE ulint lock_rec_fold(ulint space, ulint page_no)
{
    return(ut_fold_ulint_pair(space, page_no));
}
/**
 * Folds a pair of ulints.
 *
 * @return folded value 
 */
UNIV_INLINE ulint ut_fold_ulint_pair(ulint n1, ulint n2)
{
    return (
        (
            (((n1 ^ n2 ^ UT_HASH_RANDOM_MASK2) << 8) + n1)
            ^ UT_HASH_RANDOM_MASK
        ) 
        + n2
    );
}

這將意味著無(wú)法提供一個(gè)手段使得我們可以直接得知某一行是否上鎖。而是應(yīng)該先通過(guò)其所在的頁(yè)得到space id、page number通過(guò)lock_rec_fold函數(shù)得出key值而后經(jīng)過(guò)hash查詢得到lock_rec_t,而后根據(jù)heap_no掃描bit map,最終確定鎖信息。lock_rec_get_first函數(shù)實(shí)現(xiàn)了上述邏輯:

這里返回的其實(shí)是lock_t對(duì)象,摘自lock0lock.cc文件

/**
 * Gets the first explicit lock request on a record.
 *
 * @param block   : block containing the record 
 * @param heap_no : heap number of the record 
 *
 * @return first lock, NULL if none exists 
 */
UNIV_INLINE lock_t* lock_rec_get_first(const buf_block_t* block, ulint heap_no)
{
    lock_t*  lock;
    ut_ad(lock_mutex_own());
    for (lock = lock_rec_get_first_on_page(block); lock;
         lock = lock_rec_get_next_on_page(lock)
    ) {
        if (lock_rec_get_nth_bit(lock, heap_no)) {
            break;
        }
    }
    return(lock);
}

鎖維護(hù)以頁(yè)的粒度,不是一個(gè)最高效直接的方式,明顯的時(shí)間換空間,這種設(shè)計(jì)使得鎖的開(kāi)銷(xiāo)很小。某一事務(wù)對(duì)任一行上鎖的開(kāi)銷(xiāo)都是一樣的,鎖數(shù)量的上升也不會(huì)帶來(lái)額外的內(nèi)存消耗。

每個(gè)事務(wù)都對(duì)應(yīng)一個(gè)trx_t的內(nèi)存對(duì)象,其中保存著該事務(wù)鎖信息鏈表和正在等待的鎖信息。因此存在如下兩種途徑對(duì)鎖進(jìn)行查詢:

  • 根據(jù)事務(wù): 通過(guò)trx_t對(duì)象的trx_locks鏈表,再通過(guò)lock_t對(duì)象中的trx_locks遍歷可得某事務(wù)持有、等待的所有鎖信息。
  • 根據(jù)記錄: 根據(jù)記錄所在的頁(yè),通過(guò)space id、page number在lock_sys_t結(jié)構(gòu)中定位到lock_t對(duì)象,掃描bitmap找到heap_no對(duì)應(yīng)的bit位。

上述各種數(shù)據(jù)結(jié)構(gòu),對(duì)其整理關(guān)系如下圖所示:

注:

lock_sys_t中的slot顏色與lock_t顏色相同則表明lock_sys_t slot持有l(wèi)ock_t 指針信息,實(shí)在是沒(méi)法連線,不然圖很混亂

到此這篇關(guān)于Mysql鎖內(nèi)部實(shí)現(xiàn)機(jī)制之C源碼解析的文章就介紹到這了,更多相關(guān)Mysql鎖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • MySQL 通過(guò)索引優(yōu)化含ORDER BY的語(yǔ)句

    MySQL 通過(guò)索引優(yōu)化含ORDER BY的語(yǔ)句

    合理的建立索引能夠加速數(shù)據(jù)讀取效率,不合理的建立索引反而會(huì)拖慢數(shù)據(jù)庫(kù)的響應(yīng)速度。
    2010-03-03
  • MySQL無(wú)法啟動(dòng)、無(wú)法停止解決方法(安全設(shè)置后容易出現(xiàn))

    MySQL無(wú)法啟動(dòng)、無(wú)法停止解決方法(安全設(shè)置后容易出現(xiàn))

    最近在Win2003上的MySQL出現(xiàn)過(guò)多次正常運(yùn)行時(shí)無(wú)法連接數(shù)據(jù)庫(kù)故障,根本原因就是因?yàn)榘踩O(shè)置以后容易出現(xiàn)的問(wèn)題,其實(shí)很簡(jiǎn)單的解決
    2012-03-03
  • MYSQL索引建立需要注意以下幾點(diǎn)細(xì)節(jié)

    MYSQL索引建立需要注意以下幾點(diǎn)細(xì)節(jié)

    建立MYSQL索引時(shí)需要注意以下幾點(diǎn):建立索引的時(shí)機(jī)/對(duì)于like/對(duì)于有多個(gè)條件的/開(kāi)啟索引緩存/建立索引是有代價(jià)的等等,感興趣的你可以參考下本文,或許可以幫助到你
    2013-03-03
  • 詳解MySQL8.0+常用命令

    詳解MySQL8.0+常用命令

    這篇文章主要介紹了MySQL8.0+常用命令及操作命令,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-10-10
  • 詳解MySQL查詢時(shí)區(qū)分字符串中字母大小寫(xiě)的方法

    詳解MySQL查詢時(shí)區(qū)分字符串中字母大小寫(xiě)的方法

    MySQL默認(rèn)是不區(qū)分大小寫(xiě)的,這個(gè)涉及到collate字符集與校驗(yàn)規(guī)則方面的知識(shí),下面會(huì)講到,這里我們就來(lái)詳解MySQL查詢時(shí)區(qū)分字符串中字母大小寫(xiě)的方法.
    2016-05-05
  • MySQL中EXPLAIN語(yǔ)句及用法實(shí)例

    MySQL中EXPLAIN語(yǔ)句及用法實(shí)例

    我們常常用到explain這個(gè)命令來(lái)查看一個(gè)這些SQL語(yǔ)句的執(zhí)行計(jì)劃,查看該SQL語(yǔ)句有沒(méi)有使用上了索引,下面這篇文章主要給大家介紹了關(guān)于MySQL中EXPLAIN語(yǔ)句及用法的相關(guān)資料,需要的朋友可以參考下
    2022-05-05
  • 詳解Centos7 修改mysql指定用戶的密碼

    詳解Centos7 修改mysql指定用戶的密碼

    本篇文章主要介紹了Centos7 修改mysql指定用戶的密碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧。
    2016-12-12
  • CentOS7 64位下MySQL5.7安裝與配置教程

    CentOS7 64位下MySQL5.7安裝與配置教程

    這篇文章主要介紹了CentOS7 64位下MySQL5.7安裝與配置教程,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-08-08
  • MySQL索引總結(jié)(Index?Type)

    MySQL索引總結(jié)(Index?Type)

    本文主要介紹了MySQL索引總結(jié)(Index?Type),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2023-02-02
  • mysql臨時(shí)表(temporary?table)使用方法詳解

    mysql臨時(shí)表(temporary?table)使用方法詳解

    MySQL臨時(shí)表在很多場(chǎng)景中都會(huì)用到,MySQL內(nèi)部在執(zhí)行復(fù)雜SQL時(shí),需要借助臨時(shí)表進(jìn)行分組、排序、去重等操作,下面這篇文章主要給大家介紹了關(guān)于mysql臨時(shí)表(temporary?table)使用方法的相關(guān)資料,需要的朋友可以參考下
    2024-01-01

最新評(píng)論