《MySQL技術內幕——InnoDB存儲引擎》

MySQL體系結構和存儲引擎

1.1 定義數據庫和實例

數據庫:物理操作系統文件或其他形式文件類型的集合。
實例:MYSQL數據庫由一個後臺線程以及一個共享內存區構成。

1.2 MySQL體系架構

在這裏插入圖片描述
連接者:不同語言的代碼程序和mysql的交互(SQL交互)
1、連接池 管理、緩衝用戶的連接,線程處理等需要緩存的需求
2、管理服務和工具組件 系統管理和控制工具,例如備份恢復、Mysql複製、集羣等
3、sql接口 接受用戶的SQL命令,並且返回用戶需要查詢的結果
4、查詢解析器 SQL命令傳遞到解析器的時候會被解析器驗證和解析(權限、語法結構)
5、查詢優化器 SQL語句在查詢之前會使用查詢優化器對查詢進行優化
select id,name from user where age = 40;
a、這個select 查詢先根據where 語句進行選取,而不是先將表全部查詢出來以後再進行age過濾
b、這個select查詢先根據id和name進行屬性投影,而不是將屬性全部取出以後再進行過濾
c、將這兩個查詢條件聯接起來生成最終查詢結果
6、緩存 如果查詢緩存有命中的查詢結果,查詢語句就可以直接去查詢緩存中取數據
7、插入式存儲引擎 存儲引擎說白了就是如何管理操作數據(存儲數據、如何更新、查詢數據等)的一種方法。因爲在關係數據庫
中數據的存儲是以表的形式存儲的,所以存儲引擎也可以稱爲表類型(即存儲和操作此表的類型)

1.3 MySQL存儲引擎

show ENGINES 可以查看支持哪些引擎。
在這裏插入圖片描述

InnoDB存儲引擎概述

2.1 InnoDB存儲引擎概述

MySQL5.5版本開始的默認存儲引擎。
其特點是行鎖設計、支持MVCC、支持外鍵、提供一致性非鎖定讀。

2.2 InnoDB存儲引擎的版本

2.3 InnoDB體系架構

在這裏插入圖片描述

2.3.1 後臺線程

InnoDB存儲引擎是多線程模型。
1.Master Thread
負責將緩存池中的數據異步刷新到磁盤。
2.IO Thread
用來處理AIO的回調。
3.Purge Thread
用來回收已經使用並分配的undo頁。
4.Page Cleaner Thread
髒頁刷新操作。

2.3.2 內存

1.緩衝池
緩衝池就是一款內存區域,通過內存的速度來彌補磁盤較慢對數據庫性能的影響。

在這裏插入圖片描述
2.LRU List、Free List 和Flush List。
3.重做緩存日誌。
首先將重做日誌放到這個緩存區中,然後按一定頻率將其刷新到重做日誌文件中。

2.4 CheckPoint技術

CheckPoint主要作用:縮短數據庫恢復時間、緩衝池不夠用時,將髒頁刷新到磁盤、重做日誌不可用時,刷新到磁盤。

2.5 Master Thread工作方式

2.6 InnoDB的關鍵特性

2.6.1 插入緩衝

1.Insert Buffer
對於非聚集性索引的插入或更新操作,不是每一次直接插入到索引頁中,而是先判斷要插入的非聚集性索引是都在緩衝池中,如果存在,則直接插入,如果不存在則放入到一個Insert Buffer對象中。
2.Change Buffer

2.6.2 兩次寫

數據庫宕機時,可能正在寫入某個頁到表中,如果這個頁沒有寫完成,則會丟失數據。
doublewrite

2.6.3 自適應哈希索引

存儲引擎會自動根據訪問的頻率和模式來自動地爲某些熱點數據建立哈希索引。

2.6.4 異步IO

異步IO可以同時發起多個IO操作,還可以進行IO merage。

2.6.5 刷新鄰接頁

當刷新一個髒頁時,存儲引擎會檢測改頁所在區是否未髒頁,然後一起進行刷新操作。

文件

3.1 參數文件

數據庫參數可以看成鍵值對。包括動態參數和靜態參數。

3.2 日誌文件

  • 錯誤日誌 error log
  • 二進制日誌 binlog
  • 慢查詢日誌 show query log
  • 查詢日誌

3.2.1 錯誤日誌

錯誤日誌對MySQL的啓動、運行、關閉過程進行了記錄。

3.2.2 慢查詢日誌

通過慢查詢日誌可以找出有問題額SQL 語句,對其進行優化。

3.2.3 查詢日誌

查詢日誌記錄了所有對數據庫請求的信息。

3.2.4 二進制文件

二進制文件記錄了所有對MySQL數據庫執行更改的所有操作。
二進制日誌主要有以下作用:

  • 恢復
  • 複製
  • 審計

3.3 套接字文件

UNIX下可以通過套接字方式連接,因此需要一個套接字文件。

3.4 pid文件

當MYSQL實例啓動時,會將自己的進程寫入一個文件——該文件以.pid結尾。

3.5 表結構定義文件

.firm文件用來存儲表結構定義文件和視圖。

3.6 InnoDB存儲引擎文件

表就是關於特定實體額數據集合。

4.1 索引組織表

在 InnoDB存儲引擎中,表就是關於特定實體的數據集合。表都是根據主鍵順序組織存放的。這種存儲方式成爲索引組織表。

在 InnoDB存儲引擎表中,每張表都是有主鍵的,如果沒有主鍵則會按照如下方式選擇或者創建主鍵:

  • 首先判斷表是否有唯一非空索引,如果有,則該列爲主鍵。
  • 如果不符合,則創建一個6字節的指針。
    如果有多個非空唯一索引時,則按照創建順序現在第一個列作爲主鍵。

4.2 InnoDB邏輯存儲結構

從InnoDB存儲引擎的邏輯存儲結構來看,所有數據都被邏輯地存放在一個空間中,稱爲表空間。
表空間又由段(segment)、區(extent)、頁(page)組成。
在這裏插入圖片描述

4.2.1 表空間

表空間存放了所有數據,每個表可以有自己的表空間,表空間的大小可以不斷增長。

4.2.2 段

表空間是由各個段組成的,常見的有數據段、索引段、回滾段等。
前面介紹過InnoDB引擎表是索引組織的,因此數據即索引,索引即數據。數據段中爲B+樹的葉子節點,索引段中爲B+樹的非索引節點。

4.2.3 區

區是由連續的頁組成的空間。在任何情況下,每個區的大小都爲1MB。在默認情況下,InnoDB存儲引擎的頁大小爲16kb,即一個區有64個頁。

4.2.4 頁

頁的大小固定默認16kb,可以通過innodb_page_size修改頁的大小。

4.2.5 行

4.6 約束

4.6.1 數據完整性

關係型數據庫能保證自身存儲數據的完整性,不需要應用程序的控制。
主要形式有:

  • 選擇一個合適的實際類型確保一個數值滿足特定的條件。
  • 外鍵約束
  • 觸發器
  • Default約束

4.6.2 約束的創建和查找

表建立時就進行定義
利用ALERT TABLE命令來創建約束

4.6.3 約束和索引的區別

約束是一個邏輯概念,用來保證數據完整性。
索引是一個數據結構,既有邏輯上的概念,又有物理表現形式。

4.6.4 對錯誤數據的約束

通過sql_mode來設置對輸入值的約束。

4.6.5 ENUM和SET約束

sex EMUM(‘male’,‘female’) 用來約束性別只能是male或female。統一需要設置sql_mode。

4.6.6 觸發器與約束

觸發器的作用是在執行INSERT、DELETE、UPDATE命令之前或之後自動調用sql命令或者存儲過程。

4.6.7 外鍵約束

CREATE TABLE parent(
id INT NOT NULL,
PRIMARY KEY(id)
)ENGINE=INNODB

CREATE TABLE child(
id INT NOT NULL,
parent_id INT,
FOREIGN KEY(parent_id) REFERENCES parent(id)
)ENGINE=INNODB

4.7 視圖

在MySQL數據庫中,視圖是一個命名的虛表,它由一個sql查詢來定義,可以當做表使用。視圖中的數據沒有實際存儲。

索引與算法

5.1 INNODB存儲引擎索引概述

InnoDB存儲引擎常見索引:

  • B+樹索引
    B+樹索引並不能找到給定鍵值的具體的行,而是找到數據行所在的頁,然後把頁讀到內存,查找需要的數據。
  • 全文索引
  • 哈希索引
    InnoDB存儲引擎的哈希索引是自適應的,會根據表的使用情況自動生成好像索引。

5.2 數據結構與算法

5.2.1二分查找法

也稱折半查找法

5.2.2 二叉樹和平衡二叉樹

5.3 B+樹

B+樹是由B樹和索引順序訪問方法演化而來。
在B+樹中,所有記錄節點都是按照鍵值的大小順序存放在同一層的葉子節點上。

5.4 B+樹索引

B+樹索引就是B+樹在數據庫中的實現。
B+樹索引可以分爲聚集索引和輔助索引。不管是聚集索引還是輔助索引,其內部都是B+樹實現,即高度平衡,葉子節點存放着所有數據。
聚集索引與輔助索引的不同是,葉子節點是否存放一整行數據。

5.4.1 聚集索引

聚集索引就是按照每張表的主鍵構造一顆B+樹,同時葉子節點存放的是一整行的數據。每張表只能擁有一個聚簇索引。

由於定義了數據的邏輯順序,聚簇索引能夠特別快的訪問針對範圍值的查詢。

聚集索引的另一個好處是對於主鍵的排序查找和範圍查找速度非常快。

5.4.2 輔助索引

輔助索引葉子節點並不包含行記錄的全部數據,輔助索引存儲的頁籤存儲的是聚集索引鍵值。
輔助索引的書籤就是相應行數據的聚集索引鍵。

當通過輔助索引來查找數據時,InnoDB存儲引擎會遍歷輔助索引並通過葉級別的指針獲得指向主鍵索引的主鍵,然後通過主鍵索引找到一個完整的行記錄。

在這裏插入圖片描述

5.4.3 B+樹索引的分裂

nnoDB存儲引擎的Page Header中有幾個部分來保存插入的順序信息:
PAGE_LAST_INSERT
PAGE_DIRECTION
PAGE_N_DIRECTION
通過這些信息,可以決定是向左還是向右分裂,同時決定將分裂點記錄爲哪一個。

5.4.3 B+樹索引的管理

  1. 索引的管理
    索引的創建和刪除有兩種方法。一種是ALERT語句,一種是CREATE/DROP INDEX。
    用戶想要查看索引,可以使用SHOW INDEX。
  2. Fast Index Creation

5.5 Cardinality值

並不是在所有的查詢條件中出現的列都需要添加索引。
怎麼查看索引具有高選擇性呢?

5.6 B+樹索引的使用

5.6.2 聯合索引

聯合索引指對錶上多列進行索引。
在這裏插入圖片描述

聯合索引是有序的。就上面的例子來說,數據按照(a,b)的順序進行存放,即(1,1)、(1,2)、(2,1)、(2,4)、(3,1)、(3,2)。
select * from t where a = xxx and b = xxxx; select * from t where a =
xxx; (a,b)、(a)都是有序的,都可以使用到聯合索引。 select * from t where b =
xxx;因爲b不是排序的所以無法使用到索引。

CREATE TABLE t(
a INT NOT NULL,
b INT,
FOREIGN KEY(a)
KEY index1(a,b)
)ENGINE=INNODB

聯合索引的第二個好處是已經對第二個鍵值進行了排序處理。下列語句是可以用到索引的:
select * from t where a = xxx and order by b;

5.6.3 覆蓋索引

即從輔助索引中就可以查詢得到的記錄,而不需要查詢聚集索引中的記錄。

CREATE TABLE buy_log(
	user_id INT NOT NULL,
	buy_date TIMESTAMP
)
ALTER TABLE buy_log ADD KEY(user_id,buy_date)

在通常條件下,諸如(a,b)的聯合索引,一般是不可用選擇b列中的查詢條件的,但是對於統計操作,並且是覆蓋索引,可以使用:

SELECT COUNT(*) FROM buy_log WHERE buy_date > "2011-01-01" AND buy_date < "2012-01-01"

5.6.4 優化器選擇不使用索引的情況

某些情況下,EXPLIAIN查看執行計劃時,發現優化器沒有選擇索引去查找數據,而是通過掃描聚集索引。這種情況下多發生於範圍掃描、JOIN連接操作。

5.6.5 索引提示

用戶指定某個索引可以使用FORCE INDEX。

5.6.4 Multi-Range Read 優化

Multi-Range Read 優化的目的是爲了減少磁盤的隨機訪問,並且將隨機訪問轉換爲較爲順序的訪問。

5.7 哈希算法

哈希算法是一種常見的算法,時間複雜度爲O(1);

6.1 什麼是鎖

鎖機制用於管理對共享資源的併發訪問。

6.2 lock 與 latch

latch一般稱爲閂鎖,其要求持續時間短,模式主要爲互斥量和讀寫鎖。其目的用來保護併發線程操作臨界資源的準確性,並且通常沒有死鎖檢測機制。
lock的對象是事務,用來鎖定的是數據庫中的對象,入表、頁、行。有死鎖檢測機制。
在這裏插入圖片描述
通過 show engine innodb mutex 查看mutex鎖

6.3 INNODB引擎中的鎖

6.3.1 鎖的類型

INNODB存儲引擎實現瞭如下兩種標準的行級鎖

  • 共享鎖(S LOCK):允許事務讀取一行的數據。
  • 排它鎖(X LOCK):允許事務刪除或更新一行數據。
    在這裏插入圖片描述
    共享鎖之間鎖兼容,其他之間不兼容。

6.3.2 一致性非鎖定讀

一致性非鎖定讀是指數據庫通過多版本控制的方式來讀取當前執行時間數據庫中的行數據。如果讀取的行正在執行DELETE或UPDATE操作,這時讀操作不會等待行上的鎖釋放,而是去讀取行的快照數據。
之所以成爲非鎖定讀,因爲不需要等待訪問行上X鎖釋放。快照數據是指該行之前版本的數據,是通過undo段來完成。
在這裏插入圖片描述

一行記錄的快照可能不只有一個快照數據,一般稱這種技術爲多版本併發技術,MVCC多版本併發控制。
READ COMMITED 隔離級別下讀取到的是被鎖定行的最新一份快照數據。
REPATETABLE READ 隔離級別下總是讀取事務開始時的數據版本。

在這裏插入圖片描述
READ COMMITED 隔離級別下讀取到的總是最新的數據,所以是空。
REPATETABLE READ 隔離級別下總是讀取事務開始時的數據版本,讀取結果是 id=1的數據。

所謂樂觀鎖

6.3.3 一致性鎖定讀

在REPATETABLE READ模型下,InnoDB的SELECT操作使用一致性非鎖定讀。在某些情況下,需要對數據庫讀取操作加鎖以保證數據邏輯一致性。
InnoDB提供兩種一致性鎖定讀操作

SELECT ... FOR UPDATE

SELECT … FOR UPDATE對讀取的行加一個X鎖,其他事物不能對已鎖定的行加任何鎖。

SELECT ... LOCK IN SHARE MODE

SELECT … LOCK IN SHARE MODE 對讀取的記錄加一個S鎖,其他事物可以向被鎖定的行加S鎖,但是加X鎖會被阻塞。

所謂悲觀鎖

6.3.4 自增長與鎖

每個自增長的值的表都一個自增長計數器,當我們對含有自增長的計時器的表進行插入操作時,這個計時器會被初始化
這其實是一個自增長鎖,爲了提高插入性能,鎖在一個sql語句執行完就釋放,而不是整個事務結束。
爲了提高插入的性能 引入了輕量級互斥量的自增長機制。提了一個參數 innodb_autoinc_lock_mode來控制自增長的模式,該參數的默認值是1

6.4 鎖的算法

6.4.1 行鎖有三種算法

  • Record Lock 單行記錄上的鎖
    Record Lock 總是鎖住索引記錄,如果沒有設置任何索引,innodb會設置隱式的隱式主鍵來鎖定記錄
  • Gap Lock 間隙鎖,鎖定一個範圍,但不包含記錄本身
  • Next-Key Lock Record Lock+Gap Lock ,鎖定一個範圍,並鎖定記錄本身
    當查詢的索引含有唯一屬性時,會降級爲 Record Lock,即僅鎖住索引本身。

6.4.2 解決Phantom Problem(幻像問題)

Phantom Problem指在同一事務下連續執行兩次相同的sql語句,可能導致不同的結果,第二次SQL語句可能返回之前不存在的行。
在READ REPEATABLE下, Next-Key Lock機制可以避免Phantom Problem問題。

6.5 鎖問題

鎖會帶來三種問題

6.5.1 髒讀

髒讀就是再不同的事務下,一個事務可以讀到另一個事務未提交的數據,違反了數據庫的隔離性。

髒讀發生的條件是READ UNCOMMITED

6.5.2 不可重複讀(幻讀)

一個事務內兩次讀取到的數據不一致的情況爲不可重複讀。違反了數據庫一致性的要求。
不可重複讀與髒讀的區別是:髒讀讀到的是未提交的數據,不可重複讀讀到的是已經提交的數據。
不可重複讀示例:
在這裏插入圖片描述
在Next-Lock算法下,對索引的掃描,不僅鎖住掃描到的索引,還會鎖住這些索引覆蓋的範圍,因此在這個鎖住的範圍內插入是不允許的。避免了另外的事務在範圍內插入導致的不可重複讀問題。
因此InnoDB默認事務隔離級別是READ REPEATABLE。

6.5.3 丟失更新

丟失更新是另一個鎖導致的問題,簡單來說就是其中一個事務的操作會被另一個事務所覆蓋,從而導致不一致的問題。

 例如:
 1.事務t1將行記錄r更改爲v1,但是事務t1未提交。
 2.與此同時,t2將行記錄r改完t2,t2未提交。
 3.事務t1提交。
 4.事務t2提交。

在當前事務的隔離級別中,DML操作會被加上行鎖或其他對象級別的鎖,在步驟2中t2會被阻塞,直到t1提交。
在生成應用中還會產生邏輯意義上的丟失問題。

 1.事務t1查詢一行數據並防止本地內存中,展示給用戶user1。
 2.事務t2也查詢到此行數據並展示給用戶user2。
 3.用戶user1修改這行記錄,並更新數據庫提交。
 4.用戶user2修改這行記錄,並更新數據庫提交。

在這個過程中,user1的操作丟失了。要避免這種情況發生,需要讓事務在這種情況下的操作串行化,而不是並行操作。
在這裏插入圖片描述
如果用戶查詢之後,進行一些額外的操作,再進行update就可能會出現丟失更新的情況。

6.6 阻塞

因爲不同鎖之間的兼容性關係,在有些時刻一個事務中的鎖需要等待另一個事務中的鎖來是否它所佔的資源,這就是阻塞。

6.7 死鎖

6.7.1 死鎖的概念

死鎖指的是兩個或者兩個以上的事務在執行過程中,因爭奪鎖資源而照成的一種互相等待的現象。

6.7.3 死鎖的實例

在這裏插入圖片描述
A和B資源互相等待的時候會產生死鎖,簡稱AB-BA鎖。
在這裏插入圖片描述
A對4持有了X鎖,B在請求中獲得了4的S鎖,在A進行插入時會導致死鎖發生。

6.8 鎖升級

鎖升級是指將當前鎖的粒度降低。
InnoDB引擎不存在鎖升級的問題。因爲其不是根據每個記錄來產生行鎖的,相反,根據事務訪問的頁來進行鎖管理,採用的是位圖的方式。

事務

ACID原則。

7.1 認識事務

7.1.1 概述

  • A(Atomicity)
    要保持原子性,要麼都做,要麼什麼都不做。
  • C(Consistency)
    一致性是指從一種狀態轉移到另一種狀態時一致的狀態。
  • I(Isolation)
    隔離性要求事務提交前對其他事務都不可以見。
  • D(Durability)
    事務一旦提交,其結果就是永久性的。

7.1.2 分類

  • 扁平事務
  • 帶有保存點的 扁平事務
  • 鏈事務
  • 嵌套事務
  • 分佈式事務

7.2 事務的實現

7.2.1 redo

重做日誌用來實現事務的持久性,即D。其由兩部分組成:一是內存中的重做日誌緩存,其是易失的;二是重做日誌文件,其是持久的。
redo log用來保證事務的持久性,undo log用來幫助事務回滾以及MVCC的功能。

與binlog的區別

  • 重做日誌是在InnoDB引擎產生的,二進制日誌是在mysql上層產生,任何引擎都會產生二進制日誌。
  • 其次二進制的日誌進記錄的是邏輯日誌,記錄的是對應的sql;重做日誌是物理格式的日誌,其記錄的是對於每一個頁的修改。
  • 二進制日誌只在事務提交完成後進行一次寫入。重做日誌是在事務進行中不斷寫入。
    重做日誌是冪等的,而二進制日誌不一定是冪等的,比如有INSERT操作。

7.2.2 undo

重做日誌記錄了日誌的行爲,可以很好地通過其對頁進行“重做”操作。
但是事務有時還需要進行回滾操作,這時就需要undo。
undo是邏輯日誌,實際上做的是與先前相反的動作。對應每個INSETR,執行DELETE操作。
除了回滾操作,undo的另一個作用是MVCC。

7.6 事務的隔離級別

  • 讀未提交
  • 讀已提交
  • 可重複讀
  • 序列化

7.7 分佈式事務

7.7.1 mysql分佈式事務

分佈式事務指允許多個獨立的事務資源參與到一個全局的事務中。全局事務要求所有參與的事務要麼全部提交,要麼全部失敗。

XA事務允許不同數據庫之間的分佈式事務。
XA事務由一個或多個資源管理器、一個事務管理器、以及一個應用程序組成。
分佈式事務使用兩階段提交方式。
第一階段,所有參與全局事務的節點都開始準備,告訴事務管理器準備好提交了。
第二階段,事務管理器告訴資源管理器執行ROLLBACk還是COMMIT。如果任何一個節點顯示不能提交,那麼所有節被告知需要回滾。
MYSQL XA事務語法:
XA START ‘a’;
INSERT INTO z SELECT 11;
XA END ‘a’;
XA PREPARE ‘a’;
XA RECOVER;
XA COMMIT;

7.7.2 內部分佈式事務

在這裏插入圖片描述
最常見的XA存在於binlog和InnoDB之間。如果執行完1,2,在執行3時發生了宕機,就會造成主從不同步。

7.8 不好的事務習慣

7.8.1 在循環中提交

每一次提交都要寫重做日誌,循環提交效率會很慢

7.8.2 使用自動提交

mysql模型使用自動提交.開發人員應該意思到自動提交的問題。

7.8.3 使用自動回滾

對應開發人員,重要的不僅要知道發生了錯誤,還要知道發生了什麼錯誤,自動回滾存在這樣的問題。

7.9 使用長事務

長事務就是執行時間較長的事務。長事務的回滾代碼不可接受,可以拆分爲小批量事務來完成。

備份與恢復

8.1 備份與恢復概述

按照不同的備份方法分爲:

  • 熱備
  • 冷備
  • 溫備
    按照備份後的文件內容,又分爲:
  • 邏輯備份
  • 裸文件備份

裸文件備份有分爲:

  • 完全備份
  • 增量備份
  • 日誌備份

8.2 冷備

只需要定期備份備份mysql的文件。

8.3 邏輯備份

8.3.1 mysqldump

8.3.2 select into outfile

8.7 複製

8.7.1 複製的工作原理

複製是mysql提供的一種高可用高性能解決方案。可用分爲三個步驟
1.主服務器把數據更改記錄到二進制日誌中
2…從服務器把主服務器的二進制日誌複製到自己的中繼日誌中。
3.從服務器重做中繼日誌,把更改應用到自己的服務器上,以達到數據的最終一致性。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章