数据库系列:MySQL InnoDB锁机制介绍
數據庫系列:MySQL慢查詢分析和性能優化
數據庫系列:MySQL索引優化總結(綜合版)
數據庫系列:高并發下的數據字段變更
數據庫系列:覆蓋索引和規避回表
數據庫系列:數據庫高可用及無損擴容
數據庫系列:使用高區分度索引列提升性能
數據庫系列:前綴索引和索引長度的取舍
數據庫系列:MySQL引擎MyISAM和InnoDB的比較
數據庫系列:InnoDB下實現高并發控制
數據庫系列:事務的4種隔離級別
數據庫系列:RR和RC下,快照讀的區別
1 背景
隨著互聯網的發展,高并發業務的盛行,MySQL InnoDB引擎的細粒度行鎖,變成很核心的特性之一。
在并發高的情況下,如果使用不當,會導致嚴重的性能問題。比如細粒度行鎖,是實現在索引記錄上的,但如果沒有命中索引,就回退化成表鎖,那對性能是災難的。
下面我們從索引角度出發, 介紹下MySQL InnoDB的鎖機制。
2 InnoDB的索引回顧
Innodb中有2種索引:主鍵索引(也叫聚集索引 Clustered Index)、輔助索引(也叫非聚集索引 Secondary Index)。
主鍵索引: 每個表只有一個主鍵索引,b+樹結構,葉子節點存儲主鍵的值以及對應整條記錄的數據,非葉子節點不存儲記錄的數據,只存儲主鍵的值。
當表中未指定主鍵時,MySQL內部會自動給每條記錄添加一個隱藏的rowid字段(默認4個字節)作為主鍵,用rowid構建聚集索引。聚集索引在MySQL中即主鍵索引。
輔助索引: 每個表可以有多個輔助索引,b+樹結構,非聚集索引葉子節點存儲字段(索引字段)的值以及對應記錄主鍵的值,其他節點只存儲字段的值(索引字段),這就是與聚集索引不同的地方。每個表可以有多個非聚集索引。
InnoDB的每一個表都會有聚集索引:
- 假設表定義了PK,則PK就是聚集索引
- 如果未定義PK,則第一個非空unique列即是聚集索引
- 如果沒有PK也沒有非空unique列,InnoDB會創建一個隱含的row_id作為聚集索引使用
下圖更形象說明這兩種索引的區別,這邊假設了一個存儲4行數據的表。Id為主鍵索引,Name作為輔助索引,圖中清晰的體現了聚簇索引和非聚簇索引的差異。
表中有四條記錄:
5, Gates, Microsoft
7, Bezos, Amazon
11, Jobs, Apple
14, Elison, Oracle
InnoDB數據檢索過程
上面的表中有2個索引:id作為主鍵索引,name作為輔助索引。
如果需要查詢id=14的數據,只需要在左邊的主鍵索引中檢索就可以了。
如果需要搜索name='Ellison'的數據,需要2步:
- 先在輔助索引中檢索到name='Ellison'的數據,獲取id為14
- 再到主鍵索引中檢索id為14的記錄
輔助索引這個查詢過程在mysql中叫做回表,相對于主鍵索引多了第二步操作。
MyISAM數據檢索過程
- 在索引中找到對應的關鍵字,獲取關鍵字對應的記錄的地址
- 通過記錄的地址查找到對應的數據記錄
對比發現:Innodb中最好是采用主鍵索引查詢,這樣只需要一次索引,如果使用輔助索引檢索,涉及多一步的回表操作,比主鍵查詢要耗時一些。
所以,InnoDB的普通索引,實際上會掃描兩遍:
第1遍,由普通索引找到PK:檢索到name='Ellison'的數據,獲取id為14
第2遍,由PK找到行記錄:即到主鍵索引中檢索id為14的記錄
對索引有興趣的,可以參考作者的這幾篇文章:
MySQL全面瓦解22:索引的介紹和原理分析
MySQL全面瓦解23:MySQL索引實現和使用
MySQL全面瓦解24:構建高性能索引(策略篇)
3 InnoDB 幾種常見鎖
★InnoDB默認的事務隔離級別為可重復讀(Repeated Read, RR),我們當下的所有介紹都是基于這個隔離級別為前提的。
- 記錄鎖(Record Locks):鎖定單一行記錄,InnoDB 使用記錄鎖來實現行級鎖,這樣允許多個事務并發訪問不同的行。
- 間隙鎖(Gap Locks):InnoDB 的特性,用于鎖定一個范圍,但不包括實際的記錄。這主要用于防止幻讀(Phantom Reads)。
- 臨鍵鎖(Next-Key Locks):InnoDB 存儲引擎的一種鎖定機制,在執行查詢語句時,根據查詢條件所鎖定的一個范圍。這個范圍中包含有間隙鎖和記錄鎖。它的設計目的是為了解決幻讀(Phantom Reads)。
3.1 記錄鎖(Record Locks)
記錄鎖,它*索引記錄,例如:
select * from table where id=5 for update;
它會在id=1的索引記錄上加鎖,以阻止其他事務插入,更新,刪除id=1的這一行。
需要說明的是:
select * from table where id=5;
則是快照讀(SnapShot Read),它并不加鎖,快照讀可以參考作者這篇文章:數據庫系列:RR和RC下,快照讀的區別
3.2 間隙鎖(Gap Locks)
間隙鎖,它*索引記錄中的間隔,或者第一條索引記錄之前的范圍,又或者最后一條索引記錄之后的范圍。
延續上面的那個例子繼續演示:
# 表結構
table (Id PK, Name , Company);
# 表中包含四條記錄
5, Gates, Microsoft
7, Bezos, Amazon
11, Jobs, Apple
14, Elison, Oracle
執行SQL語句如下:
select * from table
where id between 7 and 13
for update;
這樣的話,會*數據的區間,以防止其他事務插入id=8的記錄。
假設沒有間隙鎖,則可能夠插入成功,而之前的select事務,會發現檢索的結果集莫名多了一條記錄,即幻影數據。
所以間隙鎖主要目的用于防止幻讀(Phantom Reads),避免其他事務在間隔中插入數據,導致 『不可重復讀』。
如果把事務的隔離級別降級為讀提交(Read Committed, RC),對,就是互聯網最常用的隔離級別,間隙鎖則會自動失效。
3.3 臨鍵鎖(Next-Key Locks)
臨鍵鎖(Next-Key Locks)是數據庫管理系統InnoDB中的一種重要鎖定機制。這種鎖是查詢時根據查詢條件鎖定的一個范圍,這個范圍包括間隙鎖和記錄鎖,左開右閉,即不鎖住左邊界,但會鎖住右邊界。臨鍵鎖的主要設計目的是為了解決所謂的“幻讀”問題。
# 左開右閉 示例
(-infinity, 1]
(1, 7]
(7, 9]
(9, +infinity]
依然沿用上面的例子,InnoDB引擎,RR隔離級別:
-- 創建一個示例表
CREATE TABLE users (
Id INT PRIMARY KEY,
Name VARCHAR(255) NOT NULL,
Company VARCHAR(255) NOT NULL,
);
-- 插入一些示例數據
INSERT INTO users (id, name, company) VALUES (1, 'Alice', 'ali');
INSERT INTO users (id, name, company) VALUES (2, 'Brand', 'tencent');
INSERT INTO users (id, name, company) VALUES (3, 'Charlie', 'baidu');
-- 開始一個事務,并使用臨鍵鎖查詢數據
START TRANSACTION;
SELECT * FROM users WHERE id > 1 FOR UPDATE;
-- 在另一個事務中嘗試插入新數據,將會被阻塞直到第一個事務釋放鎖
START TRANSACTION;
INSERT INTO users (id, name, age) VALUES (4, 'David', 30);
COMMIT;
-- 第一個事務提交后,第二個事務可以繼續執行插入操作
COMMIT;
臨鍵鎖的主要目的,也是為了避免幻讀(Phantom Read),在事務隔離級別為可重復讀的情況下,InnoDB存儲引擎默認使用臨鍵鎖。這種鎖提供了一種有效的機制來保證在并發環境中數據的完整性和一致性。
如果把事務的隔離級別降級為RC,臨鍵鎖則也會失效。
4 總結
- InnoDB的索引與行記錄存儲在一起,MyISAM則是通過索引的地址查找到對應的數據記錄,效率低一些
- InnoDB的聚集索引存儲行記錄,普通索引存儲PK,所以普通索引要查詢兩次
- 記錄鎖鎖定索引關聯的具體記錄
- 間隙鎖鎖定間隔,防止間隔中被其他事務插入
- 臨鍵鎖鎖定索引記錄+間隔,防止幻讀
- elect...for update加鎖的幾種情況:
- 主鍵字段:加行鎖。
- 唯一索引字段:加行鎖。
- 普通索引字段:加行鎖。
- 主鍵范圍:加多個行鎖。
- 普通字段:加表鎖。
- 查詢空數據:不加鎖。
- 行鎖與表鎖的區別
- 如果事務1加了行鎖,一直未釋放鎖,事務2操作相同記錄,會一直等待直至超時。
- 如果事務1加了表鎖,一直未釋放鎖,事務2無論操作哪一行記錄,都會一直等待直到超時
總結
以上是生活随笔為你收集整理的数据库系列:MySQL InnoDB锁机制介绍的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 自学 --day9---js中的数学操作
- 下一篇: mysql 的相关操作_MySQL(记录