These tables have a small footprint. Table-level locking limits the performance in read/write workloads, so it is often used
in read-only or read-mostly workloads in Web and data warehousing configurations.
應用範圍比較小。表級鎖定限制了讀/寫的性能,所以在 Web 和數據倉庫配置中,
它一般用於只讀或以讀爲主的工做。
特色:
支持表級別的鎖(插入和更新會鎖表)。不支持事務。
擁有較高的插入(insert)和查詢(select)速度。
存儲了表的行數(count 速度更快)。
(怎麼快速向數據庫插入 100 萬條數據?咱們有一種先用 MyISAM 插入數據,而後
修改存儲引擎爲 InnoDB 的操做。)
適合:只讀之類的數據分析的項目
InnoDB(2 個文件)
https://dev.mysql.com/doc/refman/5.7/en/innodb-storage-engine.html
The default storage engine in MySQL 5.7. InnoDB is a transaction-safe (ACID compliant) storage engine for MySQL that
has commit, rollback, and crash-recovery capabilities to protect user data. InnoDB row-level locking (without escalation to
coarser granularity locks) and Oracle-style consistent nonlocking reads increase multi-user concurrency and performance.
InnoDB stores user data in clustered indexes to reduce I/O for common queries based on primary keys. To maintain data
integrity, InnoDB also supports FOREIGN KEY referential-integrity constraints.
mysql 5.7 中的默認存儲引擎。InnoDB 是一個事務安全(與 ACID 兼容)的 MySQL
存儲引擎,它具備提交、回滾和崩潰恢復功能來保護用戶數據。InnoDB 行級鎖(不升級
爲更粗粒度的鎖)和 Oracle 風格的一致非鎖讀提升了多用戶併發性和性能。InnoDB 將
用戶數據存儲在彙集索引中,以減小基於主鍵的常見查詢的 I/O。爲了保持數據完整性,
InnoDB 還支持外鍵引用完整性約束。
特色:
支持事務,支持外鍵,所以數據的完整性、一致性更高。
支持行級別的鎖和表級別的鎖。
支持讀寫併發,寫不阻塞讀(MVCC)。
特殊的索引存放方式,能夠減小 IO,提高查詢效率。
適合:常常更新的表,存在併發讀寫或者有事務處理的業務系統
Memory(1 個文件)
Stores all data in RAM, for fast access in environments that require quick lookups of non-critical data. This engine was
formerly known as the HEAP engine. Its use cases are decreasing; InnoDB with its buffer pool memory area provides a
general-purpose and durable way to keep most or all data in memory, and NDBCLUSTER provides fast key-value lookups for
huge distributed data sets.
將全部數據存儲在 RAM 中,以便在須要快速查找非關鍵數據的環境中快速訪問。這
個引擎之前被稱爲堆引擎。其使用案例正在減小;InnoDB 及其緩衝池內存區域提供了一
種通用、持久的方法來將大部分或全部數據保存在內存中,而 ndbcluster 爲大型分佈式
數據集提供了快速的鍵值查找。
特色:
把數據放在內存裏面,讀寫的速度很快,可是數據庫重啓或者崩潰,數據會所有消
失。只適合作臨時表。
將表中的數據存儲到內存中。
CSV(3 個文件)
Its tables are really text files with comma-separated values. CSV tables let you import or dump data in CSV format, to
exchange data with scripts and applications that read and write that same format. Because CSV tables are not indexed, you
typically keep the data in InnoDB tables during normal operation, and only use CSV tables during the import or export stage.
它的表其實是帶有逗號分隔值的文本文件。
csv表容許以csv格式導入或轉儲數據,
以便與讀寫相同格式的腳本和應用程序交換數據。由於 csv 表沒有索引,因此一般在正
常操做期間將數據保存在 innodb 表中,而且只在導入或導出階段使用 csv 表。
特色:不容許空行,不支持索引。格式通用,能夠直接編輯,適合在不一樣數據庫之
間導入導出
Archive(2 個文件)
These compact, unindexed tables are intended for storing and retrieving large amounts of seldom-referenced historical,
archived, or security audit information.
這些緊湊的未索引的表用於存儲和檢索大量不多引用的歷史、存檔或安全審計信息。
特色:不支持索引,不支持 update delete。
這是 MySQL 裏面常見的一些存儲引擎,咱們看到了,不一樣的存儲引擎提供的特性都
不同,它們有不一樣的存儲機制、索引方式、鎖定水平等功能。
咱們在不一樣的業務場景中對數據操做的要求不一樣,就能夠選擇不一樣的存儲引擎來滿
足咱們的需求,這個就是 MySQL 支持這麼多存儲引擎的緣由。
1.5.4.如何選擇存儲引擎?
若是對數據一致性要求比較高,須要事務支持,能夠選擇 InnoDB。
若是數據查詢多更新少,對查詢性能要求比較高,能夠選擇 MyISAM。
若是須要一個用於查詢的臨時表,能夠選擇 Memory。
若是全部的存儲引擎都不能知足你的需求,而且技術能力足夠,能夠根據官網內部
手冊用 C 語言開發一個存儲引擎
https://dev.mysql.com/doc/internals/en/custom-engine.html
1.6. 執行引擎(Query Execution Engine),返回結果
OK,存儲引擎分析完了,它是咱們存儲數據的形式,繼續第二個問題,是誰使用執
行計劃去操做存儲引擎呢?
這就是咱們的執行引擎,它利用存儲引擎提供的相應的 API 來完成操做。
爲何咱們修改了表的存儲引擎,操做方式不須要作任何改變?由於不一樣功能的存
儲引擎實現的 API 是相同的。
最後把數據返回給客戶端,即便沒有結果也要返回。
2. MySQL 體系結構總結
基於上面分析的流程,咱們一塊兒來梳理一下 MySQL 的內部模塊。
2.1. 模塊詳解
一、 Connector:用來支持各類語言和 SQL 的交互,好比 PHP,Python,Java 的
JDBC;
二、 Management Serveices & Utilities:系統管理和控制工具,包括備份恢復MySQL 複製、集羣等等;
三、 Connection Pool:鏈接池,管理須要緩衝的資源,包括用戶密碼權限線程等等;
四、 SQL Interface:用來接收用戶的 SQL 命令,返回用戶須要的查詢結果
五、 Parser:用來解析 SQL 語句;
六、 Optimizer:查詢優化器;
七、 Cache and Buffer:查詢緩存,除了行記錄的緩存以外,還有表緩存,Key 緩存,權限緩存等等;
八、 Pluggable Storage Engines:插件式存儲引擎,它提供 API 給服務層使用,跟具體的文件打交道
2.2. 架構分層
整體上,咱們能夠把 MySQL 分紅三層,跟客戶端對接的鏈接層,真正執行操做的服
務層,和跟硬件打交道的存儲引擎層(參考 MyBatis:接口、核心、基礎)。
2.1.1.鏈接層
咱們的客戶端要鏈接到 MySQL 服務器 3306 端口,必需要跟服務端創建鏈接,那麼
管理全部的鏈接,驗證客戶端的身份和權限,這些功能就在鏈接層完成。
2.1.2.服務層
鏈接層會把 SQL 語句交給服務層,這裏面又包含一系列的流程:
好比查詢緩存的判斷、根據 SQL 調用相應的接口,對咱們的 SQL 語句進行詞法和語
法的解析(好比關鍵字怎麼識別,別名怎麼識別,語法有沒有錯誤等等)。
而後就是優化器,MySQL 底層會根據必定的規則對咱們的 SQL 語句進行優化,最
後再交給執行器去執行。
2.1.3.存儲引擎
存儲引擎就是咱們的數據真正存放的地方,在 MySQL 裏面支持不一樣的存儲引擎。
再往下就是內存或者磁盤。
3. 一條更新 SQL 是如何執行的?
講完了查詢流程,咱們是否是再講講更新流程、插入流程和刪除流程?
在數據庫裏面,咱們說的 update 操做其實包括了更新、插入和刪除。若是你們有看
過 MyBatis 的源碼,應該知道 Executor 裏面也只有 doQuery()和 doUpdate()的方法,
沒有 doDelete()和 doInsert()。
更新流程和查詢流程有什麼不一樣呢?
基本流程也是一致的,也就是說,它也要通過解析器、優化器的處理,最後交給執行器。
區別就在於拿到符合條件的數據以後的操做。
3.1. 緩衝池 Buffer Pool
首先,InnnoDB 的數據都是放在磁盤上的,InnoDB 操做數據有一個最小的邏輯單
位,叫作頁(索引頁和數據頁)。咱們對於數據的操做,不是每次都直接操做磁盤,因
爲磁盤的速度太慢了。InnoDB 使用了一種緩衝池的技術,也就是把磁盤讀到的頁放到一
塊內存區域裏面。這個內存區域就叫 Buffer Pool
下一次讀取相同的頁,先判斷是否是在緩衝池裏面,若是是,就直接讀取,不用再
次訪問磁盤。
修改數據的時候,先修改緩衝池裏面的頁。內存的數據頁和磁盤數據不一致的時候,
咱們把它叫作髒頁。InnoDB 裏面有專門的後臺線程把 Buffer Pool 的數據寫入到磁盤,
每隔一段時間就一次性地把多個修改寫入磁盤,這個動做就叫作刷髒。
Buffer Pool 是 InnoDB 裏面很是重要的一個結構,它的內部又分紅幾塊區域。這裏
咱們趁機到官網來認識一下 InnoDB 的內存結構和磁盤結構。
3.3.1.內存結構
Buffer Pool 主要分爲 3 個部分: Buffer Pool、Change Buffer、Adaptive Hash
Index,另外還有一個(redo)log buffer。
一、Buffer Pool
Buffer Pool 緩存的是頁面信息,包括數據頁、索引頁。
查看服務器狀態,裏面有不少跟 Buffer Pool 相關的信息:
SHOW STATUS LIKE '%innodb_buffer_pool%';
這些狀態均可以在官網查到詳細的含義,用搜索功能。
https://dev.mysql.com/doc/refman/5.7/en/server-status-variables.html
Buffer Pool 默認大小是 128M(134217728 字節),能夠調整。
查看參數(系統變量):
SHOW VARIABLES like '%innodb_buffer_pool%';
這些參數均可以在官網查到詳細的含義,用搜索功能。
https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html
內存的緩衝池寫滿了怎麼辦?(Redis 設置的內存滿了怎麼辦?)InnoDB 用 LRU
算法來管理緩衝池(鏈表實現,不是傳統的 LRU,分紅了 young 和 old),通過淘汰的
數據就是熱點數據
內存緩衝區對於提高讀寫性能有很大的做用。思考一個問題:
當須要更新一個數據頁時,若是數據頁在 Buffer Pool 中存在,那麼就直接更新好了。
不然的話就須要從磁盤加載到內存,再對內存的數據頁進行操做。也就是說,若是
沒有命中緩衝池,至少要產生一次磁盤 IO,有沒有優化的方式呢?
二、Change Buffer 寫緩衝
若是這個數據頁不是惟一索引,不存在數據重複的狀況,也就不須要從磁盤加載索
引頁判斷數據是否是重複(惟一性檢查)。這種狀況下能夠先把修改記錄在內存的緩衝
池中,從而提高更新語句(Insert、Delete、Update)的執行速度。
這一塊區域就是 Change Buffer。5.5 以前叫 Insert Buffer 插入緩衝,如今也能支
持 delete 和 update。
最後把 Change Buffer 記錄到數據頁的操做叫作 merge。何時發生 merge?
有幾種狀況:在訪問這個數據頁的時候,或者經過後臺線程、或者數據庫 shut down、
redo log 寫滿時觸發。
若是數據庫大部分索引都是非惟一索引,而且業務是寫多讀少,不會在寫數據後立
刻讀取,就可使用 Change Buffer(寫緩衝)。寫多讀少的業務,調大這個值:
SHOW VARIABLES LIKE 'innodb_change_buffer_max_size';
表明 Change Buffer 佔 Buffer Pool 的比例,默認 25%。
三、Adaptive Hash Index
索引應該是放在磁盤的,爲何要專門把一種哈希的索引放到內存?下次課再說。
四、(redo)Log Buffer
思考一個問題:若是 Buffer Pool 裏面的髒頁尚未刷入磁盤時,數據庫宕機或者重
啓,這些數據丟失。若是寫操做寫到一半,甚至可能會破壞數據文件致使數據庫不可用。
爲了不這個問題,InnoDB 把全部對頁面的修改操做專門寫入一個日誌文件,而且
在數據庫啓動時從這個文件進行恢復操做(實現 crash-safe)——用它來實現事務的持
久性。
這個文件就是磁盤的 redo log(叫作重作日誌),對應於/var/lib/mysql/目錄下的
ib_logfile0 和 ib_logfile1,每一個 48M。
這 種 日 志 和 磁 盤 配 合 的 整 個 過 程 , 其 實 就 是 MySQL 裏 的 WAL 技 術
(Write-Ahead Logging),它的關鍵點就是先寫日誌,再寫磁盤。
show variables like 'innodb_log%';

問題:
一樣是寫磁盤,爲何不直接寫到 db file 裏面去?爲何先寫日誌再寫磁盤?
咱們先來了解一下隨機 I/O 和順序 I/O 的概念。
磁盤的最小組成單元是扇區,一般是 512 個字節。
操做系統和內存打交道,最小的單位是頁 Page。
操做系統和磁盤打交道,讀寫磁盤,最小的單位是塊 Block
若是咱們所須要的數據是隨機分散在不一樣頁的不一樣扇區中,那麼找到相應的數據需
要等到磁臂旋轉到指定的頁,而後盤片尋找到對應的扇區,才能找到咱們所須要的一塊
數據,一次進行此過程直到找完全部數據,這個就是隨機 IO,讀取數據速度較慢。
假設咱們已經找到了第一塊數據,而且其餘所需的數據就在這一塊數據後邊,那麼
就不須要從新尋址,能夠依次拿到咱們所需的數據,這個就叫順序 IO。
刷盤是隨機 I/O,而記錄日誌是順序 I/O,順序 I/O 效率更高。所以先把修改寫入日
志,能夠延遲刷盤時機,進而提高系統吞吐。
固然 redo log 也不是每一次都直接寫入磁盤,在 Buffer Pool 裏面有一塊內存區域
(Log Buffer)專門用來保存即將要寫入日誌文件的數據,默認 16M,它同樣能夠節省
磁盤 IO。
SHOW VARIABLES LIKE 'innodb_log_buffer_size';
須要注意:redo log 的內容主要是用於崩潰恢復。磁盤的數據文件,數據來自 buffer
pool。redo log 寫入磁盤,不是寫入數據文件。
那麼,Log Buffer 何時寫入 log file?
在咱們寫入數據到磁盤的時候,操做系統自己是有緩存的。flush 就是把操做系統緩
衝區寫入到磁盤。
log buffer 寫入磁盤的時機,由一個參數控制,默認是 1。
SHOW VARIABLES LIKE 'innodb_flush_log_at_trx_commit';
https://dev.mysql.com/doc/refman/5.7/en/innodb-parameters.html#sysvar_innodb_flush_log_at_trx_commit
這是內存結構的第 4 塊內容,redo log,它又分紅內存和磁盤兩部分。redo log 有
什麼特色?
一、redo log 是 InnoDB 存儲引擎實現的,並非全部存儲引擎都有。
二、不是記錄數據頁更新以後的狀態,而是記錄這個頁作了什麼改動,屬於物理日誌。
三、redo log 的大小是固定的,前面的內容會被覆蓋。
check point 是當前要覆蓋的位置。若是 write pos 跟 check point 重疊,說明 redo
log 已經寫滿,這時候須要同步 redo log 到磁盤中。
這是 MySQL 的內存結構,總結一下,分爲:
Buffer pool、change buffer、Adaptive Hash Index、 log buffer。
磁盤結構裏面主要是各類各樣的表空間,叫作 Table space。
3.3.2.磁盤結構
表空間能夠看作是 InnoDB 存儲引擎邏輯結構的最高層,全部的數據都存放在表空
間中。InnoDB 的表空間分爲 5 大類。
系統表空間 system tablespace
在默認狀況下 InnoDB 存儲引擎有一個共享表空間(對應文件/var/lib/mysql/
ibdata1),也叫系統表空間。
InnoDB 系統表空間包含 InnoDB 數據字典和雙寫緩衝區,Change Buffer 和 Undo
Logs),若是沒有指定 file-per-table,也包含用戶建立的表和索引數據。
一、undo 在後面介紹,由於有獨立的表空間。
二、數據字典:由內部系統表組成,存儲表和索引的元數據(定義信息)。
三、雙寫緩衝(InnoDB 的一大特性):
InnoDB 的頁和操做系統的頁大小不一致,InnoDB 頁大小通常爲 16K,操做系統頁
大小爲 4K,InnoDB 的頁寫入到磁盤時,一個頁須要分 4 次寫
若是存儲引擎正在寫入頁的數據到磁盤時發生了宕機,可能出現頁只寫了一部分的
狀況,好比只寫了 4K,就宕機了,這種狀況叫作部分寫失效(partial page write),可
能會致使數據丟失
show variables like 'innodb_doublewrite';
咱們不是有 redo log 嗎?可是有個問題,若是這個頁自己已經損壞了,用它來作崩
潰恢復是沒有意義的。因此在對於應用 redo log 以前,須要一個頁的副本。若是出現了
寫入失效,就用頁的副原本還原這個頁,而後再應用 redo log。這個頁的副本就是 double
write,InnoDB 的雙寫技術。經過它實現了數據頁的可靠性。
跟 redo log 同樣,double write 由兩部分組成,一部分是內存的 double write,
一個部分是磁盤上的 double write。由於 double write 是順序寫入的,不會帶來很大的
開銷。
在默認狀況下,全部的表共享一個系統表空間,這個文件會愈來愈大,並且它的空
間不會收縮。
獨佔表空間 file-per-table tablespaces
咱們可讓每張表獨佔一個表空間。這個開關經過 innodb_file_per_table 設置,默
認開啓
SHOW VARIABLES LIKE 'innodb_file_per_table';
開啓後,則每張表會開闢一個表空間,這個文件就是數據目錄下的 ibd 文件(例如
/var/lib/mysql/gupao/user_innodb.ibd),存放表的索引和數據。
可是其餘類的數據,如回滾(undo)信息,插入緩衝索引頁、系統事務信息,二次
寫緩衝(Double write buffer)等仍是存放在原來的共享表空間內。
通用表空間 general tablespaces
通用表空間也是一種共享的表空間,跟 ibdata1 相似。
能夠建立一個通用的表空間,用來存儲不一樣數據庫的表,數據路徑和文件能夠自定
義。語法:
create tablespace ts2673 add datafile '/var/lib/mysql/ts2673.ibd' file_block_size=16K engine=innodb;
在建立表的時候能夠指定表空間,用 ALTER 修改表空間能夠轉移表空間。
create table t2673(id integer) tablespace ts2673;
不一樣表空間的數據是能夠移動的。
刪除表空間須要先刪除裏面的全部表:
drop table t2673;
drop tablespace ts2673;
臨時表空間 temporary tablespaces
存儲臨時表的數據,包括用戶建立的臨時表,和磁盤的內部臨時表。對應數據目錄
下的 ibtmp1 文件。當數據服務器正常關閉時,該表空間被刪除,下次從新產生。
Redo log
磁盤結構裏面的 redo log,在前面已經介紹過了。
undo log tablespace
https://dev.mysql.com/doc/refman/5.7/en/innodb-undo-tablespaces.html
https://dev.mysql.com/doc/refman/5.7/en/innodb-undo-logs.html
undo log(撤銷日誌或回滾日誌)記錄了事務發生以前的數據狀態(不包括 select)。
若是修改數據時出現異常,能夠用 undo log 來實現回滾操做(保持原子性)。
在執行 undo 的時候,僅僅是將數據從邏輯上恢復至事務以前的狀態,而不是從物
理頁面上操做實現的,屬於邏輯格式的日誌。
redo Log 和 undo Log 與事務密切相關,統稱爲事務日誌。
undo Log 的數據默認在系統表空間 ibdata1 文件中,由於共享表空間不會自動收
縮,也能夠單首創建一個 undo 表空間。
show global variables like '%undo%';
有了這些日誌以後,咱們來總結一下一個更新操做的流程,這是一個簡化的過程。
name 原值是 lei1。
update user set name = 'penyuyan' where id=1;
一、事務開始,從內存或磁盤取到這條數據,返回給 Server 的執行器;
二、執行器修改這一行數據的值爲 penyuyan;
三、記錄 name=qingshan 到 undo log;
四、記錄 name=penyuyan 到 redo log;
五、調用存儲引擎接口,在內存(Buffer Pool)中修改 name=penyuyan;
六、事務提交。
內存和磁盤之間,工做着不少後臺線程。
3.3.3.後臺線程
(供瞭解)
後臺線程的主要做用是負責刷新內存池中的數據和把修改的數據頁刷新到磁盤。後
臺線程分爲:master thread,IO thread,purge thread,page cleaner thread。
master thread 負責刷新緩存數據到磁盤並協調調度其它後臺進程。
IO thread 分爲 insert buffer、log、read、write 進程。分別用來處理 insert buffer、
重作日誌、讀寫請求的 IO 回調。purge thread 用來回收 undo 頁。
page cleaner thread 用來刷新髒頁。
除了 InnoDB 架構中的日誌文件,MySQL 的 Server 層也有一個日誌文件,叫作
binlog,它能夠被全部的存儲引擎使用。
3.3. Binlog
https://dev.mysql.com/doc/refman/5.7/en/binary-log.html
binlog 以事件的形式記錄了全部的 DDL 和 DML 語句(由於它記錄的是操做而不是
數據值,屬於邏輯日誌),能夠用來作主從複製和數據恢復。
跟 redo log 不同,它的文件內容是能夠追加的,沒有固定大小限制。
在開啓了 binlog 功能的狀況下,咱們能夠把 binlog 導出成 SQL 語句,把全部的操
做重放一遍,來實現數據的恢復。
binlog 的另外一個功能就是用來實現主從複製,它的原理就是從服務器讀取主服務器
的 binlog,而後執行一遍。
配置方式和主從複製的實現原理在 Mycat 第二節課中有講述。
有了這兩個日誌以後,咱們來看一下一條更新語句是怎麼執行的:
例如一條語句:update teacher set name='盆魚宴' where id=1;
一、先查詢到這條數據,若是有緩存,也會用到緩存。
二、把 name 改爲盆魚宴,而後調用引擎的 API 接口,寫入這一行數據到內存,同時
記錄 redo log。這時 redo log 進入 prepare 狀態,而後告訴執行器,執行完成了,能夠隨時提交。
三、執行器收到通知後記錄 binlog,而後調用存儲引擎接口,設置 redo log爲 commit狀態。
四、更新完成。
這張圖片的重點(不必背下來):
一、先記錄到內存,再寫日誌文件
二、記錄 redo log 分爲兩個階段。
三、存儲引擎和 Server 記錄不一樣的日誌。
三、先記錄 redo,再記錄 binlog。