精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

MySQL索引原理與應用:索引類型,存儲結構與鎖

數據庫 MySQL
在數據結構與算法--索引一節中,我們討論了 B+Tree, LSM-Tree 這樣的文件索引以及全文索引的基礎算法,本文則會針對文件索引在關系型數據庫中的實際應用進行探討。

在數據結構與算法--索引 https://url.wx-coder.cn/O07eI 一節中,我們討論了 B+Tree, LSM-Tree 這樣的文件索引以及全文索引的基礎算法,本文則會針對文件索引在關系型數據庫中的實際應用進行探討。

索引(Index)是幫助數據庫系統高效獲取數據的數據結構,而數據庫索引本質上是以增加額外的寫操作,與用于維護索引數據結構的存儲空間為代價的,用于提升數據庫中數據檢索效率的數據結構。索引可以幫助我們快速地定位到數據而不需要每次搜索的時候都遍歷數據庫中的每一行。當然,索引不是建立的越多、越長越好,因為索引除了占用空間之外,對后續數據庫的增加、刪除、修改都有額外的操作來更新索引。一般來說,小表使用全表掃描更快,中大表才使用索引,而超級大表索引基本無效,我們可能需要借助獨立的全文索引系統;MySQL 自帶的全文索引只能用于 InnoDB、MyISAM ,并且只能對英文進行全文檢索,一般使用 ES,Solr 這樣的全文索引引擎。

索引類型

從索引的實現上,我們可以將其分為聚集索引與非聚集索引,或稱輔助索引或二級索引,這兩大類;從索引的實際應用中,又可以細分為普通索引、唯一索引、主鍵索引、聯合索引、外鍵索引、全文索引這幾種。

InnoDB 可以看做是聚集索引,因為它的 B+ 樹的葉結點包含了完整的數據記錄。InnoDB 的數據文件本身就是索引文件,表數據文件本身就是按 B+Tree 組織的一個索引結構,這棵樹的葉節點 data 域保存了完整的數據記錄。這個索引的 key 是數據表的主鍵,因此 InnoDB 表數據文件本身就是主索引。InnoDB 的輔助索引 data 域存儲相應記錄主鍵的值而不是地址。換句話說,InnoDB 的所有輔助索引都引用主鍵作為 data 域。

而 MyISAM 方式 B+ 樹的葉結點只是存儲了數據的地址,故稱為非聚集索引。MyISAM 引擎使用 B+Tree 作為索引結構,葉節點的 data 域存放的是數據記錄的地址;在 MyISAM 中,主索引和輔助索引(Secondary key)在結構上沒有任何區別,只是主索引要求 key 是唯一的,而輔助索引的 key 可以重復。

在 InnoDB 中,又有聚簇索引和普通索引之分,聚簇索引根據主鍵來構建,葉子節點存放的是該主鍵對應的這一行記錄,根據主鍵查詢可以直接利用聚簇索引定位到所在記錄。而普通索引根據申明這個索引時候的列來構建,葉子節點存放的是這一行記錄對應的主鍵的值,根據普通索引查詢需要先在普通索引上找到對應的主鍵的值,然后根據主鍵值去聚簇索引上查找記錄,俗稱回表。如果我們查詢一整行記錄的話,一定要去聚簇索引上查找,而如果我們只需要根據普通索引查詢主鍵的值,由于這些值在普通索引上已經存在,所以并不需要回表,這個稱為索引覆蓋,在一定程度上可以提高查詢效率。

普通索引中還有唯一索引和聯合索引兩個特例,唯一索引在插入和修改的時候會校驗該索引對應的列的值是否已經存在,聯合索引將兩個列的值按照申明時候的順序進行拼接后在構建索引。

數據行并不是存儲引擎管理的最小存儲單位,索引只能夠幫助我們定位到某個數據頁,每一次磁盤讀寫的最小單位為也是數據頁,而一個數據頁內存儲了多個數據行,我們需要了解數據頁的內部結構才能知道存儲引擎怎么定位到某一個數據行,可以參考 MySQL 存儲管理 https://url.wx-coder.cn/IF5HH 系列。

索引選擇性

對索引列和字符串前綴長度,都參考選擇性(Selectivity)這個指標來確定:選擇性定義為不重復的索引值和數據總記錄條數的比值,其選擇性越高,那么索引的查詢效率也越高,譬如對于性別這種參數,建立索引根本沒有意義。 

  1. Index Selectivity = Cardinality / #T 

顯然選擇性的取值范圍為 (0, 1],選擇性越高的索引價值越大,這是由 B+Tree 的性質決定的。在實際的數據庫中,我們可以通過以下語句來計算某列的選擇性: 

  1. SELECT count(DISTINCT(title))/count(*) AS Selectivity FROM titles; 

主鍵

在 InnoDB 內部,表數據是優化主鍵快速查詢而排列分布的,其查找速度是最快的,該索引中鍵值的邏輯順序決定了表中相應行的物理順序。即使表中沒有適合做主鍵的列,也推薦采用一個自動增長的整數主鍵(代理鍵),那么這個表在增加數據的時候是順序存放的,而且后續在別的表參考該外鍵查詢的時候也會得到優化。

如果在創建表時沒有顯式地定義主鍵(Primary Key),則 InnoDB 存儲引擎會按如下方式選擇或創建主鍵:

  • 首先表中是否有非空的唯一索引(Unique NOT NULL),如果有,則該列即為主鍵。
  • 不符合上述條件,InnoDB 存儲引擎自動創建一個 6 個字節大小的指針,用戶不能查看或訪問。

主鍵的選擇

在分布式 ID https://url.wx-coder.cn/tQ5eH 一文中我們討論過分布式場景下的分布式 ID 的選擇策略,而在數據庫中,我們同樣會有這樣的考量。首先,MySQL 官方有明確的建議主鍵要盡量越短越好,36 個字符長度的 UUID 不符合要求;如果主鍵是一個很長的字符串并且建了很多普通索引,將造成普通索引占有很大的物理空間。并且主鍵***是順序遞增的,否則在 InnoDB 引擎下,UUID 的無序性可能會引起數據位置頻繁變動,嚴重影響性能。

自增 ID 在插入的時候可以保證相鄰的兩條記錄可能在同一個數據塊,而訂單號這樣的業務相關的連續性設計上可能沒有自增 ID 好,導致連續插入可能在多個數據塊,增加了磁盤讀寫次數。

  • 唯一性:自增 ID 很容易會被暴力破解,數據遷移的時候,特別是發生表格合并這種操作的時候,會不可避免地存在沖突。UUID 則能夠保證唯一性,徹底避免沖突。
  • 鍵長度:自增字段的長度較 UUID 小很多,這會對檢索的性能有較大影響。Innodb 引擎進行數據檢索時,也是先根據索引找到主鍵,然后根據主鍵找到記錄;這樣在主鍵長度短的情況下,會有較好的讀性能。
  • 并發性:自增 ID 并且高并發的情況下,競爭自增鎖會降低數據庫的吞吐能力。UUID 則能夠在應用層生成 UUID,提高數據庫的吞吐能力。
  • 數據庫索引:InnoDB 中表數據是按照主鍵順序存放的,在寫入數據時候如果發生了隨機 IO,那么就會頻繁地移動磁盤塊。當數據量大的時候,寫的短板將非常明顯。自增 ID 中新增的數據可以默認按序排列,對于性能有很大的提升;UUID 則主鍵之間沒有順序規律。

主鍵與唯一索引

主鍵就是唯一索引,但是唯一索引不一定是主鍵,唯一索引可以為空,但是空值只能有一個,主鍵不能為空。對于單列索引,要求該列所有數據都不相同,但允許有 NULL 值;對于多列的聯合索引,要求這些列的組合是唯一的。唯一索引其本身既可以作為索引,實際中也可以用以產生數據約束,防止增加或者修改后產生相同數據,從而保證數據的完整性。

對于字符串類型,可以指定索引前綴長度(且對于 BLOB/TEXT 前綴長度參數是必須的),在 InnoDB 表中其前綴長度最長是 767 bytes,且參數 M 是用 bytes 計量的。所以太長的字符串,建立 B+Tree 索引浪費比較大,這時候用手動模擬 HASH 索引是個方法,不過這種方式對字符串無法靈活的使用前綴方式查詢(例如 LIKE 這類的操作)。

聯合索引

單列索引指的是在表上為某一個字段建立的索引,一般索引的創建選擇整型或者較小的定長字符串將更有利于效率的提升。聯合索引指的是多個字段按照一定順序組織的索引。以索引 (name, city, gender) 為例,其首先是按照 name 字段順序組織的,當 name 字段的值相同時(如 Bush),其按照 city 字段順序組織,當 city 字段值相同時,其按照 gender 字段組織。由于聯合索引上通過多個列構建索引,有時候我們可以將需要頻繁查詢的字段加到聯合索引里面,譬如經常需要根據 name 查找 age 我們可以建一個 name 和 age 的聯合索引。

常見的條件聯合包括了 WHERE 條件聯合與 ORDER BY 條件聯合;所謂 WHERE 條件聯合指的是,對于 WHERE 條件中的等值條件,其字段使用與聯合索引的字段一致(順序可以不一致)。

ORDER BY 聯合指的是如果 ORDER BY 后面的字段是聯合索引覆蓋 where 條件之后的一個字段,由于索引已經處于有序狀態,MySQL 就會直接從索引上讀取有序的數據,然后在磁盤上讀取數據之后按照該順序組織數據,從而減少了對磁盤數據進行排序的操作。即對于未覆蓋 ORDER BY 的查詢,其有一項 Creating sort index,即為磁盤數據進行排序的耗時***;對于覆蓋 ORDER BY 的查詢,其就不需要進行排序,而其耗時主要體現在從磁盤上拉取數據的過程。

前綴索引

MySQL 的前綴索引可以分為三類:聯合索引前綴,like 前綴和字符串前綴。

聯合索引前綴與最左匹配(Leftmost Prefix)

聯合索引前綴指的是在建立多列索引的時候,必須按照從左到右的順序使用全部或部分的索引列,才能充分的使用聯合索引,比如:(col1, col2, col3) 使用 (col1)、(col1, col2)、(col1, col2, col3) 有效。在查詢語句中會一直向右匹配直到遇到范圍查詢 (>,<,BETWEEN,LIKE) 就停止匹配,其后的索引列將不會使用索引來優化查找了。

以 (name, city, interest) 三個字段聯合的索引為例,如果查詢條件為 where name='Bush'; 那么就只需要根據 B+樹定位到 name 字段***個 Bush 所在的值,然后順序掃描后續數據,直到找到***個不為 Bush 的數據即可,掃描過程中將該索引片的數據 id 記錄下來,***根據 id 查詢聚簇索引獲取結果集。同理對于查詢條件為 where name='Bush' and city='Chicago'; 的查詢,MySQL 可以根據聯合索引直接定位到中間灰色部分的索引片,然后獲取該索引片的數據 id,***根據 id 查詢聚簇索引獲取結果集。

由此我們可以得出聯合索引前綴的注意點:

  • 無法跨越字段使用聯合索引,如 where name='Bush' and interest='baseball';,對于該查詢,name 字段是可以使用聯合索引的***個字段過濾大部分數據的,但是對于 interest 字段,其無法通過 B+ 樹的特性直接定位第三個字段的索引片數據,比如這里的 baseball 可能分散在了第二條和第七條數據之中。最終,interest 字段其實進行的是覆蓋索引掃描。
  • 對于非等值條件,如 >、<、!= 等,聯合索引前綴對于索引片的過濾只能到***個使用非等值條件的字段為止,后續字段雖然在聯合索引上也無法參與索引片的過濾。這里比如 where name='Bush' and city>'Chicago' and interest='baseball';,對于該查詢條件,首先可以根據 name 字段過濾索引片中***個字段的非 Bush 的數據,然后根據聯合索引的第二個字段定位到索引片的 Chicago 位置,由于其是非等值條件,這里 MySQL 就會從定位的 Chicago 往下順序掃描,由于 interest 字段是可能分散在索引第三個字段的任何位置的,因而第三個字段無法參與索引片的過濾。

因此 B-Tree 的列順序非常重要,上述使用規則都和列順序有關。對于實際的應用,一般要根據具體的需求,創建不同列和不同列順序的索引。假設有索引 Index(A,B,C): 

  1. # 使用索引  
  2. A>5 AND A<10 - 最左前綴匹配  
  3. A=5 AND B>6 - 最左前綴匹配  
  4. A=5 AND B=6 AND C=7 - 全列匹配  
  5. A=5 AND B IN (2,3) AND C>5 - 最左前綴匹配,填坑  
  6. # 不能使用索引  
  7. B>5 - 沒有包含最左前綴  
  8. B=6 AND C=7 - 沒有包含最左前綴  
  9. # 使用部分索引  
  10. A>5 AND B=2 - 使用索引 A 列  
  11. A=5 AND B>6 AND C=2 - 使用索引的 A 和 B 列 

使用索引對結果進行排序,需要索引的順序和 ORDER BY 子句中的順序一致,并且所有列的升降序一致(ASC/DESC)。如果查詢連接了多個表,只有在 ORDER BY 的列引用的是***個表才可以(需要按序 JOIN)。 

  1. # 使用索引排序  
  2. ORDER BY A - 最左前綴匹配  
  3. WHERE A=5 ORDER BY B,C - 最左前綴匹配  
  4. WHERE A=5 ORDER BY B DESC - 最左前綴匹配  
  5. WHERE A>5 ORDER BY A,B - 最左前綴匹配  
  6. # 不能使用索引排序  
  7. WHERE A=5 ORDER BY B DESC,C ASC - 升降序不一致  
  8. WHERE A=5 ORDER BY B,D - D 不在索引中  
  9. WHERE A=5 ORDER BY C - 沒有包含最左前綴  
  10. WHERE A>5 ORDER BY B,C - ***列是范圍條件,無法使用 BC 排序  
  11. WHERE A=5 AND B IN(1, 2) ORDER BY C - B 也是范圍條件,無法用 C 排序  

like 前綴

對于 like 前綴,其是指在使用 like 查詢時,如果使用的表達式為 first_name like 'rMq%';那么其是可以用到 first_name 字段的索引的。但是對于 first_name like '%Chu%';,其就無法使用 first_name 的索引。對于 like 前綴,MySQL 底層實際上是使用了一個補全策略來使用索引的,比如這里 first_name like 'rMq%';,MySQL 會將其補全為兩條數據:rMqAAAAA 和 rMqzzzzz,后面補全部分的長度為當前字段的***長度。在使用索引查詢時,MySQL 就使用這兩條數據進行索引定位,***需要的結果集就是這兩個定位點的中間部分的數據。如下是使用 like 前綴的一個示意圖:

字符串前綴

字符串前綴索引指的是只取字符串前幾個字符建立的索引。在進行查詢時,如果一個字段值較長,那么為其建立索引的成本將非常高,并且查詢效率也比較低,字符串前綴索引就是為了解決這一問題而存在的。字符串前綴索引主要應用在兩個方面:

  • 字段前綴部分的選擇性比較高;
  • 字段整體的選擇性不太大(如果字段整體選擇性比較大則可以使用哈希索引)。

譬如為 first_name 字段建立了長度為 4 的前綴索引,可以看到,如果查詢使用的是 where first_name='qWhNIZqxcbD';,那么 MySQL 首先會截取等值條件的前四個字符,然后將其與字符串前綴索引進行比較,從而定位到前綴為"qWhN"的索引片,然后獲取該索引片對應的磁盤數據,***將獲取的磁盤數據的 first_name 字段與查詢的等值條件的值進行比較,從而得到結果集。

字符串前綴索引最需要注意的一個問題是如何選擇前綴的長度,長度選擇合適時,前綴索引的過濾性將和對整個字段建立索引的選擇性幾乎相等。這里我們就需要用到前面講解的關于字段選擇性的概念,即字段選擇性為對該字段分組之后,數據量***的組的數據量占總數據量的比例。這里選擇前綴長度時,可以理解為,前綴的選擇性為按照前綴分組之后,數據量***的組占總數據量的比例。如下表所示為計算前綴長度的 SQL 公式: 

  1. select count(*) as cnt, first_name as perf from actor group by perf ORDER BY cnt desc limit 10;    -- 0  
  2. select count(*) as cnt, left(first_name, 2) as perf from actor group by perf ORDER BY cnt desc limit 10;    -- 2  
  3. select count(*) as cnt, left(first_name, 3) as perf from actor group by perf ORDER BY cnt desc limit 10;    -- 3  
  4. select count(*) as cnt, left(first_name, 4) as perf from actor group by perf ORDER BY cnt desc limit 10;    -- 4  

其他索引

覆蓋索引

覆蓋索引指的是對于查詢中使用的除去參與索引過濾掃描的所有字段將其加入到該查詢所使用的索引尾部的索引。覆蓋索引掃描的優點在于由于查詢中所使用的所有字段都在同一索引的字段,因而在進行查詢時只需要在索引中獲取相關數據即可,而不需要回磁盤掃描相應的數據,從而避免了查詢中最耗時的磁盤 I/O 讀取。對于如下查詢: 

  1. select a, b, c from t where a='a' and b='b'

該查詢中如果建立聯合索引(a, b, c),那么這就是使用了覆蓋掃描的索引,因為對于該查詢,可以使用索引的前兩個字段 a 和 b 根據 where 條件進行索引片的過濾,對過濾后的索引片直接在索引中讀取 a, b, c 三個字段的值即可,而無需回表掃描。

三星索引

三星索引指的是對于一個查詢,設立了三個通用的索引條件滿足的條件,建立的索引對于特定的查詢每滿足一個條件就表示該索引得到一顆星,當該索引得到三顆星時就表示該索引對于該查詢是一個三星索引。三星索引是對于特定查詢的***索引,建立三星索引的條件如下:

  • 取出所有的等值謂詞的列 (WHERE COL=…) 作為索引開頭的列;
  • 將 ORDER BY 中的列加入到索引中;
  • 將查詢語句中剩余的列加入到索引中,將易變得列放到***以降低更新成本。

譬如對于如下的查詢,索引 (first_name, last_name, email) 就是一個三星索引: 

  1. SELECT first_name, last_name, email FROM user WHERE first_name = 'aa' ORDER BY last_name; 

三星索引的創建過程可以發現如下規律:

  • 覆蓋等值謂詞條件,如 first_name,可以過濾大部分的索引片數據;
  • 覆蓋 order by 字段可以避免對結果集的排序,如 last_name;
  • 覆蓋其余字段可以避免回磁盤讀取數據,即使用了覆蓋索引掃描,如 email。

索引存儲結構

MySQL 查詢的時候會先通過索引定位到對應的數據頁,然后檢測數據頁是否在緩沖池內,如果在就直接返回,如果不在就去聚簇索引中通過磁盤 IO 讀取對應的數據頁并放入緩沖池。一個數據頁會包含多個數據行。緩存池通過 LRU 算法對數據頁進行管理,也就是最頻繁使用的數據頁排在列表前面,不經常使用的排在隊尾,當緩沖池滿了的時候會淘汰掉隊尾的數據頁。從磁盤新讀取到的數據頁并不會放在隊列頭部而是放在中間位置,這個中間位置可以通過參數進行修。緩沖池也可以設置多個實例,數據頁根據哈希算法決定放在哪個緩沖池。

在 MySQL 存儲結構一文中,我們討論過 MySQL 數據頁的存儲結構。

Memory Architecture | 內存架構

InnoDB 的內存主要有以下幾個部分組成:緩沖池 (buffer pool)、重做日志緩沖池(redo log buffer)以及額外的內存池(additional memory pool),如下圖所示:

其中緩沖池占***塊內存,用來緩存各自數據,數據文件按頁(每頁 16K)讀取到緩沖池,按最近最少使用算法(LRU)保留緩存數據。緩沖池緩沖的數據類型有:數據頁、索引頁、插入緩沖、自適應哈希索引、鎖信息、數據字典信息等,其中數據頁和索引頁占了絕大部分內存。日志緩沖將重做日志信息先放入這個緩沖區,然后按一定頻率(默認為 1s)將其刷新至重做日志文件。

InnoDB 通過一些列后臺線程將相關操作進行異步處理,同時借助緩沖池來減小 CPU 和磁盤速度上的差異。當查詢的時候會先通過索引定位到對應的數據頁,然后檢測數據頁是否在緩沖池內,如果在就直接返回,如果不在就去聚簇索引中通過磁盤 IO 讀取對應的數據頁并放入緩沖池。一個數據頁會包含多個數據行。緩存池通過 LRU 算法對數據頁進行管理,也就是最頻繁使用的數據頁排在列表前面,不經常使用的排在隊尾,當緩沖池滿了的時候會淘汰掉隊尾的數據頁。從磁盤新讀取到的數據頁并不會放在隊列頭部而是放在中間位置,這個中間位置可以通過參數進行修。緩沖池也可以設置多個實例,數據頁根據哈希算法決定放在哪個緩沖池。

Storage Architecture | 存儲結構

InnoDB 存儲引擎的邏輯存儲結構和 Oracle 大致相同,所有數據都被邏輯地存放在一個空間中,我們稱之為表空間(tablespace)。表空間又由段(segment)、區(extent)、頁(page)組成。頁在一些文檔中有時也稱為塊(block),1 extent = 64 pages,InnoDB 存儲引擎的邏輯存儲結構大致如圖所示:

表空間作為存儲結構的***層,所有數據都存放在表空間中,默認情況下用一個共享表空間 ibdata1 ,如果開啟了 innodb_file_per_table 則每張表的數據將存儲在單獨的表空間中,也就是每張表都會有一個文件,

表空間由各個段構成,InnoDB 存儲引擎由索引組織的,而索引中的葉子節點用來記錄數據,存儲在數據段,而非葉子節點用來構建索引,存儲在索引段。區是由連續的頁組成,任何情況下一個區都是 1MB,一個區中可以有多個頁,每個頁默認為 16KB ,所以默認情況下一個區中可以包含 64 個連續的頁,頁的大小是可以通過 innodb_page_size 設置,頁中存儲的是具體的行記錄。一行記錄最終以二進制的方式存儲在文件里。

從物理意義上來看,InnoDB 表由共享表空間、日志文件組(更準確地說,應該是 Redo 文件組)、表結構定義文件組成。若將 innodb_file_per_table 設置為 on,則每個表將獨立地產生一個表空間文件,以 ibd 結尾,數據、索引、表的內部數據字典信息都將保存在這個單獨的表空間文件中。表結構定義文件以 frm 結尾,這個是與存儲引擎無關的,任何存儲引擎的表結構定義文件都一樣,為 .frm 文件。

Process Architecture | 進程架構

默認情況下,InnoDB 的后臺線程有 7 個,其中 4 個 IO thread, 1 個 Master thread, 1 個 Lock monitor thread, 一個 Error monitor thread。InnoDB 的主要工作都是在一個單獨的 Master 線程里完成的。Master 線程的優先級***,它主要分為以下幾個循環:主循環(loop)、后臺循環(background loop)、刷新循環(flush loop)、暫停循環(suspend loop)。

其中主循環的偽代碼如下: 

  1. void master_thread() (  
  2.     loop:  
  3.     for (int i =0; i <10; i++){  
  4.         do thing once per second  
  5.         sleep 1 second if necessary  
  6.     }  
  7.     do things once per ten seconds  
  8.     goto loop;  
  9.  
  • 其中每秒一次的操作包括:刷新日志緩沖區(總是),合并插入緩沖(可能),至多刷新 100 個臟數據頁(可能),如果沒有當前用戶活動,切換至 background loop (可能)。
  • 其中每 10 秒一次的操作包括:合并至多 5 個插入緩沖(總是),刷新日志緩沖(總是),刷新 100 個或 10 個臟頁到磁盤(總是),產生一個檢查點(總是),刪除無用 Undo 頁 (總是)。
  • 后臺循環,若當前沒有用戶活動或數據庫關閉時,會切換至該循環執行以下操作:刪除無用的 undo 頁(總是),合并 20 個插入緩沖(總是),跳回到主循環(總是),不斷刷新 100 個頁,直到符合條件跳轉到 flush loop(可能)。
  • 如果 flush loop 中也沒有什么事情可做,邊切換到 suspend loop,將 master 線程掛起。

索引與鎖

MySQL 為我們提供了行鎖、表鎖、頁鎖三種級別的鎖,其中表鎖開銷小,加鎖快;不會出現死鎖;鎖定力度大,發生鎖沖突概率高,并發度***。行鎖開銷大,加鎖慢;會出現死鎖;鎖定粒度小,發生鎖沖突的概率低,并發度高;頁鎖開銷和加鎖速度介于表鎖和行鎖之間;會出現死鎖;鎖定粒度介于表鎖和行鎖之間,并發度一般。每個存儲引擎都可以有自己的鎖策略,例如 MyISAM 引擎僅支持表級鎖,而 InnoDB 引擎除了支持表級鎖外,也支持行級鎖(默認)。

  行鎖 表鎖 頁鎖
MyISAM    
BDB  
InnoDB  

InnoDB 行鎖是通過給索引上的索引項加鎖來實現的,這一點 MySQL 與 Oracle 不同,后者是通過在數據塊中對相應數據行加鎖來實現的。InnoDB 這種行鎖實現特點意味著:只有通過索引條件檢索數據,InnoDB 才使用行級鎖,否則,InnoDB 將使用表鎖,同樣地,當 for update 的記錄不存在會導致鎖住全表。當表有多個索引的時候,不同的事務可以使用不同的索引鎖定不同的行,另外,不論是使用主鍵索引、唯一索引或普通索引,InnoDB 都會使用行鎖來對數據加鎖。

InnoDB 的加鎖過程比較復雜,將所有掃描到的記錄都加鎖,范圍查詢會加間隙鎖,然后加鎖過程按照兩階段鎖 2PL 來實現,也就是先加鎖,然后所有的鎖在事物提交的時候釋放。加鎖的策略會和數據庫的隔離級別有關,在默認的可重復讀的隔離級別的情況下,加鎖的流程還會和查詢條件中是否包含索引,是主鍵索引還是普通索引,是否是唯一索引等有關。

譬如對于 select * from o_order where order_sn = '201912102322' for update; 這條 SQL 語句,在不同的索引情況下其加鎖策略也不一致:

  • order_sn 是主鍵索引,這種情況將在主鍵索引上的 order_sn = 201912102322 這條記錄上加排他鎖。
  • order_sn 是普通索引,并且是唯一索引,將會對普通索引上對應的一條記錄加排他鎖,對主鍵索引上對應的記錄加排他鎖。
  • order_sn 是普通索引,并且不是唯一索引,將會對普通索引上 order_sn = 201912102322 一條或者多條記錄加鎖,并且對這些記錄對應的主鍵索引上的記錄加鎖。這里除了加上行鎖外,還會加上間隙鎖,防止其他事務插入 order_sn = 201912102322 的記錄,然而如果是唯一索引就不需要間隙鎖,行鎖就可以。
  • order_sn 上沒有索引,innoDB 將會在主鍵索引上全表掃描,這里并沒有加表鎖,而是將所有的記錄都會加上行級排他鎖,而實際上 innoDB 內部做了優化,當掃描到一行記錄后發現不匹配就會把鎖給釋放,當然這個違背了 2PL 原則在事務提交的時候釋放。這里除了對記錄進行加鎖,還會對每兩個記錄之間的間隙加鎖,所以最終將會保存所有的間隙鎖和 order_sn = 201912102322 的行鎖。
  • order_sn = 201912102322 這條記錄不存在的情況下,如果 order_sn 是主鍵索引,則會加一個間隙鎖,而這個間隙是主鍵索引中 order_sn 小于 201912102322 的***條記錄到大于 201912102322 的***條記錄。試想一下如果不加間隙鎖,如果其他事物插入了一條 order_sn = 201912102322 的記錄,由于 select for update 是當前讀,即使上面那個事物沒有提交,如果在該事物中重新查詢一次就會發生幻讀。
  • 如果沒有索引,則對掃描到的所有記錄和間隙都加鎖,如果不匹配行鎖將會釋放只剩下間隙鎖。回憶一下上面講的數據頁的結果中又一個***記錄和最小記錄,Infimum 和 Supremum Record,這兩個記錄在加間隙鎖的時候就會用到。
責任編輯:龐桂玉 來源: segmentfault
相關推薦

2010-05-11 10:47:13

MySQL索引類型

2010-05-31 13:38:17

2022-06-02 15:34:45

vmstorage監控

2023-06-08 07:25:56

數據庫索引數據結構

2025-02-08 08:10:00

2022-06-08 07:34:02

持久化數據存儲原理索引存儲格式

2017-07-25 12:07:14

MySQL索引SQL

2010-10-08 13:53:14

2010-05-26 13:42:08

MySQL數據庫索引

2022-03-25 10:38:40

索引MySQL數據庫

2024-07-25 09:01:22

2023-10-21 21:13:00

索引SQL工具

2024-09-19 08:09:37

MySQL索引數據庫

2020-02-14 18:10:40

MySQL索引數據庫

2013-11-19 10:08:06

MongoDB

2010-06-04 11:28:05

MySQL數據庫

2011-06-03 13:25:32

分詞索引庫

2011-07-11 16:05:42

MySQL索引

2023-11-01 09:44:21

MySQLJava

2025-11-13 08:05:57

點贊
收藏

51CTO技術棧公眾號

亚洲精品在线影院| 三级小视频在线观看| 97精品一区二区| 欧美一区二区三区思思人| 影音先锋成人资源网站| 黄色av小说在线观看| 久久成人亚洲| 久久影视免费观看| 800av在线播放| 欧美电影h版| 亚洲免费在线电影| 精品久久蜜桃| 国产精品久久婷婷| 久久人人97超碰国产公开结果| 最近2019中文免费高清视频观看www99 | 欧美在线国产精品| 最新日韩免费视频| 国产成人一二片| 欧美日韩亚洲不卡| 国产欧美日韩小视频| 国产高清一区在线观看| 高清成人免费视频| 91精品国产综合久久香蕉的用户体验 | 亚洲国产一区视频| 一区二区91美女张开腿让人桶| 蜜桃91麻豆精品一二三区| 日本强好片久久久久久aaa| 久久久免费观看| 九九这里只有精品视频| 国产不卡av一区二区| 精品国产露脸精彩对白| 岛国毛片在线播放| 欧美电影免费观看网站| 无吗不卡中文字幕| 国产免费一区二区视频| 国产美女av在线| 久久亚洲精华国产精华液| 粉嫩高清一区二区三区精品视频| 在线观看视频中文字幕| 久久久久久9| 97成人精品区在线播放| 九九精品在线观看视频| 四虎成人精品永久免费av九九| 亚洲美女av黄| 少妇户外露出[11p]| 岛国av一区| 欧美成人一区二区三区| 一级片免费在线观看视频| 亚洲免费看片| 欧美老人xxxx18| 欧美一级视频在线| 色综合.com| 9191国产精品| 亚洲三级在线视频| 网站一区二区| 精品国产91洋老外米糕| 亚洲av成人精品一区二区三区| 97精品久久| 日韩国产激情在线| 91精彩刺激对白露脸偷拍| 亚洲精品亚洲人成在线| 亚洲乱码国产乱码精品精天堂| 中日韩精品一区二区三区| 色先锋久久影院av| 亚洲欧美在线第一页| 69精品无码成人久久久久久| 成人精品影院| 久久久成人的性感天堂| 538精品在线视频| 欧美精品导航| 欧美一级淫片videoshd| 亚洲天堂五月天| 蜜桃av一区二区三区| 成人免费自拍视频| www.四虎在线观看| 99精品视频在线免费观看| 美脚丝袜一区二区三区在线观看| 免费一级在线观看播放网址| 国产人妖乱国产精品人妖| 一区二区三区免费看| 成人在线网址| 五月天激情小说综合| 日韩av在线综合| 青草综合视频| 精品国产a毛片| 国产精品久久久久无码av色戒| 成人精品天堂一区二区三区| 欧美疯狂性受xxxxx另类| 91看片在线播放| 日本不卡视频在线| 成人av免费看| 国产视频在线看| 亚洲综合色视频| 成人一级片网站| 亚洲欧洲日韩精品在线| 亚洲精品二三区| av资源在线免费观看| 欧美视频在线观看| 国产精品免费久久久久影院| 超碰在线人人干| 久久九九99视频| 国产精品一二三在线观看| 大胆人体一区| 日韩午夜激情av| 色欲AV无码精品一区二区久久 | 国产精品三p一区二区| 国产亚洲a∨片在线观看| 全程偷拍露脸中年夫妇| 久久这里只有| 国产高清精品一区二区三区| h视频在线播放| 欧美日韩国产区| 亚洲一级片免费观看| 国产亚洲欧美日韩在线观看一区二区 | 亚洲黄色网址| 精品国产区一区| 奇米网一区二区| 午夜亚洲视频| 国产精品免费一区二区三区观看 | 欧美多人乱p欧美4p久久| 青青艹在线观看| www..com久久爱| 99re6这里有精品热视频| 视频一区在线免费看| 亚洲第一区第一页| 欧美黄色aaa| 麻豆精品视频在线观看| 日本高清视频一区二区三区 | 小处雏高清一区二区三区| 日本三级韩国三级久久| 三级视频在线看| 亚洲二区在线视频| 日本wwww色| 亚洲精品久久| 成人激情免费在线| 日本中文字幕在线视频| 在线观看日韩高清av| 玖玖爱在线观看| 夜夜爽av福利精品导航| 国产精品免费一区二区三区| 日韩av官网| 日韩一区二区三区在线| 黄色a级片在线观看| 精品一区二区三区免费视频| 亚洲午夜精品国产| 国产麻豆一区| 日韩性xxxx爱| 中文字幕 人妻熟女| 中文字幕av在线一区二区三区| 丁香婷婷激情网| 精品久久久久中文字幕小说| 国产成人亚洲精品| 第一视频专区在线| 欧美日韩一区二区三区不卡| 超碰人人人人人人人| 久久99久久99| 国产精品一二三在线观看| 中文一区二区三区四区| 亚洲18私人小影院| 欧美色综合一区二区三区| 色综合久久88色综合天天| 性猛交ⅹxxx富婆video| 麻豆久久一区二区| 亚洲色婷婷久久精品av蜜桃| 99ri日韩精品视频| 2019中文字幕在线免费观看| 毛片网站在线观看| 欧美日韩成人高清| 草视频在线观看| 国产白丝精品91爽爽久久| 国产特级淫片高清视频| 国产精品一区二区99| 国产精品亚洲美女av网站| 成人黄色网址| 亚洲精品国精品久久99热一| 久久久蜜桃一区二区| 国产精品久久一级| av电影中文字幕| 欧美一级播放| 伊人久久大香线蕉成人综合网| 久久综合给合| 欧美一级电影久久| 欧美日韩在线看片| 亚洲国产精品高清久久久| 国产成人av免费| 一区二区三区日本| 88久久精品无码一区二区毛片| 美女诱惑一区二区| 国产不卡一区二区视频| 欧美限制电影| 国产精品加勒比| av成人免费| 国内精品伊人久久| √新版天堂资源在线资源| 欧美va亚洲va| 中国老头性行为xxxx| 亚洲一区av在线| 潮喷失禁大喷水aⅴ无码| 成人免费看视频| 亚洲综合av在线播放| 国产亚洲欧洲| 香蕉视频免费版| 成人精品电影| 蜜桃成人在线| 精品无人区一区二区| 国产日韩欧美另类| 台湾佬成人网| 97精品欧美一区二区三区| 麻豆视频在线| 亚洲性无码av在线| 婷婷在线免费观看| 欧美一区在线视频| 在线观看毛片视频| 日韩欧美在线视频观看| 国产精品第108页| 亚洲视频每日更新| 久久一级免费视频| 久久五月婷婷丁香社区| 爱情岛论坛亚洲自拍| 久久精品99久久久| 成人亚洲视频在线观看| 国产亚洲毛片在线| 成人免费播放器| 欧美+亚洲+精品+三区| 中文字幕一区综合| 成人91在线| 亚洲v日韩v欧美v综合| 伊人春色之综合网| 女同一区二区| 香蕉久久精品| 欧美激情www| 婷婷综合福利| 免费久久一级欧美特大黄| 成人性生交大片免费看96| 99精品欧美一区二区三区| 成人在线视频区| 91久久久久久国产精品| 永久免费观看精品视频| 国产精品自产拍在线观看| 成人欧美一区二区三区的电影| 午夜精品一区二区三区在线| 免费污视频在线| 欧美精品第一页在线播放| 麻豆av在线免费观看| 欧美黑人xxxx| 波多野结衣在线播放| 性色av香蕉一区二区| 黄色在线免费观看网站| 97免费视频在线播放| 成年男女免费视频网站不卡| 性欧美xxxx交| 乱馆动漫1~6集在线观看| 992tv成人免费视频| 免费电影日韩网站| 国产精品国产自产拍高清av水多| 日韩制服一区| 成人免费视频网| 91成人在线精品视频| 精品欧美一区二区在线观看视频| 首页亚洲中字| 日韩电影天堂视频一区二区| 成久久久网站| 日本xxxxx18| 亚洲国产高清视频| 国产日产欧美视频| 日本美女一区二区| 亚洲av毛片在线观看| 成人免费看黄yyy456| 欧美老熟妇乱大交xxxxx| 国产精品素人一区二区| a级片在线观看免费| 亚洲午夜精品网| 嫩草影院一区二区三区| 69久久夜色精品国产69蝌蚪网| 成人激情四射网| 亚洲美女精品久久| 免费网站免费进入在线| 欧美精品久久久久a| 日韩成人av电影| 91色p视频在线| 噜噜噜天天躁狠狠躁夜夜精品| 日韩高清在线播放| 欧美成人高清| 国产黄色特级片| 国产精品综合一区二区三区| 99久久免费看精品国产一区| 中文字幕精品综合| 国产无套内射又大又猛又粗又爽| 色婷婷香蕉在线一区二区| 国产熟女一区二区三区五月婷| 亚洲精品久久久久国产| 精品美女视频在线观看免费软件 | 久久久久久久久精| 色呦呦日韩精品| 国产福利资源在线| 亚洲丝袜在线视频| 欧美日韩经典丝袜| 国产精品视频xxxx| 群体交乱之放荡娇妻一区二区| 一本久久a久久精品vr综合| 亚洲国产专区校园欧美| 亚洲欧美久久久久| 91女神在线视频| 欧美黄色一级网站| 欧美日韩国产不卡| 亚洲欧美日本在线观看| 蜜臀久久99精品久久久久久宅男| 伊人久久视频| 国产69精品久久久久9999apgf | 在线观看免费高清视频| 日韩精品免费综合视频在线播放| 精品欧美色视频网站在线观看| 3344国产精品免费看| 国产欧美视频在线| 亚洲精品无人区| 久久国产毛片| 99re这里只有| 亚洲在线观看免费视频| 91久久久久久久久久久久| 亚洲片在线观看| 欧美aa一级| 国内一区二区在线视频观看| 亚洲精品二区三区| 拔插拔插华人永久免费| 国产三级三级三级精品8ⅰ区| 日本学生初尝黑人巨免费视频| 欧美日本在线观看| 高清国产福利在线观看| 国产成人福利网站| 深爱激情综合| 欧美女人性生活视频| 成人激情动漫在线观看| 九九视频在线免费观看| 日韩欧美一级片| fc2ppv国产精品久久| 成人免费观看网址| 911久久香蕉国产线看观看| 91国内在线播放| 日韩一区在线看| 国产精品久久久久久免费| 日韩亚洲欧美中文在线| 91麻豆精品| 公共露出暴露狂另类av| 国产真实乱子伦精品视频| 五月天色婷婷丁香| 91精品国产品国语在线不卡| 国产写真视频在线观看| 成人一区二区电影| 亚洲国产精品久久久久蝴蝶传媒| 中文字幕国产高清| 亚洲精品免费在线| 成人午夜福利视频| 欧美精品激情blacked18| 日本欧美韩国国产| 人妻少妇被粗大爽9797pw| 国产午夜精品一区二区三区视频| 无码任你躁久久久久久久| 国产一区二区三区直播精品电影| 日本另类视频| 天堂v在线视频| 国产精品自拍三区| 日本五十熟hd丰满| 亚洲欧美日本另类| 色8久久久久| 蜜臀精品一区二区| 久久网站热最新地址| 在线观看xxxx| 欧美夫妻性视频| 久久视频在线观看| 另类小说第一页| 亚洲欧美欧美一区二区三区| 六月婷婷中文字幕| 国产精品国产自产拍高清av水多| 日韩极品一区| zjzjzjzjzj亚洲女人| 欧美性猛交xxx| 免费观看在线午夜影视| 国产精品一区二区你懂得| 午夜一区二区三区不卡视频| 欧美成人短视频| 精品国产乱码久久久久久影片| 深夜成人在线| 亚洲最大色综合成人av| 成人av网站大全| 中日韩av在线| 国内外成人免费激情在线视频网站| 亚洲精品国产精品粉嫩| 亚洲综合在线一区二区| 欧美日韩精品国产| 免费在线观看黄色网| 久久久久久精| 国产美女一区二区三区| 99久久久久久久久| 欧美日本黄视频| 欧美一级精品片在线看| 欧美极品jizzhd欧美仙踪林| 欧美性做爰猛烈叫床潮| 91桃色在线观看| 在线成人av电影| 久久久99精品免费观看不卡|