嘿? 同窗, 咱們又見面了.mysql
此次狂磕mysql系列進入了第三回合, 回龍觀大叔清明節約了我去體育公園打網球, 休息之餘問了下他最近的狀況, 說這兩天對人生有了更多的思考, 聽完後感受很驚悚, 以後我再抽機會分享下他那悲觀的心態.面試
今天仍是分享下回龍觀大叔的學習心得吧sql
大叔: 小子你不講武德, 有些知識我還沒學到呢...數據庫
還記得上次面試官問我 MyISAM 和 InnoDB 區別時, 我得意答到:有個區別可能不怎麼注意, 就是他們的目錄結構不同緩存
面試官確定的點了點頭, 我繼續說: MyISAM表會有三個文件 結構、數據、索引, 而InnoDB只有一個目錄.服務器
面試官讓我出門右轉乘電梯.markdown
首先查看存儲地址函數
SHOW VARIABLES LIKE 'datadir';
+---------------+-----------------------+
| Variable_name | Value |
+---------------+-----------------------+
| datadir | /usr/local/mysql/ |
+---------------+-----------------------+
複製代碼
每次建立數據庫都會建立對應的庫目錄, 此目錄下存放着表的相關文件性能
物理存儲結構:學習
咱們經過上一回合知道了 InnoDB是經過 頁 爲基本單位來管理存儲空間的, 一個 頁 大概才16KB, 咱們一個數據表動輒都十幾G二十幾G, 而咱們怎麼管理這些零碎的16KB頁呢?
爲了更好的管理這些頁,InnoDB有一個表空間或者文件空間(英文名:table space或者file space)的概念,這個表空間是一個抽象的概念,它能夠對應文件系統上一個或多個真實文件(不一樣表空間對應的文件數量可能不一樣)。每個表空間能夠被劃分爲不少不少不少個頁,咱們的表數據就存放在某個表空間下的某些頁裏
空間也分爲 系統表空間、獨立表空間和其餘類型的表空間, 例如undo表空間、臨時表空間等, 咱們目前只關注具體表數據存儲, 因此這裏先只關注獨立表空間.
建立表後會在對應數據庫目錄下存在 表名.frm 和 表名.ibd 兩個文件.
物理存儲結構:
咱們能夠看到兩種存儲引擎的設計思想的差別, MyISAM 是面向具體表實現的, 他區分了 索引、數據、結構, 劃分爲三個基本文件, 而InnoDB 是面向頁存儲的, 他使用頁的存儲模型將數據與全部索引放在一塊兒.
對於InnoDB的獨立表空間來講,每一個表的數據都會被存儲到一個與表名同名的.ibd文件中;對於MyISAM存儲引擎來講,數據和索引會分別存放到與表同名的.MYD和.MYI文件中。這些文件會隨着表中記錄的增長而增大,它們的大小受限於文件系統支持的最大文件大小
哎~看到這裏, 終於知道面試官爲何對我不滿意了...
上面說到了目錄結構, 咱們一杆子桶到底, 看看 InnoDB 邏輯上是怎麼管理的呢?
咱們知道一個頁大概有 16KB, 連續的64個頁就是一個區, 也就是說一個區默認佔用1MB空間大小.
而每256個區又被劃分紅一組.
存放葉子節點的區的集合就算是一個段(segment),存放非葉子節點的區的集合也算是一個段.
默認狀況下一個使用InnoDB存儲引擎的表只有一個聚簇索引,一個索引會生成2個段,而段是以區爲單位申請存儲空間的,一個區默認佔用1M存儲空間,因此默認狀況下一個只存了幾條記錄的小表也須要2M的存儲空間麼?
開始咱們數據記錄小, 段是從某個碎片區以單個頁面爲單位來分配存儲空間的, 當某個段已經佔用了32個碎片區頁面以後,就會以完整的區爲單位來分配存儲空間.
對於咱們的sql運行效果怎麼樣, EXPLAIN 是咱們必須學會的一個命令.
EXPLAIN 各個列做用以下:
列名 | 做用 |
---|---|
id | 運行惟一id |
select_type | SELECT關鍵字對應的那個查詢的類型 |
table | 查詢表名 |
partitions | 匹配的分區信息 |
type | 索引命中策略-如上圖 |
possible_keys | 可能用到的索引 |
key | 實際用到的索引 |
key_len | 實際使用到的索引長度 |
ref | 當使用索引列等值查詢時,與索引列進行等值匹配的對象信息 |
rows | 預估影響的行數 |
filtered | 某個表通過搜索條件過濾後剩餘記錄條數的百分比 |
Extra | 額外建議信息 |
下面咱們挑幾個比較重要的字段:
type 類型有 system, const, eq_ref,ref, fulltext, ref_or_null, index_merge, unique_subquery, index_subquery, range, index, ALL, 基本跟咱們上圖索引查詢類型是同樣的, 只是更加細化了一些和增長了子查詢類型.
possible_keys 表示查詢優化器認爲可能用到索引列, 這是一個權重計算過程, key 表示在查詢實際使用的索引, 若是咱們 possible_keys 過多而使用到的 key 少, 證實查詢優化器計算查詢成本時就得花費更長時間,因此若是能夠的話,儘可能刪除那些用不到的索引.
key_len 表示最後選擇的索引 key 的長度, key的長度越長佔用內存空間越大, 對於咱們掃面頁來講會加慢查詢速度, 因此 mysql 默認狀況下單個列的索引不能超過767個字符(utf-8)
咱們知道InnoDB一個page的默認大小是16k。因爲是Btree組織,要求葉子節點上一個page至少要包含兩條記錄(不然就退化鏈表了)。因此一個記錄最多不能超過8k
ref 表示關聯類型或訪問類型,即MySQL決定如何查找表中的行,查找數據行對應的大概範圍。 依次從最優到最差的分別爲:system>const>eq_ref>ref>range>index>All, 對於一個百萬級以上表來講, 咱們須要保證基本的 index 查詢.
rows 表示查詢優化器計算須要掃描的數據行數, 這個參考意義很大, 對於索引創建錯誤或數據分佈不稀疏狀況, 可經過此字段觀察.
filtered 表示查詢預測的分數, 表示返回結果的行數佔需讀取行數的百分比, filtered 的值越大越好.
Extra 額外建議信息, 下面摘抄了幾段
No matching min/max row 當查詢列表處有MIN或者MAX彙集函數,可是並無符合WHERE子句中的搜索條件的記錄時,將會提示該額外信息
Using index 查詢列表以及搜索條件中只包含屬於某個索引的列,也就是在可使用索引覆蓋的狀況下,在Extra列將會提示該額外信息
Using index condition 雖然出現了索引列,但卻不能使用到索引
在mysql 5.6版本以前咱們只能根據 EXPLAIN 查看sql執行查詢計劃, 5.6以後版本新推出 optimizer trace 功能, 咱們看看怎麼使用吧.
#查看是否開啓, 默認關閉
SHOW VARIABLES LIKE 'optimizer_trace';
#開啓
SET optimizer_trace="enabled=on";
複製代碼
開啓後咱們不須要作任何操做, 查詢語句執行完成後,就能夠到information_schema數據庫下的OPTIMIZER_TRACE表中查看完整的優化過程
OPTIMIZER_TRACE有4個列,分別是:
QUERY:表示咱們的查詢語句。
TRACE:表示優化過程的JSON格式文本。
MISSING_BYTES_BEYOND_MAX_MEM_SIZE:因爲優化過程可能會輸出不少,若是超過某個限制時,多餘的文本將不會被顯示,這個字段展現了被忽略的文本字節數。
INSUFFICIENT_PRIVILEGES:表示是否沒有權限查看優化過程,默認值是0,只有某些特殊狀況下才會是1,咱們暫時不關心這個字段的值
咱們知道內存讀寫速度要比磁盤快得多, 利用中間內存替換磁盤讀寫速度也是咱們業務開發經常使用的方式, 做爲強大的數據庫存儲引擎, 天然也少不了內存的利用
咱們看看 InnoDB 是怎麼作的呢?
InnoDB 存儲引擎是基於磁盤存儲的,對數據記錄的管理是以頁爲單位的。因爲CPU與磁盤之間在速度上的巨大差距,那麼緩衝池就應運而生了,它的存在提升數據庫的總體性能.
在MySQL服務器啓動的時候就向操做系統申請了一片連續的內存, 學名叫作Buffer Pool.
Buffer Pool存儲的是頁的數據, 在數據庫中讀取頁的操做,首先將從磁盤讀取的頁存放在緩衝池中。下一次再讀取數據頁時,先判斷該數據頁是否在緩衝池中,若是在緩衝池中,會直接讀取該數據頁,不然讀取磁盤上的頁
對數據庫中頁的修改操做,會首先修改在緩衝池中的頁,而後再以必定的頻率刷新到磁盤上。這裏有一點須要注意,頁從緩衝池刷新到磁盤的操做並非在每次頁發生更新時觸發的,而是經過 Checkpoint 機制刷新到磁盤
緩衝池的大小直接影響了數據庫的總體性能。隨着內存技術的成熟,內存成本也在不斷降低,所以強烈建議在數據庫專用服務器上,將盡量多的物理內存分配給緩衝池
Buffer Pool默認只有128M, 咱們能夠根據機器內存狀況設置 innodb_buffer_pool_size 參數, 通常狀況推薦設置 innodb-buffer-pool-size 爲服務器總可用內存的75%.
對於緩存的頁, 修改後還未同步到磁盤中咱們稱爲髒頁. 可是咱們的 Buffer Pool 那麼大, Checkpoint 是怎麼同步的呢?
全部修改的頁會單獨放到一個鏈表中, 當咱們同步磁盤時只須要同步這個 flush 鏈表就能夠了.
LRU鏈表分爲young和old兩個區域,能夠經過innodb_old_blocks_pct來調節old區域所佔的比例。首次從磁盤上加載到Buffer Pool的頁會被放到old區域的頭部,在innodb_old_blocks_time間隔時間內訪問該頁不會把它移動到young區域頭部。在Buffer Pool沒有可用的空閒緩存頁時,會首先淘汰掉old區域的一些頁
此文主要講了 數據庫目錄結構、Innodb 存儲的邏輯結構、索引和 InnoDB Buffer Pool 相關科普知識.
由於時間關係, 後面部分是我本身摘摘抄抄拼起來的.
期待大叔從新調整好心情, 帶你們狂磕事務篇.