国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

MySQL 核心模塊揭秘

數據庫 MySQL
鎖模塊結構的 rec_hash 屬性是個哈希表,分為很多小格子,每個格子管理一個行鎖結構鏈表。latches 屬性用于保證同一時刻只有一個線程讀寫 rec_hash 屬性的同一個格子對應的行鎖結構鏈表,以及同一時刻只有一個線程讀寫同一個表對象的 locks 鏈表。

1. 引言

前面三篇文章,我們分別介紹了 InnoDB 表鎖、行鎖,以及它們的鎖結構。

表鎖結構和行鎖結構是鎖模塊的基礎組成部分,它們就像一塊磚,哪里需要哪里搬。

然而,要蓋房子,光有磚不行,還得有鋼筋、水泥等材料,這些材料就由鎖模塊結構提供。

鎖模塊結構只有一個對象(lock_sys),在 InnoDB 中是全局唯一的。

2. 鎖模塊結構

鎖模塊結構類型為 lock_sys_t,去掉注釋以及兩個無關緊要的屬性之后,簡化如下:

struct lock_sys_t {
  locksys::Latches latches;
  hash_table_t *rec_hash;
  hash_table_t *prdt_hash;
  hash_table_t *prdt_page_hash;
  Lock_mutex wait_mutex;
  srv_slot_t *waiting_threads;
  srv_slot_t *last_slot;
  bool rollback_complete;
  std::chrono::steady_clock::duration n_lock_max_wait_time;
  os_event_t timeout_event;
}

單從屬性數量上看,鎖模塊結構并不復雜,甚至可以說比較簡單。

其實,鎖模塊的復雜性,不在于表鎖結構、行鎖結構,也不在于鎖模塊結構,而是在于各個事務、各種加鎖場景相互交錯導致的錯綜復雜的加鎖結果。

例如,一個事務等待獲得另一個事務持有的鎖,雖然會出現或長或短的等待鏈,但也不算太壞的情況。更壞的情況是出現了環形的等待鏈,也就是出現了死鎖。

如果出現死鎖,我們又需要被動復現死鎖,以解釋形成死鎖的原因,那簡直頭大了。

為了不滑入復雜的深淵,我們就此打住,先來介紹鎖模塊結構的屬性。

鎖模塊結構中有三個類型為 hash_table_t 的屬性,分別是 rec_hash、prdt_hash、prdt_page_hash。

其中,prdt_hash、prdt_page_hash 由謂詞鎖使用。我們并不打算介紹謂詞鎖,忽略這兩個屬性,也就順理成章了。

n_lock_max_wait_time 屬性的值是 MySQL 本次啟動以來,行鎖的最長等待時間。通過以下命令可以查詢到這個屬性的值:

show status like 'innodb_row_lock_time_max';

+--------------------------+-------+
| Variable_name            | Value |
+--------------------------+-------+
| Innodb_row_lock_time_max | 50157 |
+--------------------------+-------+

rollback_complete 屬性,用于 MySQL 啟動過程中,標識從 undo 日志中恢復出來的、需要回滾的事務是否已全部回滾完成。

如果 rollback_complete = false,說明從 undo 日志中恢復出來的、需要回滾的事務還沒有全部回滾完成,InnoDB 會遍歷讀寫事務鏈表(trx_sys->rw_trx_list),釋放這些事務加的表鎖和行鎖。

這些事務全部回滾完成之后,rollback_complete 會被修改為 true。

前面介紹了鎖模塊結構中兩個比較簡單的屬性,剩下的其它屬性,我們分為幾個小節一一介紹。

2.1 誰來管理行鎖結構?

上一篇文章,我們介紹過,事務對多條記錄加行鎖,滿足條件時,可以共用一個行鎖結構。

雖然共用能減少行鎖結構的數量,但是,同一時刻,InnoDB 中可能還是有很多行鎖結構。

這么多行鎖結構,要怎么組織,用到時才能方便、快速的找到呢?

這就需要用到鎖模塊結構的 rec_hash 屬性了。

rec_hash 屬性是個哈希表,它的類型為 hash_table_t,創建鎖模塊對象(lock_sys)之后分配內存:

void lock_sys_create(ulint n_cells)
{
  ...
  // 創建鎖模塊對象,分配內存
  lock_sys = static_cast<lock_sys_t *>(
      ut::zalloc_withkey(...));
  ...
  // 創建哈希表(rec_hash),分配內存
  lock_sys->rec_hash =
    ut::new_<hash_table_t>(n_cells);
  ...
}

lock_sys_create() 由 srv_start() 調用:

dberr_t srv_start(bool create_new_db) {
  ...
  lock_sys_create(srv_lock_table_size);
  ...
}

變量 srv_lock_table_size 在 innodb_init_params() 中賦值,它的值會傳遞給 lock_sys_create() 的參數 n_cells。

static int innodb_init_params() {
  ...
  srv_lock_table_size = 
    5 * (srv_buf_pool_size / UNIV_PAGE_SIZE);
  ...
}

srv_buf_pool_size 是 buffer pool 的大小,UNIV_PAGE_SIZE 是一個數據頁的大小,它們的單位都是字節。

以 buffer pool 大小為 128M、數據頁大小為 16K 為例,變量 srv_lock_table_size 的值計算如下:

// 128M = 134217728 字節
// 16K  = 16384 字節
srv_lock_table_size = 5 * (134217728 / 16384) 
                    = 40960

變量 srv_lock_table_size 的值(40960)最終會傳遞給 lock_sys_create() 的參數 n_cells。用 40960 替換 n_cells 之后如下:

void lock_sys_create(ulint n_cells)
{
  ...
  lock_sys->rec_hash = 
    ut::new_<hash_table_t>(40960);
  ...
}

以上代碼說明 buffer pool 大小為 128M,數據頁大小為 16K 時,鎖模塊結構的 rec_hash 屬性有 40960 個格子。

每個格子都有編號,從 0 開始,一直到 40959。

這些格子并不是用來存儲行鎖結構,而是用來管理行鎖結構,它們的作用相當于線頭,找到了線頭就能牽出一根線。

創建行鎖結構之后,會先根據行鎖結構中那些記錄所屬數據頁的頁號和表空間 ID,計算得到哈希值,再根據哈希值計算得到格子的編號。

多個行鎖結構可能計算得到相同的哈希值,從而得到相同的編號,對應到同一個格子,這些行鎖結構通過各自的 hash 屬性形成一個行鎖結構鏈表。如果我們把這個鏈表看成一根線,這個格子就是這根線的線頭。

計算出格子編號之后,行鎖結構會插入到格子對應的行鎖結構鏈表的最前面。

想要找到某個行鎖結構,也需要根據同樣的規則,計算得到格子編號,再根據編號找到格子,最后遍歷這個格子對應的行鎖結構鏈表,以找到目標行鎖結構。

2.2 誰來保護表鎖和行鎖結構?

前面我們介紹了 rec_hash 是個哈希表,分為很多格子,每個格子管理一個行鎖結構鏈表。同一個鏈表的所有行鎖結構,計算得到的哈希值相同。

事務加行鎖時,會優先考慮共用已有的行鎖結構,這就要先找到一個可以共用的行鎖結構。

首先,需要找到 rec_hash 的某個格子。

然后,遍歷這個格子對應的行鎖結構鏈表,并根據共用條件,判斷某個行鎖結構是否可以共用。

事務加行鎖時,如果生成了新的行鎖結構,需要找到 rec_hash 的某個格子,把行鎖結構插入到這個格子對應的行鎖結構鏈表的最前面。

事務提交或回滾時,釋放所有行鎖,需要找到每個鎖結構在哪個格子對應的行鎖結構鏈表中,并從鏈表中刪除這個行鎖結構。

事務加表鎖時,會遍歷這個表對象的 locks 鏈表,以判斷可以立即獲得表鎖,還是需要進入等待狀態。

事務提交或回滾時,釋放所有表鎖,需要從每個表對象的 locks 鏈表中刪除這個表鎖結構。

多個事務執行上面這些操作,可能會同時讀寫 rec_hash 中某個格子對應的行鎖結構鏈表,也可能同時讀寫某個表對象的 locks 鏈表。

為了避免并發操作同時讀寫同一個行鎖結構鏈表、或者同時讀寫同一個表對象的 locks 鏈表出現沖突,需要有個什么東西,來限制同一時刻只有一個事務讀寫某個行鎖結構鏈表、或者某個表對象的 locks 鏈表。

于是,就有了鎖模塊結構的 latches 屬性,它的類型為 locksys::Latches。

class Latches {
 private:
  ...
  Unique_sharded_rw_lock global_latch;
  Page_shards page_shards;
  Table_shards table_shards;
  ...
}

latches 也是一個對象,有三個屬性,分別為 global_latch、page_shards、table_shards。

事務提交或回滾時,釋放所有行鎖和表鎖會用到 global_latch。

事務加行鎖時,會用到 page_shards。

事務加表鎖時,會用到 table_shards。

page_shards、table_shards 的類型分為 Page_shards、Table_shards,定義如下:

static constexpr size_t SHARDS_COUNT = 512;

class Page_shards {
  ...
  Padded_mutex mutexes[SHARDS_COUNT];
  ...
}

class Table_shards {
  ...
  Padded_mutex mutexes[SHARDS_COUNT];
  ...
}

Page_shards 的 mutexes 屬性是個數組,有 512 個元素。

有新的行鎖結構需要加入某個行鎖結構鏈表,或者需要遍歷某個行鎖結構鏈表以找到目標行鎖結構時,會根據行鎖結構中那些記錄所屬數據頁的頁號和表空間 ID,計算得到哈希值,再根據哈希值計算得到數組下標,到 mutexes 數組中拿到下標對應的互斥量,就可以保護需要讀寫的行鎖結構鏈表了。

Table_shards 的 mutexes 屬性也是個數組,同樣有 512 個元素。

某個表對象的 locks 鏈表需要保護時,會直接用表 ID 對 512 取模(table_id % 512),得到的結果作為數組下標,到 mutexes 數組中拿到下標對應的互斥量,就可以保護這個表對象的 locks 鏈表了。

2.3 鎖等待了怎么辦?

鎖模塊結構中,有三個屬性和鎖等待相關,分別是 wait_mutex、waiting_threads、last_slot,它們的初始化代碼如下:

void lock_sys_create(ulint n_cells)
{
  ulint lock_sys_sz;
  // 鎖模塊結構占用的內存大小
  // 加上 waiting_threads 指向的內存區域的大小
  // 因為這兩部分要一起分配內存
  lock_sys_sz = sizeof(*lock_sys) 
              + srv_max_n_threads 
              * sizeof(srv_slot_t);
  ...
  void *ptr = &lock_sys[1];
  lock_sys->waiting_threads = 
    static_cast<srv_slot_t *>(ptr);
  // 初始化時
  // last_slot 和 waiting_threads 指向同一個位置
  lock_sys->last_slot = 
    lock_sys->waiting_threads;

  mutex_create(LATCH_ID_LOCK_SYS_WAIT, 
    &lock_sys->wait_mutex);
  ...
}

waiting_threads 屬性是個指針,它指向一片內存區域,這片內存區域分為 srv_max_n_threads 個 slot,每個 slot 存放一個 srv_slot_t 對象。

srv_max_n_threads 在 innodb_init_params() 中賦值,硬編碼為 102400。

也就是說,waiting_threads 屬性指向的內存區域,最多可以存放 102400 個 srv_slot_t 對象。

圖片如果某個事務不能立即獲得鎖(表鎖或行鎖),就會在這片內存區域中找到一個空閑的 slot,構造一個包含該事務以及鎖信息的 srv_slot_t 對象放入這個 slot,并標記這個 slot 為已使用狀態。

last_slot 屬性也是個指針,初始化時,和 waiting_threads 屬性指向相同的內存地址。

隨著不斷有事務進入鎖等待狀態、以及處于鎖等待狀態的事務獲得鎖,last_slot 會不斷變化。

不過,不管怎么變化,last_slot 始終遵循一個原則,就是它指向的那個 slot,以及之后的所有 slot 都處于空閑狀態。

為什么需要 last_slot?

因為后臺線程檢查鎖等待是否超時,會從后往前遍歷 waiting_threads 屬性指向的內存區域。

如果沒有 last_slot,每次遍歷都需要從最后一個 slot 開始,到第一個 slot 為止,檢查每個 slot 對應的鎖等待是否超時。

然而,通常情況下,waiting_threads 屬性指向的內存區域中的 102400 個 slot,其中大部分都是空閑的。

空閑 slot 沒有被正在等待鎖的事務占用,實際上不需要檢查鎖等待是否超時。

如果沒有 last_slot,每次檢查鎖等待是否超時,都要遍歷所有 slot,顯然很浪費時間。

為了提升檢查鎖等待超時的效率,只需要遍歷已使用狀態的 slot 就可以了,這就需要有個東西來標識哪個范圍內的 slot 是已使用狀態,于是,就有了 last_slot。

有一點需要說明,如果某個事務曾經進入過鎖等待狀態,占用了某個 slot。某一輪檢查鎖等待超時之前,這個事務獲得了鎖,又會把它占用的那個 slot 重置為空閑狀態。

所以,last_slot 之前的那些 slot,并不全部是已使用狀態,也有一些是空閑的,但是這個數量應該不會很多,遍歷這些少量的空閑 slot,也不會浪費太多時間。

介紹完 waiting_threads、last_slot,終于輪到 wait_mutex 屬性了。

從屬性名上看,wait_mutex 屬性顯然是個互斥量。

多個事務同時讀寫 last_slot 屬性,可能造成沖突,這就需要有個東西來保證同一時刻只有一個線程讀寫 last_slot 屬性,于是就有了 wait_mutex。

2.4 那就發個鎖等待通知

事務想要加鎖(表鎖或行鎖),如果發生了鎖等待,新出現的鎖等待,和原來那些鎖等待攪和在一起,有可能會出現死鎖。

為了及時發現死鎖,事務進入鎖等待狀態之前,會觸一個事件,通知后臺線程出現了鎖等待。

這個事件就保存在鎖模塊結構的 timeout_event 屬性中。

監聽 timeout_event 事件的后臺線程收到通知之后,就會開始檢查是否發生了死鎖。如果檢查發現了死鎖,就及時解決。

3. 總結

鎖模塊結構的 rec_hash 屬性是個哈希表,分為很多小格子,每個格子管理一個行鎖結構鏈表。

latches 屬性用于保證同一時刻只有一個線程讀寫 rec_hash 屬性的同一個格子對應的行鎖結構鏈表,以及同一時刻只有一個線程讀寫同一個表對象的 locks 鏈表。

waiting_threads 屬性指向一片分為 102400 個 slot 的內存區域,每個等待獲得鎖的事務會占用其中一個 slot。

last_slot 屬性用于減少檢查鎖等待超時需要遍歷的 slot 數量,提升效率。

wait_mutex 屬性用于保證同一時刻只有一個線程讀寫 last_sot 屬性。

timeout_event 屬性用于發生鎖等待時,通知后臺線程及時檢查是否出現了死鎖。

作者:操盛春,愛可生技術專家,公眾號『一樹一溪』作者,專注于研究 MySQL 和 OceanBase 源碼。

責任編輯:武曉燕 來源: 愛可生開源社區
相關推薦

2024-05-15 09:05:42

MySQL核心模塊

2024-04-03 08:20:53

MySQL核心模塊

2024-08-28 08:50:11

MySQL核心模塊

2024-03-27 13:33:00

MySQLInnoDB事務

2024-08-07 14:58:00

MySQL釋放鎖核心模塊

2024-10-30 10:38:08

2024-10-16 11:11:51

隔離InnoDB死鎖

2024-05-29 10:17:01

2024-09-04 08:44:18

MySQL核心模塊

2025-02-26 08:26:38

2024-11-05 10:52:07

2010-01-26 14:04:02

2025-01-17 08:17:55

2009-07-21 09:06:14

開發團隊Windows 7

2022-07-12 10:38:25

分布式框架

2023-08-24 10:33:19

serviceexportsinfo類

2024-05-08 16:54:21

Python編程開發

2021-06-21 17:00:05

云計算Hologres云原生

2025-08-20 16:21:30

2019-01-23 10:42:21

華為云
點贊
收藏

51CTO技術棧公眾號

国产超碰精品在线观看| 日韩欧美一级| 亚洲国产专区校园欧美| 精品久久国产字幕高潮| 神马影院午夜我不卡| 国产成人免费av一区二区午夜 | 亚洲动漫第一页| 国产综合在线看| 午夜成人影视| 99精品久久久久久| 日本一区二区三区在线观看视频| 欧美日韩视频免费在线观看| 国产经典久久久| 欧美久久在线观看| 一级在线免费视频| 中国国产一级毛片| 国产亚洲一区二区在线观看| 成人蜜桃视频| 97色婷婷成人综合在线观看| 亚洲欧洲国产日韩| 亚洲国产综合自拍| 一区二区三区四区高清视频| 91精品国产高清一区二区三区蜜臀| 久久成人综合| 亚洲综合小说| 日韩主播视频在线| 成人自拍视频在线| 久久久久久久久久久久久女国产乱| 亚洲视频 欧洲视频| 高潮白浆女日韩av免费看| 欧美日产国产精品| 亚洲人成亚洲人成在线观看| 色婷婷久久av| 日本欧美精品在线| 成人影片在线播放| 中文字幕色一区二区| 丰满人妻中伦妇伦精品app| xxxx影院| 99se视频在线观看| 亚洲一区站长工具| 全球av集中精品导航福利| 亚洲女同另类| 久久99精品国产.久久久久久| 久久久精品国产免大香伊| 五月激情六月综合| 精品盗摄一区二区三区| 免费av在线一区| 91热福利电影| 青青草原网站在线观看| 国产三级三级三级看三级| 深夜福利在线看| xx欧美视频| 国产一区二区三区四区大秀| 亚洲欧美bt| 久久综合精品国产一区二区三区| 中文字幕一区二区三区免费视频| 中文字幕乱码日本亚洲一区二区| 日本一区午夜艳熟免费| 蜜桃在线一区二区三区| 精品蜜桃传媒| 亚洲国内精品| 成人在线看片| 亚洲国产99| 精品免费视频123区| 午夜激情一区| 国产一区二区三区无遮挡 | 欧美做受高潮电影o| 青青草超碰在线| 91色porny| 国产一区二区三区乱码| 免费高清在线一区| 亚洲欧洲免费无码| 国产午夜精品久久久| 欧美日本精品在线| 高清国语自产拍免费一区二区三区 | 国产精品白浆| 深夜福利视频一区| 亚洲成人av福利| 成年女人的天堂在线| 欧美午夜激情视频| 污视频在线观看网站| 91久久久免费一区二区| 美女的诞生在线观看高清免费完整版中文 | 亚洲免费视频成人| 欧美一级片在线| 777午夜精品福利在线观看| 欧美精品v日韩精品v国产精品| 日本免费观看网站| 在线观看免费版| 久久综合五月婷婷| 日本女优在线视频一区二区| 亚洲男人天堂一区| 国产性色av一区二区| 成人av网站观看| 日日躁夜夜躁aaaabbbb| 丁香花视频在线观看| 不卡一区2区| av在线不卡免费看| 88在线观看91蜜桃国自产| **欧美日韩vr在线| 五月天综合婷婷| 黄色av网站在线免费观看| 亚洲日本va| 激情综合网最新| 在线观看国产91| 2021久久精品国产99国产精品| 国产卡一卡二在线| 成人在线二区| 亚洲精品进入| 国产精品123| 91精品国产综合久久精品性色| 欧美专区第一页| 精品人妻少妇一区二区| 五月婷婷丁香色| 色综合网站在线| 亚洲校园激情春色| 国产美女高潮久久白浆| 久久三级视频| 久热精品在线播放| 精品国产免费一区二区三区香蕉| 成人激情视屏| 91国产丝袜在线放| 91天堂素人约啪| √新版天堂资源在线资源| 中文字幕在线日韩| 99久久激情| 无码人妻少妇伦在线电影| 亚洲三级在线观看| 日本免费在线观看| 欧美日韩aaaaa| 岛国av免费在线观看| 欧洲成人性视频| 成人丝袜高跟foot| 欧洲亚洲精品久久久久| 亚洲福利二区| 蜜桃视频www网站在线观看| 国产一区日韩一区| 亚洲一二三四久久| 97香蕉超级碰碰久久免费软件 | 欧美日韩在线一| 欧美性淫爽ww久久久久无| 欧美第一在线视频| 亚洲人成无码网站久久99热国产| 亚洲欧美国产高清| 日本在线一区二区三区| 欧美在线激情| 欧美性色视频在线| 国产精品永久| 久久久高清一区二区三区| 亚洲女人被黑人巨大进入al| 欧美精品在线一区| av国产在线观看| 91精品国产福利在线观看麻豆| 亚洲美腿欧美偷拍| 性欧美在线看片a免费观看| 久久精品.com| 国产精品一区三区在线观看| 粉嫩在线一区二区三区视频| 精品视频久久久| 中日韩在线视频| xxx性欧美| 男女男精品视频网| 欧美成人精品福利| 日韩精品久久一区| 人交獸av完整版在线观看| 国产精品久久久亚洲一区| 欧美日免费三级在线| 国产精品久久久久久久久久久久午夜片 | 久久久久久久久电影| 一本大道熟女人妻中文字幕在线 | 在线观看国产麻豆| 久久99久久亚洲国产| 成人精品视频一区二区三区 | 国产亚洲精品bt天堂精选| 国产99在线| 亚洲欧美日产图| 亚洲精品国精品久久99热| 久久中文字幕导航| 777午夜精品免费视频| 你懂的国产精品| 手机在线观看av| 天天干在线影院| 成人av免费看| 日韩欧美不卡在线观看视频| 一本久道久久久| 女海盗2成人h版中文字幕| 亚洲不卡中文字幕| 亚洲国产天堂久久综合网| 日韩中文字幕91| 色豆豆成人网| 传媒在线观看| 400部精品国偷自产在线观看| 国产精品福利久久久| 三级精品视频久久久久| 国产精品丝袜久久久久久app| 午夜亚洲福利| 欧美日韩国产v| 免费h片在线| 欧美极品欧美精品欧美图片| 欧美黑人xxxxx|