《MySQL + Innodb存儲引擎 》三刷總結

第一章

1 . 數據庫指的是 二進制文件,而數據庫實例是操作所有數據的程序。
2 . MySQL的整體結構從下到上 : 物理的二進制文件(不同的文件系統分卷形式等) ----- 針對表的存儲引擎 ----- 優化器組件、緩衝組件、管理服務與工具組件、SQL接口組件 ------ 連接池組件 ----- 不同語言的對接接口
3 . 針對單表建立一個 .ibd 文件,每一個數據庫 裏面單獨分成了一個文件夾,該文件夾下面是所有的表文件及 .frm 文件.
4 . 使用 MVCC 針對一個數據,如果一行數據已經被加 X 鎖了,可以利用redo日誌還原出它之前的版本值,讓另一個事務去去讀之前的版本值,獲得高併發性.
5 . 四種隔離級別解決的問題以及採用的策略、insert buffer , double write , 自適應哈希索引, 預讀取(後面)
6 . 數據的通信本質上是進程之間的通信,MySQL有TCP ,共享內存等方式,所以在JDBC中的通信也是首先建立一個進程通信,然後在進程裏面不斷的通過現場發起操作。

.
.

第二章 innobd 概述

Checkpoint :

首先是爲了防止緩衝刷新失敗,數據丟失,事務提交時,採用了 先寫redo日誌,再修改頁的方法。是爲了避免所有的數據都緩存在內存中,然後當電腦宕機的時候,數據丟失,雖然可以從redo日誌文件恢復,但是數據量過大,checkpoint 即表示在checkpoint 之前的數據,都是已經被刷新到了磁盤的。
緩衝池不夠是,執行一次checkpoint。
Redo日誌重用,會執行checkpoint
通過LSN log sequence number 來標識最新的checkpoint
Sharp checkpoint 執行在數據庫關閉的時候
Fuzzy checkpoint 執行在
1 . master thread 週期性的異步刷新緩衝池的髒頁到磁盤中
2 . LRU列表,需要檢查列表是否足夠空閒頁,然後發現髒頁則執行checkpoint(一個單獨的 page cleaner 線程執行檢查可用頁數量 )
3 . Async/ Sync flush : redo 日誌已滿,需要將剩餘 checkpoint 之後的所有日誌及相關的頁,全部刷新到 磁盤中。然後redo日誌可以全部複用了。剩餘checkpoint 之後的 是有一個閾值的,不同的情況下不同。主要也是爲了保證 redo 日誌文件的循環使用。 放在了 cleaner page thread 中。
4 . 髒頁數量達到閾值,強制執行一次checkpoint

Master Thread :

內部多個循環 :主循環 loop ,後臺循環 back loop ,刷新循環 flush loop ,暫停循環 suspend loop。Master根據數據集狀態在不同的循環裏面切換

main loop

在 master 中有每秒進行一次的: 日誌緩存刷新到磁盤即使事務還沒提交、可能存在合併插入緩存、沒有用戶活動則切換到 backloop 、 每秒可能刷新一定量的 髒頁到磁盤。
每 10 秒進行一次的:刷新髒頁、合併插入緩衝、日誌緩衝刷新到日誌文件、刪除無用邏輯undo頁

bask loop

刪除無用的邏輯undo頁、 合併插入緩衝、跳回 main loop

flush loop

刷新髒頁

suspend loop

沒有事情了,到掛起狀態

關鍵特性

插入緩衝

關於緩衝:
在緩衝的層面上: redo日誌緩衝 ----- 額外內存池 ------ 緩衝池
緩衝池裏面 : 數據頁、索引頁 、 插入緩衝 、 鎖信息 、 自適應哈希索引 、 數據字典信息
關於索引:
關於數據存儲是在一個數據頁裏面,然後針對主鍵創建了 B+ 樹,一個表的數據可能有很多的數據頁,數據的分頁也是按照主鍵進行區分的,所以 查找的時候是相對的順序的,速率很高。
但是針對 輔助索引(其他字段顯示的建立了索引),也會有一個索引樹,但是 存放仍然是按照主鍵順序存放的,但是這樣按照 輔助索引進行查找的時候,訪問是亂序隨機的,可能一次在這個數據頁,一次在另外一個數據頁,所以效率不高。
.
針對的情況 : 表中是按照主鍵進行順序存放的,所以在 按照主鍵進行查找更新操作的時候,很多的訪問都是順序的,按照輔助索引很多的都是隨機訪問。所以 加入一個 insert buffer ,即將這些針對輔助索引進行的操作緩存起來,然後用後臺線程 在查找的時候,看哪一些的操作 是針對葉子節點的同一個 數據頁的,然後將這些操作一次性的進行。
每個表都有一個 唯一的 space id。
每個頁 也有 page_no

double write

因爲 頁 是 16 K ,文件最小是 4K ,而磁盤IO 最小是 512 Bytes,所以 會存在部分寫失效的情況,導致那髒頁的16K 數據丟失,並且磁盤的數據 損壞,而 redo log(後面) 是記錄 磁盤的物理操作的log (最小IO 也是512),但是已經寫入了部分,導致對應頁的損壞,無法使用redo log 進行恢復。
所以 在將 髒頁 刷新到 磁盤之前,第一步 首先 拷貝到內存 的 double write buffer (2M),第二步 將2M 兩次寫入 共享表空間(2 M),第三步 將buffer 的髒頁刷新到 磁盤。如果宕機損壞,則從共享表空間拷貝,恢復最新的數據。

自適應哈希索引

對於某些熱點的查詢,可以針對 索引 值建立哈希索引,然後不需要經過 B+ 數的幾層索引查詢,則直接定位到數據頁及偏移。
哈希索引只能用於 等值 查詢。

異步IO

不需要等待執行完畢
可以合併多個IO ,比如頁是連續的,會一次IO從磁盤讀48K。

刷新鄰近頁

刷新髒頁的時候,檢測該頁所在區(64個頁)是否有其他的髒頁,有則一起刷新,通過AIO,合併IO,減少磁盤 IO 次數。
存在的問題:不怎麼髒的頁,刷新後變成髒頁,需要再一次從磁盤去獲取???反而是負擔。

以上的總結 :
從磁盤 到 文件 到 頁 的 一次 IO 最小粒度是不同的。會存在 部分 寫的問題,所以需要保存一個副本在共享表空間裏面。防止數據損壞。
針對輔助索引需要建立插入緩衝,合併多個針對同一個數據頁的操作,減少隨機離散訪問次數。
針對熱點的查詢建立哈希索引提高效率。
異步IO 可以檢測到頁的操作,判斷是否連續,是否針對同一個頁,然後從磁盤進行IO的時候,可以一次性讀取到內存中。
.
.
.

第三章 文件

是文件,存在文件系統中的,最小的 IO 是4k。

參數文件

日誌文件

錯誤日誌

記錄整個操作中的錯誤報告

慢查詢日誌 slow log

將 SQL 超過時間閾值的記錄下
將 沒有運行 索引的SQL記錄下
數據庫前期,查詢時間都短,引入 邏輯讀取 與 磁盤物理 讀取次數兩個特性判斷SQL是否記錄

查詢日誌

所有的請求都會記錄,無論成功

二進制日誌

二進制日誌,主要記錄的是對數據庫表的更新操作
有不同的格式,默認格式 statement 爲 直接記錄 SQL 語句,然後從數據庫庫通過SQL更新
其他格式 : ROW ,記錄的是表的對每一行的更改,選擇不同的隔離級別,併發性更好

套接字文件

pid文件

只存放了 數據庫實例 當前的進程ID,每次重啓會更新

表結構的定義文件

在數據庫文件夾下 : 針對每一個表都會有與之對應的 .frm 文件,存儲表的結構。數據存在數據庫文件夾下面的 .ibd 文件.
在這裏插入圖片描述

存儲引擎文件

在這裏插入圖片描述

表空間文件

共享表空間文件 ----- && ----- 獨立表空間文件(.ibd 記錄 數據、索引、等)

redo log 重做日誌文件

與 checkpoint 相關
每次寫入磁盤的大小是 512 kb,所以一定成功,不存在部分寫。
重做日誌的條目格式:
redo_log_type ---- sapce ------- page_no ------ redo_log_body
類型 ----- 表空間ID ---- 頁的偏移量 ---- 數據
具體記錄的什麼????以及 爲什麼說 redo 是記錄 的 物理操作。 其他的日誌記錄的是 邏輯操作。

.
.

第四章 表

索引組織表

每個表都會有一個主鍵,都是按照主鍵索引順序存放數據的,如果沒有顯示的定義主鍵
1 . 非 null 的 unique 的第一個 字段定義爲所以
2 . 不存在的話,引擎自動創建一個6字節的指針作爲主鍵形式的存在

引擎的存儲的 “邏輯” 結構

整體結構:
tablespace(共享、獨立) ---- segment(leaf / non-leaf / roolback) ----- extent(1M) ---- page(16K) ---- row

表空間

有 : 共享的 ibdata 與 獨立的 .ibd 兩種,獨立的表空間中值記錄數據、索引、插入緩衝bitmap頁。 其他的 信息 如 undo 信息,插入緩衝索引頁、事務系統信息、二次寫緩衝 仍然是在 ibdata 中,所以會一直增大。並且前面提到了在 double write 的時候,也需要用到 共享表空間。

數據段 索引段 回滾段
數據爲 B+樹的葉子節點 索引段爲非葉子節點

1MB 、 一次從磁盤申請多個區保證頁的連續 64個頁 16 KB
頁有壓縮的概念,但是區大小不變
剛開始新建的表會申請碎片的頁空間(每個段開始前都有 32 個頁的 碎片空間???),而不是連續的64個頁的區。碎片頁的來源是 其他 頁的內部碎片空間嗎 ????
32個碎片頁空間使用完後, 表空間 開始以 一個區 一個區(1M) 的申請空間

默認 16 KB ,可以通過參數設置改變爲 4K 8K
常見頁有 : 數據頁B-tree 、undo 頁 、 system 頁 、事務數據頁 、 插入緩衝位圖頁 等等

每個頁最多 7992 行, 16KB/2 - 200(最小的算,減去頭部的一些值、標籤等)

行記錄的格式

compact

每一行的前面都有 幾部分 的頭部信息,然後纔是 每一列的數據
變長字段長度列表 ----- NULL標誌位 ----- 記錄頭信息(多種) ---- 列1 ---- 列2 ---------------------------------…

redundant

字段長度偏移列表 ---- 記錄頭繼續你學 ---- 列1 ----- 列2 —

compressed && dynamic

主流形式 : 一行數據裏面 : 各種頭部信息 + 指向 真實數據頁 的偏移量指針(20字節,off page) + 可能尾部信息
compressed 的可以對大類型數據進行壓縮
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
---------- 測試結果並沒有連接到 off page ???

char

針對 英文 數字的 char 類型,一個字符爲 一個字節 ,CHAR 類型爲定長的,但是 針對一個字符 多個字節的字符集 ,CHAR 類型被處理爲 變長的 。比如漢字。

行溢出問題

varchar 65535 字節,一個頁最多 16384 字節, 所以要創建 varchar (65535) 的字段,其實最多 65532 字段,varchar 說的65535 是所有varchar類型的總長度。
但是一個頁最多16384字節,如果創建了 int(8),varchar(65520),varchar(10) 是可以的,但是針對溢出的部分,有特殊的機制。
所以這種情況下,針對每一行,.ibd 文件中,正常的部分之外,一行還會存放 768 bytes 的varchar 前綴數據 + 偏移量(偏移到 真正存放數據的 uncompressed BLOB Page )
即相當於 每一行 : 其他數據 + 前綴數據(保存一部分) + 指向一個 BLOB page 的指針 + 其他
如果 一個 頁中可以放下至少兩條數據 , 那麼 varchar 的就不會 單獨存放到一個 BLOB page。

數據頁結構

FileHeader 38 bytes ------- 頁的頭信息
page header 56 bytes ------- 數據頁的狀態信息
…行記錄
Infimun + supremum records ------ Infimun 比任何主鍵值都小的值 supremum 比任何主鍵值都大的值
user records ---------- 行記錄 每條記錄之間 通過 鏈表 連接

free space --------空閒空間 鏈表形式 記錄刪除後,空間加入空閒鏈表中(結構體??)
page directory ---------- 用於 在 B+ 數查到具體的數據頁後,裝入內存中,然後對頁 通過 page directory 進行二叉查找到具體 的 記錄。(總的來說就是 B+樹程度上 查找記錄 針對的 頁 ,,,, 而 在頁 中查找記錄 ,是鏈表的結構)
file tailer 8bytes ----------檢測頁的完整性 : checksum && 與頭部某個值相同

在這裏插入圖片描述

約束

1 . 主鍵、唯一、外鍵、not null 、default強制
2 . 錯誤數據的約束 ,比如 not null ,固定格式等。
3 . enum: 值中 之一
4 . set :無重複
5 . check 參數
6 . 創建觸發器,在數據操作之前先檢查一遍,錯誤寫入日誌
7 . 外鍵約束 父子表 ,子表不能插入外鍵在父表中不存在的值

視圖

1 . 虛表,不存儲實際數據,由SQL語句得來,相當於一個已經過濾好的數據集
2 . 不需要關心基表的各種結構,直接從 視圖 裏面取數據
3 . 可以對視圖實現行的控制,因爲是一個數據結構的存在,不同於數據實表,需要數據庫權限
4 . 一定程度的數據安全
5 . mysql 中不支持物化
6 . 可以屏蔽 原表 對 視圖的影響

分區

對很大的表進行分區,可能會提高查詢速度,但是針對類似於需要全文檢索的,分區形式的IO開銷很大。

分區類型

1 . range 範圍類型
對數據的 某一字段(最好是主鍵,輔助字段建立索引之後離散讀取會好很多) 進行範圍的劃分,不同範圍劃分到不同的分區裏面,這樣針對某些操作的 速度可以提高很多, 可以置對一個分區的數據進行操作。
2 . list 類型
分區裏面將 離散 的值全部定義好,不在分區的是會失敗。
3 . hash 類型
只需要制定分區數量 與 hash的表達式。分區自動按照表達式計算應該分到哪一個分區,可以 自定義 字段。
4 . key
也是hash 函數,但是使用內置函數
5 . columns 分區
上面的 針對的 值 ,必須是 Integer 類型,不是的話,需要轉化。
直接使用 對應 的數據類型進行分區,不需要轉化成 integer 類型。

子分區

允許 range 和 list 再進行 hash 或者 key 的分區

null 的分區

range 中 比任何都要小
list 中必須提前顯示指定
hash 和 key 的都是返回 0

性能

對於 OLAP 應用,一次需要很多數據,分區可以很好地提高效率
但是 對於 OLTP 應用,如果 針對 非主鍵的字段進行 等值查詢,那麼需要遍歷十個分區(每個分區都有B+數的 IO ),並且是離散的訪問。即使針對其他字段建立索引了,但是輔助索引的字段仍然可能存在於多個分區裏面,IO開銷還是很大。

分區 與 表的數據交換

表需要有相同結構
非分區表的數據需要滿足該分區的條件
.
.

第五章 索引與算法

B+樹
全文索引
自適應哈希索引

數據結構

二分查找
BST 與 平衡二叉樹

B+樹操作

插入 刪除的分裂

B+樹索引

聚集索引

針對主鍵建立的B+樹,葉子節點是存放該行記錄的對應的 頁 。
葉子節點的數據頁通過雙向鏈表進行鏈接。(邏輯上是連續的,物理上不是,通過鏈表的指針進行鏈接)
每個葉子節點只存在一個 數據頁 。
針對主鍵的查詢非常快

輔助索引(非聚集索引)

對非主鍵建立輔助索引:即對這些字段新建一個 B+ 樹,葉子節點除了存放對應的 鍵值對,還存有一個 bookmark 書籤。書籤存放的是 ---- 可以查詢到這個 輔助索引的 對應的主鍵索引值,是通過遍歷建立起來的,所以可能有多個。然後可以根據 查詢到的主鍵索引,在聚集索引的B+樹上面定位到數據頁。
所以整體結構上,輔助索引 在 聚集索引上方,但是沒有聯繫。是獨立的,不會影響到聚集索引。

B+樹索引的分裂

管理 ?????????????????????????

1 . alter 創建、刪除
2 . fast index creation 加S 鎖
3 . Online schema change ,事務創建的是,讓讀寫事務對錶操作
4 . Online DDL

cardinality 值

創建B+樹索引比較適用於 數據重複 很少的。cardinality 表示 索引字段不重複記錄的預估值。所以在 cardinality / rows 的值越接近於 1 時,b+索引越有用
.
採樣 計算 cardinality 值,值更新發生在 insert 和 update 中,但是按照一定的策略纔會去更新一次:“1/ 16 的數據更改了” --或者 – 發生變化的次數大於一個閾值
每次默認從葉子節點採樣8個數據頁,該參數可以設置。

B+樹索引的使用

聯合索引

多個字段 建立聯合索引,左右子節點的分配首先 從第一字段開始匹配,然後向後。
第一個子彈的排序是順序的,但是後面的數據是相對亂序的。所以針對後面字段的單個索引查詢使用這個不好。
對於同一個的 葉子節點下的數據,第一個字段後面的一個字段的相對順序是順序的,不需要排隊。

覆蓋索引

直接通過 輔助索引 獲得查詢結構,不需要通過 輔助索引的葉子節點再次做一次聚集索引查詢。也沒有聚集索引關於 數據頁 的IO 操作。索引,輔助索引的開銷會小很多。比如: 統計 有多少的行記錄,直接使用 輔助索引進行統計即可。即稱爲 覆蓋索引。
(我覺得 覆蓋 索引是一個抽象的概念,針對的是 非聚集的輔助索引 而言的)

不使用索引

範圍查找 或者 JOIN 連接

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