Home
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try a Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

4

Local AI

--

Uedu Code

--

Taipei AQI 57 · Taichung AQI 74 · Tainan AQI 63 · Kaohsiung AQI 64
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 09

儲存引擎

InnoDB、MyISAM、MEMORY 等引擎的差別與全文檢索
Interaction實作 25 minute(s)

Learning Objectives

  • 說明什麼是儲存引擎,用 SHOW ENGINES 與 information_schema.TABLES 查詢可用的引擎與每張表格的引擎
  • 比較 InnoDB 與 MyISAM:交易、外鍵、鎖定範圍、資料排列方式
  • 說出 MEMORY、CSV、ARCHIVE、BLACKHOLE、MERGE 各自的用途與限制,並用 ALTER TABLE … ENGINE= 轉換引擎
  • 建立 FULLTEXT 索引,分辨全文檢索的自然語言模式與布林模式
  • 知道 MySQL 8.0 已移除的舊語法與舊引擎:TYPE=、BDB、ISAM,以及 HEAP 改名為 MEMORY

什麼是儲存引擎

MySQL 可以分成兩層來看。上層負責解析 SQL、檢查權限、決定查詢怎麼執行; 下層的儲存引擎(storage engine)負責把資料真正寫進硬碟(或記憶體)、建立索引、處理鎖定。 同一個 SQL 敘述交給不同的儲存引擎執行,資料存放的方式、能不能復原、多人同時寫入時的表現都不一樣。

MySQL 的特色是每張表格可以各自選擇儲存引擎,同一個資料庫裡可以同時有 InnoDB 和 MyISAM 的表格。建立表格時用 ENGINE= 指定:

CREATE TABLE asset (
  assetID INT NOT NULL,
  description VARCHAR(255)
) ENGINE=MyISAM;

沒有寫 ENGINE= 時,使用系統變數 default_storage_engine 的值。MySQL 5.5 起預設引擎是 InnoDB,MySQL 8.0 也是。

舊教材的寫法 TYPE=MyISAM 在 MySQL 5.5 已經移除,8.0 執行會出現語法錯誤(ERROR 1064),一律改寫成 ENGINE=MyISAM。

查詢可用的引擎與表格的引擎

範例在 shop 資料庫裡建立兩張表格:一張指定 MyISAM,一張沒有指定。接著從 information_schema.TABLES 查詢每張表格的引擎,再用 ALTER TABLE 把 MyISAM 的表格轉成 InnoDB。

SHOW ENGINES 的欄位與 ALTER TABLE … ENGINE=

欄位 意思
Engine 引擎名稱
Support DEFAULT(預設引擎)、YES(可以使用)、NO(這個版本沒有提供)
Transactions 是否支援交易(下一課的主題)
XA 是否支援分散式交易
Savepoints 是否支援交易中的存檔點

列出的引擎依安裝的版本與套件而略有不同;在 MySQL 8.0 只有 InnoDB 的 Transactions 是 YES。

查詢表格的引擎有兩種方式:

SHOW TABLE STATUS LIKE 'asset';          -- Engine 欄位
SELECT TABLE_NAME, ENGINE
FROM information_schema.TABLES
WHERE TABLE_SCHEMA = 'shop';             -- 一次列出整個資料庫

information_schema 是 MySQL 內建、唯讀的資料庫,裡面的表格描述了「伺服器上有哪些資料庫、表格、欄位」。 本課的直譯器只模擬了它常用的幾張表格;SHOW TABLE STATUS 裡的列數、檔案大小等數字也是簡化的示意值。

ALTER TABLE … ENGINE= 會重建整張表格:MySQL 建立一張新引擎的空表格,把資料逐列複製過去,再取代原本的表格。 表格很大時要花很久,而且新引擎不支援的定義(例如 MEMORY 不能有 TEXT 欄位)會讓轉換失敗。

InnoDB 與 MyISAM

課程原本以 MyISAM 為主(它在 MySQL 5.5 以前是預設引擎),現在幾乎所有新表格都使用 InnoDB:

InnoDB MyISAM
交易(COMMIT/ROLLBACK) 支援 不支援,每個敘述執行完就寫入
外鍵 支援,會檢查參照完整性 語法可以寫,但會被忽略,不做任何檢查
鎖定範圍 資料列層級:多人修改不同的列可以同時進行 表格層級:有人寫入時,整張表格的其他寫入都要等
當機後復原 依 redo log 自動復原到最後一次提交 資料可能損毀,需要 REPAIR TABLE 修復
資料排列 依主鍵排列(叢集索引) 依寫入的先後存放
檔案 每張表格一個 .ibd 檔 .MYD(資料)、.MYI(索引)、.sdi(表格定義)

依主鍵排列的意思是:InnoDB 的資料本身就存放在主鍵索引裡,不管寫入的順序如何,資料列都依主鍵由小到大排好。 沒有定義主鍵時,InnoDB 會用第一個 UNIQUE NOT NULL 的索引,再沒有就自己產生一個隱藏的列編號。

舊投影片提到 MyISAM 的 .frm 檔:MySQL 8.0 把表格定義改存在 InnoDB 裡的資料字典,.frm 檔已經不再使用,MyISAM 改用 .sdi 檔保存表格定義。 mysql 系統資料庫的表格在 8.0 也大多改成了 InnoDB(記錄查詢日誌的 general_log、slow_log 是 CSV 表格)。

MyISAM 目前的用途很少:舊系統留下的表格、只讀不寫的資料。需要交易、外鍵或多人同時寫入時一律使用 InnoDB。

同樣的資料,兩種引擎的差別

兩張部門表格的結構相同,只有引擎不同。以 30、10、20 的順序寫入同樣的三筆資料,再用沒有 ORDER BY 的 SELECT 讀出來。最後在 MyISAM 的 staff_myisam 寫入一筆部門編號 99 的員工:這張表格建立時寫了外鍵,但 MyISAM 不會檢查。

其他儲存引擎

引擎 資料存在哪裡 特點與限制 適合的用途
MEMORY 記憶體 速度快;伺服器重新啟動後資料全部消失(表格定義還在);不能有 TEXT、BLOB 欄位;表格層級鎖定;大小受 max_heap_table_size 限制 暫存、可以重新產生的查詢結果
CSV 逗號分隔的文字檔(.CSV),可以直接用試算表開啟 所有欄位都必須是 NOT NULL;不能建立索引(所以也不能有主鍵與 AUTO_INCREMENT) 與其他程式交換資料
ARCHIVE 壓縮後的檔案 只能新增(INSERT)與查詢(SELECT),不能 UPDATE、DELETE;只有 AUTO_INCREMENT 欄位可以有索引 很少讀取、只增不改的紀錄,例如稽核日誌
BLACKHOLE 不存 寫入的資料全部丟掉,SELECT 永遠是空的 測試 SQL 語法;複寫(replication)架構中只轉送、不保存資料的中繼伺服器
MERGE(MRG_MyISAM) 由多張結構相同的 MyISAM 表格組成 本身不存資料,查詢時把各表格的資料合在一起 依月份分開存放的日誌(下一段)

已經不存在的引擎:原課程提到的幾個名稱在 MySQL 8.0 都不能用了。

  • ISAM:MyISAM 的前身,MySQL 5.0 移除
  • BDB(Berkeley DB):早期支援交易的引擎,MySQL 5.1 移除,交易功能由 InnoDB 取代
  • HEAP:就是 MEMORY 的舊名稱。8.0 仍接受 ENGINE=HEAP 這個寫法,但建立出來的表格顯示為 MEMORY,新程式請直接寫 MEMORY

指定不存在的引擎(例如 ENGINE=BDB)會出現 ERROR 1286 (42000): Unknown storage engine 'BDB'。

MEMORY、CSV、ARCHIVE 與 BLACKHOLE 的限制

每種引擎先示範一次它不接受的寫法,再示範正確的寫法。紅色的步驟是錯誤訊息:注意每個錯誤代碼對應的是哪一種限制。

MERGE 表格:把每個月的日誌合起來查詢

課程的情境:系統每個月建立一張日誌表格(log2003Jan、log2003Feb、log2003Mar), 單一表格不會太大,舊月份也容易備份或刪除;但查詢「這一季全部的日誌」時,希望像查一張表格一樣方便。MERGE 表格就是做這件事:

CREATE TABLE logs (
  logid INT AUTO_INCREMENT PRIMARY KEY,
  logts DATETIME,
  entry CHAR(255)
) ENGINE=MERGE UNION=(log2003Jan, log2003Feb, log2003Mar) INSERT_METHOD=LAST;
  • UNION=(…) 列出組成的表格。這些表格必須都是 MyISAM,而且欄位與索引的定義完全相同,否則查詢時會出現 ERROR 1168
  • INSERT_METHOD 決定寫入 MERGE 表格的資料放到哪裡:FIRST(第一張)、LAST(最後一張)、NO(不能寫入,這是預設值)
  • MERGE 表格的主鍵不會跨表格檢查:三張表格各自的 logid 都從 1 開始,合在一起就有三個 1
  • 引擎名稱寫 MERGE 或 MRG_MyISAM 都可以,查詢時顯示為 MRG_MyISAM

表格名稱的大小寫:MySQL 把每張表格存成檔案,在 Linux(lower_case_table_names=0,Ubuntu 的預設)上, log2003Jan 和 log2003jan 是兩個不同的檔案名稱,寫錯大小寫就會找不到表格;Windows 預設不分大小寫(值為 1)。 表格名稱一律照建立時的寫法,不要依賴不分大小寫。

MERGE 只能搭配 MyISAM。現在同樣的需求多半改用 InnoDB 的分割表格(PARTITION BY RANGE),一張表格依日期分成多個分割區,同時保有交易與資料列鎖定。

MERGE 表格的查詢與寫入

執行前,logs 資料庫裡已經有三張月份表格,各有一筆資料。範例建立 MERGE 表格 logs,透過它查詢與寫入,再故意把表格名稱的大小寫寫錯。

想一想:各種引擎的限制

1.

在 MySQL 8.0 執行下面的敘述,結果是什麼?

CREATE TABLE export_csv (id INT, name VARCHAR(20)) ENGINE=CSV;
2.

三張月份表格各有 1 筆資料。建立 MERGE 表格時寫的是 INSERT_METHOD=FIRST,再寫入一筆:

CREATE TABLE logs ( … ) ENGINE=MERGE
  UNION=(log2003Jan, log2003Feb, log2003Mar) INSERT_METHOD=FIRST;
INSERT INTO logs (logts, entry) VALUES ('2003-03-15', 'x');
SELECT COUNT(*) FROM log2003Jan;

最後一行的結果是什麼?

3.

一張 MEMORY 表格裡有 100 筆資料。MySQL 伺服器重新啟動之後,這張表格會怎樣?

第 1 次作答

全文檢索:在文章裡找詞

要找內文提到 merger 的文章,第一個想到的寫法是 WHERE body LIKE '%merger%'。 這個寫法可以用,但有兩個問題:開頭是 % 時用不到索引,每次都要讀完整張表格;而且只能回答「有沒有」,不能依相關程度排序。

全文檢索(full-text search)先把文字切成一個一個的詞,建立「哪個詞出現在哪些資料列」的索引,查詢時再算出每一列的相關度。 課程的 article 表格在 title 與 body 兩個欄位上建立一個 FULLTEXT 索引:

CREATE TABLE article (
  articleID INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  title VARCHAR(255),
  body TEXT,
  FULLTEXT (title, body)
) ENGINE=MyISAM;

SELECT title
FROM article
WHERE MATCH (title, body) AGAINST ('merger');
  • MATCH (…) 裡的欄位必須和某一個 FULLTEXT 索引的欄位完全相同。只寫 MATCH (title) 會出現 ERROR 1191 (HY000): Can't find FULLTEXT index matching the column list
  • 舊投影片說「只有 MyISAM 支援全文檢索」:這是 MySQL 5.5 以前的情況,InnoDB 從 MySQL 5.6 起也支援 FULLTEXT 索引
  • 中文沒有空白分隔詞,預設的切詞方式不適用。中文內容要在建立索引時加上 WITH PARSER ngram,依固定長度(預設 2 個字)切詞

自然語言模式(natural language mode,預設的模式)的規則,以 MyISAM 為準:

規則 說明
至少符合一個詞 AGAINST ('merge acquisition takeover') 找出含有其中任何一個詞的列
依相關度排序 MATCH 寫在 WHERE 裡、又沒有 ORDER BY 時,結果依相關度由高到低排列
50% 門檻 出現在一半(含)以上資料列的詞被當成太常見的詞,不計分,所以查不到任何一列
最短詞長 少於 4 個字元的詞不會被索引(ft_min_word_len,MyISAM 預設 4)
停用詞 the、and、about 這類常見的詞不會被索引

50% 門檻常讓初學者困惑:在只有兩三筆資料的測試表格裡,幾乎每個詞都超過一半,什麼都查不到。

自然語言模式的全文檢索

article 表格有 6 篇文章(MyISAM)。第二個查詢把 MATCH 放在 SELECT 清單,可以看到每一列的相關度分數:沒有出現 merger 的列分數是 0。最後兩個查詢都查不到資料,但原因不同。

布林模式與 InnoDB 的全文檢索

在 AGAINST 的字串後面加上 IN BOOLEAN MODE,就可以用運算子控制條件。布林模式沒有 50% 門檻:

寫法 意思
linux java 沒有運算子:含有任一個詞即可
+linux 一定要有 linux
-desktop 不能有 desktop
~Oracle 含有 Oracle 的列相關度降低,但不排除
"Open Source" 片語:兩個詞要依序相連出現
data* 以 data 開頭的詞

課程的範例:

SELECT title FROM article
WHERE MATCH (title, body)
      AGAINST ('+linux +"Open Source" -desktop Java ~Oracle' IN BOOLEAN MODE);

意思是:一定要有 linux、一定要有片語 Open Source、不能有 desktop;有 Java 的相關度提高,有 Oracle 的相關度降低。 布林模式不會自動依相關度排序,需要固定順序時自己加 ORDER BY。本課的直譯器在布林模式下的分數是簡化的計算,題目只看查到哪些列。

InnoDB 的全文檢索規則有幾點不同:

MyISAM InnoDB
50% 門檻 有 沒有
最短詞長 4(ft_min_word_len) 3(innodb_ft_min_token_size)
內建停用詞 數百個英文常用詞 36 個
相關度公式 MyISAM 自己的公式 不同的公式,同樣的查詢分數不同

最短詞長是伺服器啟動時讀取的設定,修改後要重新啟動 MySQL,並重建 FULLTEXT 索引才會生效。

布林模式,以及轉成 InnoDB 之後

同一張 article 表格。前三個查詢使用布林模式;接著把表格轉成 InnoDB,再查一次上一個範例查不到的 SQL。

想一想:全文檢索

1.

article(MyISAM,6 列)裡,software 出現在其中 3 列。下面的查詢結果是什麼?

SELECT COUNT(*) FROM article
WHERE MATCH (title, body) AGAINST ('software');
2.

merger 出現在第 1 篇與第 5 篇;第 5 篇的內文還提到 telecom。下面的查詢傳回哪一篇的 articleID?

SELECT articleID FROM article
WHERE MATCH (title, body) AGAINST ('+merger -telecom' IN BOOLEAN MODE);
3.

把 article 轉成 InnoDB 之後,同樣用自然語言模式查 software:

ALTER TABLE article ENGINE=InnoDB;
SELECT COUNT(*) FROM article
WHERE MATCH (title, body) AGAINST ('software');

結果是什麼?

第 1 次作答

動手寫:布林模式的全文檢索

news 資料庫的 article 表格(MyISAM,FULLTEXT 索引在 title, body 上)已經準備好了。請列出提到 java 或 linux、但沒有提到 desktop 的文章的 articleID 與 title,依 articleID 由小到大排序。

你的輸出
預期輸出

重點整理

引擎 重點
InnoDB 預設引擎;交易、外鍵、資料列鎖定、當機自動復原,資料依主鍵排列
MyISAM 不支援交易與外鍵,表格層級鎖定;舊系統才會用到
MEMORY 存在記憶體,重新啟動後資料消失;不能有 TEXT、BLOB
CSV 文字檔,欄位必須 NOT NULL,不能有索引
ARCHIVE 壓縮存放,只能 INSERT 與 SELECT
BLACKHOLE 寫入的資料直接丟掉
MERGE(MRG_MyISAM) 把結構相同的 MyISAM 表格合起來查詢;INSERT_METHOD 決定寫到哪一張
  • 指定引擎用 ENGINE=(TYPE= 已移除);轉換用 ALTER TABLE 表格 ENGINE=引擎,會重建整張表格
  • 查詢引擎:SHOW ENGINES、information_schema.TABLES 的 ENGINE 欄位
  • ISAM、BDB 已移除;HEAP 是 MEMORY 的舊名稱
  • 全文檢索:FULLTEXT (欄位) 建立索引,MATCH (欄位) AGAINST ('詞') 查詢,MATCH 的欄位要與索引完全相同
  • 自然語言模式(MyISAM)有 50% 門檻、最短詞長 4;布林模式用 +、-、~、"片語" 控制條件;InnoDB 沒有 50% 門檻、最短詞長 3

下一課介紹 InnoDB 最重要的功能:交易。

這一課完成了!
Next lesson:交易
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。