[TOC]
> 官網:https://dev.mysql.com/doc/refman/8.0/en/innodb-locking.html
對于`UPDATE`、`DELETE`、`INSERT`語句,InnoDB會自動給涉及數據集加**排他鎖(X)** 。而MyISAM在執行查詢語句`SELECT`前,會自動給涉及的所有表加**讀鎖**,在執行增、刪、改操作前,會自動給涉及的表加**寫鎖**,這個過程并不需要我們去手動操作。
:-: 
## 表鎖
**開銷小,加鎖快;不會出現死鎖;鎖定力度大,發生鎖沖突概率高,并發度最低。**
不同的存儲引擎支持的鎖粒度是不一樣的:
* **InnoDB行鎖和表鎖都支持、MyISAM只支持表鎖**
* **InnoDB只有通過索引條件檢索數據才使用行級鎖,否則,InnoDB使用表鎖也就是說,InnoDB的行鎖是基于索引的**
表鎖下又分為兩種模式: 表讀鎖(Table Read Lock)、表寫鎖(Table Write Lock)。
* **讀讀不阻塞:**?當前用戶在讀數據,其他的用戶也在讀數據,不會加鎖
* **讀寫阻塞:**?當前用戶在讀數據,其他的用戶不能修改當前用戶讀的數據,會加鎖
* **寫寫阻塞:**?當前用戶在修改數據,其他的用戶不能修改當前用戶正在修改的數據,會加鎖
讀鎖和寫鎖是互斥的,讀寫操作是串行
* 如果某個進程想要獲取讀鎖,同時另外一個進程想要獲取寫鎖。在mysql中,寫鎖是優先于讀鎖的!
* 寫鎖和讀鎖優先級的問題是可以通過參數調節的:`max_write_lock_count`和`low-priority-updates`
> MyISAM支持查詢與插入操作的并發進行,也可以通過系統變量`concurrent_insert`指定哪種模式。在MyISAM中默認:如果MyISAM表的中間沒有被刪除的行的話,那MyISAM是允許在一個進程讀表的同時,另一個進程從表尾做插入記錄的。但是INNODB是不支持的。
## 行鎖
**開銷大,加鎖慢;會出現死鎖;鎖定粒度小,發生鎖沖突的概率低。**
InnoDB實現了兩種類型的行鎖:
* **共享鎖(S鎖、讀鎖)**: 允許一個事務去讀一行,阻止其他事務獲得相同數據集的排他鎖。即多個客戶可以同時讀取同一個資源,但不允許其他客戶修改。
* **排他鎖(X鎖、寫鎖)**: 允許獲得排他鎖的事務更新數據,阻止其他事務取得相同數據集的讀鎖和寫鎖。寫鎖是排他的,寫鎖會阻塞其他的寫鎖和讀鎖。
**為了允許行鎖和表鎖共存,實現多粒度鎖機制**,InnoDB還有兩種內部使用的**意向鎖(Intention Locks)**,這兩種意向鎖都是**表鎖**:
* **意向共享鎖(IS):** 事務打算給表數據行加共享鎖,事務在給一個數據行加共享鎖前必須先取得該表的IS鎖。
* **意向排他鎖(IX):** 事務打算給表數據行加排他鎖,事務在給一個數據行加排他鎖前必須先取得該表的IX鎖。
| | X | IX | S | IS |
| --- | --- | --- | --- | --- |
| X | 沖突 | 沖突 | 沖突 | 沖突 |
| IX | 沖突 | 兼容的 | 沖突 | 兼容的 |
| S | 沖突 | 沖突 | 兼容的 | 兼容的 |
| IS | 沖突 | 兼容的 | 兼容的 | 兼容的 |
> 意圖鎖不會阻塞任何東西。意圖鎖的主要目的是表明有人正在鎖定一行,或者要鎖定表中的一行。
## 悲觀鎖、樂觀鎖
### 并發控制
當程序中可能出現并發的情況時,就需要保證在并發情況下數據的準確性,以此確保當前用戶和其他用戶一起操作時,所得到的結果和他單獨操作時的結果是一樣的。這就叫做并發控制。并發控制的目的是**保證一個用戶的工作不會對另一個用戶的工作產生不合理的影響。**
沒有做好并發控制,就可能導致臟讀、幻讀和不可重復讀等問題。
實現并發控制的主要手段分為樂觀并發控制和悲觀并發控制兩種。
### 悲觀鎖
悲觀并發控制(又名 “悲觀鎖”,Pessimistic Concurrency Control,縮寫 “PCC”)是一種并發控制的方法。它可以阻止一個事務以影響其他用戶的方式來修改數據。如果一個事務執行的操作的某行數據應用了鎖,那只有當這個事務把鎖釋放,其他事務才能夠執行與該鎖沖突的操作
悲觀鎖的實現,往往依靠數據庫提供的鎖機制 (也只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系統不會修改數據)。
**實現:**
在對任意記錄進行修改前,先嘗試為該記錄加上排他鎖(exclusive locking)。
如果加鎖失敗,說明該記錄正在被修改,那么當前查詢可能要等待或者拋出異常。 具體響應方式由開發者根據實際需要決定。
如果成功加鎖,那么就可以對記錄做修改,事務完成后就會解鎖了。
其間如果有其他對該記錄做修改或加排他鎖的操作,都會等待我們解鎖或直接拋出異常。
#### 優缺點
悲觀并發控制實際上是 “先取鎖再訪問” 的保守策略,為數據處理的安全提供了保證。
一個事務如果鎖定了某行數據,其他事務就必須等待該事務處理完才可以處理那行數,降低了并行性
### 樂觀鎖
樂觀并發控制(又名 “樂觀鎖”,Optimistic Concurrency Control,縮寫 “OCC”)是一種并發控制的方法。它假設多用戶并發的事務在處理時不會彼此互相影響,各事務能夠在不產生鎖的情況下處理各自影響的那部分數據。在提交數據更新之前,每個事務會先檢查在該事務讀取數據后,有沒有其他事務又修改了該數據。如果其他事務有更新的話,正在提交的事務會進行回滾。
相對于悲觀鎖,在對數據庫進行處理的時候,樂觀鎖并不會使用數據庫提供的鎖機制。一般的實現樂觀鎖的方式就是記錄數據版本。
數據版本,為數據增加的一個版本標識。當讀取數據時,將版本標識的值一同讀出,數據每更新一次,同時對版本標識進行更新。當我們提交更新的時候,判斷數據庫表對應記錄的當前版本信息與第一次取出來的版本標識進行比對,如果數據庫表當前版本號與第一次取出來的版本標識值相等,則予以更新,否則認為是過期數據。
#### 優缺點
樂觀并發控制相信事務之間的數據競爭 (data race) 的概率是比較小的,因此盡可能直接做下去,直到提交的時候才去鎖定,所以不會產生任何鎖和死鎖。
## 間隙鎖 (Gap Locks)
間隙鎖是在**索引記錄之間的間隙**上的鎖,或在第一條索引記錄之前或最后一條索引記錄之后的間隙上的鎖。
~~~
SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE
~~~
阻止其他事務將值`15`插入 column`t.c1`,無論該列中是否已經存在任何此類值,因為該范圍內所有現有值之間的間隙都已鎖定。
> 不同的事務可以在間隙上持有沖突的鎖。 唯一目的是防止其他事務插入到間隙中。間隙鎖可以共存。一個事務采用的間隙鎖不會阻止另一個事務在同一間隙上采用間隙鎖。
**間隙鎖避免了在 Repeatable read隔離級別下的幻讀**
## 記錄鎖 (Record Locks)
對索引記錄的鎖。例如,`SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE;`阻止任何其他事務插入、更新或刪除值為 的`t.c1`行`10`。
記錄鎖總是鎖定索引記錄,即使定義的表沒有索引。對于這種情況,`InnoDB`創建一個隱藏的聚集索引并將該索引用于記錄鎖定
## 下一鍵鎖 (Next-Key Locks)
下一個鍵鎖是索引記錄上的記錄鎖和索引記錄之前的間隙上的間隙鎖的組合。
`InnoDB`執行行級鎖定的方式是,當它搜索或掃描表索引時,它會在它遇到的索引記錄上設置共享或排他鎖。因此,行級鎖實際上是索引記錄鎖。索引記錄上的 **next-key** 鎖定也會影響該索引記錄之前的“gap”。也就是說,**next-key** 鎖是索引記錄鎖加上索引記錄前面的間隙上的間隙鎖。
## 死鎖
兩個或兩個以上的進程在執行過程中,因爭奪資源而造成的一種互相等待的現象,若無外力作用,它們都將無法推進下去.此時稱系統處于死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱為死鎖進程。
死鎖的關鍵在于:**兩個(或以上)的 Session 加鎖的順序不一致。**
## InnoDB中不同SQL語句設置的鎖
1. `SELECT ... FROM`
是一致的讀取,讀取數據庫的快照,并且**不設置鎖**,除非事務隔離級別設置為`SERIALIZABLE`。
2. `SELECT ... FROM ... LOCK IN SHARE MODE`
在所有索引掃描范圍的索引記錄上加上共享的next key鎖;如果是唯一索引,只需要在相應記錄上加index record lock。這些被共享lock住的行無法進行`update`、`delete`。
如果沒有使用到索引,則鎖住全表(表級的排他鎖),無法進行`insert`、`update`、`delete`。
3. `SELECT ... FROM ... FOR UPDATE`
在所有索引掃描范圍的索引記錄上加上**排他的next key鎖**。如果是唯一索引,只需要在相應記錄上加`index record lock`。
如果沒有利用到索引將鎖住全表(表級的排他鎖),其它事務無法進行`insert`、`update`、`delete`操作。
4. `UPDATE ... WHERE ...`
在所有索引掃描范圍的索引記錄上加上**排他的next key鎖**。如果是唯一索引,只需要在相應記錄上加`index record lock`。
如果沒有利用到索引將鎖住全表(表級的排他鎖),其它事務無法進行其他的`insert`、`update`、`delete`操作。
5. `DELETE FROM ... WHERE ...`
在所有索引掃描范圍的索引記錄上加上**排他的next key鎖**。如果是唯一索引,只需要在相應記錄上加`index record lock`。
如果沒有利用到索引將鎖住全表(表級的排他鎖),其它事務無法進行其它的`insert`、`update`、`delete`操作
6. `INSERT……`
在插入的記錄上加一把排他鎖,這個鎖是一個`index-record lock`,并不是**next-key 鎖**,因此就沒有**gap 鎖**,他將不會阻止其他會話在該條記錄之前的gap插入記錄。
- PHP
- PHP 核心架構
- PHP 生命周期
- PHP-FPM 詳解
- PHP-FPM 配置優化
- PHP 命名空間和自動加載
- PHP 運行模式
- PHP 的 Buffer(緩沖區)
- php.ini 配置文件參數優化
- 常見面試題
- 常用函數
- 幾種排序算法
- PHP - 框架
- Laravel
- Laravel 生命周期
- ThinkPHP
- MySQL
- 常見問題
- MySQL 索引
- 事務
- 鎖機制
- Explain 使用分析
- MySQL 高性能優化規范
- UNION 與 UNION ALL
- MySQL報錯:sql_mode=only_full_group_by
- MySQL 默認的 sql_mode 詳解
- 正則表達式
- Redis
- Redis 知識
- 持久化
- 主從復制、哨兵、集群
- Redis 緩存擊穿、穿透、雪崩
- Redis 分布式鎖
- RedisBloom
- 網絡
- 計算機網絡模型
- TCP
- UDP
- HTTP
- HTTPS
- WebSocket
- 常見幾種網絡攻擊方式
- Nginx
- 狀態碼
- 配置文件
- Nginx 代理+負載均衡
- Nginx 緩存
- Nginx 優化
- Nginx 配置 SSL 證書
- Linux
- 常用命令
- Vim 常用操作命令
- Supervisor 進程管理
- CentOS與Ubuntu系統區別
- Java
- 消息隊列
- 運維
- RAID 磁盤陣列
- 邏輯分區管理 LVM
- 業務
- 標準通信接口設計
- 業務邏輯開發套路的三板斧
- 微信小程序登錄流程
- 7種Web實時消息推送方案
- 用戶簽到
- 用戶注冊-短信驗證碼
- SQLServer 刪除同一天用戶重復簽到
- 軟件研發完整流程
- 前端
- Redux
- 其他
- 百度云盤大文件下載
- 日常報錯記錄
- GIT
- SSL certificate problem: unable to get local issuer certificate
- NPM
- reason: connect ECONNREFUSED 127.0.0.1:31181
- SVN
- SVN客戶端無法連接SVN服務器,主機積極拒絕
- Python
- 基礎
- pyecharts圖表
- 對象
- 數據庫
- PySpark
- 多線程
- 正則
- Hadoop
- 概述
- HDFS