MySQL高階學習(第一天)
MySQL學習筆記(第一天)
參考文章:
MySQL為什麼不用陣列、雜湊表、二叉樹等資料結構作為索引呢
一、 MySQL的邏輯架構
連線層:最上層是一些客戶端和連線服務, 包含本地 sock 通訊和大多數基於客戶端/服務端工具實現的類似於 tcp/ip 的
通訊。 主要完成一些類似於連線處理、 授權認證、 及相關的安全方案。 在該層上引入了執行緒池的概念, 為通過認證
安全接入的客戶端提供執行緒。 同樣在該層上可以實現基於 SSL 的安全連結。 伺服器也會為安全接入的每個客戶端驗
證它所具有的操作許可權。
服務層:第二層主要完成核心服務功能,如SQL介面,並完成快取的查詢,SQL的分析和優化以及部分內建函式的執行。所有跨儲存引擎的功能也在這一層實現,如過程,函式等,在該層,伺服器會解析並建立相應的內部解析樹,並對其完成相應的優化如確定查詢表的順序,是否利用索引等,最後生成相應的執行操作。如果是select語句,伺服器還有查詢內部的快取。如果快取空間足夠大,這樣在解決大量讀操作的環境中可以很好的提高系統的效能。
Management Serveices & Utilities | 系統管理和控制工具 |
---|---|
SQL Interface: | SQL 介面。 接受使用者的 SQL 命令, 並且返回使用者需要查詢的結果。 比如 select from 就是呼叫 SQL Interface |
Parser | 解析器。 SQL 命令傳遞到解析器的時候會被解析器驗證和解析 |
Optimizer | 查詢優化器。 SQL 語句在查詢之前會使用查詢優化器對查詢進行優化, 比如有 where 條件時, 優化器來決定先投影還是先過濾。 |
Cache 和 Buffer | 查詢快取。 如果查詢快取有命中的查詢結果, 查詢語句就可以直接去查詢快取中取 資料。 這個快取機制是由一系列小快取組成的。 比如表快取, 記錄快取, key 快取, 許可權快取等 |
引擎層:儲存引擎層, 儲存引擎真正的負責了 MySQL 中資料的儲存和提取, 伺服器通過 API 與儲存引擎進行通訊。 不同
的儲存引擎具有的功能不同, 這樣我們可以根據自己的實際需要進行選取。
儲存層:資料儲存層, 主要是將資料儲存在運行於裸裝置的檔案系統之上, 並完成與儲存引擎的互動。
二、儲存引擎
show engines: 檢視所有的資料引擎。
show variables like '%storage_engine%' 檢視預設的資料庫引擎 。
對比項 | MyIASM | InnoDB |
---|---|---|
外來鍵 | 不支援 | 支援 |
事務 | 不支援 | 支援 |
行表鎖 | 表鎖,即使操作一條記錄也會鎖住整個表, 不適合高併發的操作 | 行鎖,操作時只鎖某一行, 不對其它行有影響, 適合高併發的操作 |
快取 | 只快取索引, 不快取真實資料 | 不僅快取索引還要快取真實資料, 對記憶體要求較高, 而且內 存大小對效能有決定性的影響(MySQL8已刪除) |
關注點 | 讀效能 | 併發寫、事務、資源 |
預設安裝 | Y | Y |
預設使用 | N | Y |
自帶系統表使用 | Y | N |
三、SQL載入順序
手寫SQL順序
select <select_list>
from <table_name>
<join_type> join <join_table> on <join_condition>
where <where_condition>
group by <group_by_list>
having <having_condition>
order by <order_by_condition>
limit <limt_number>
MySQL執行順序
FROM <left_table>
ON <join_condition>
<join_type> JOIN <right_table>
WHERE <where_condition>
GROUP BY <group_by_list>
HAVING <having_condition>
SELECT
DISTINCT <select_list>
ORDER BY <order_by_condition>
LIMIT <limit_number>
-
載入from子句的前兩個表計算笛卡爾積,生成虛擬表vt1;
-
篩選關聯表符合on表示式的資料,保留主表,生成虛擬表vt2;
-
如果使用的是外連線,執行on的時候,會將主表中不符合on條件的資料也載入進來,做為外部行
-
如果from子句中的表數量大於2,則重複第一步到第三步,直至所有的表都載入完畢,更新vt3;
-
執行where表示式,篩選掉不符合條件的資料生成vt4;
-
執行group by子句。group by 子句執行過後,會對子句組合成唯一值並且對每個唯一值只包含一行,生成vt5,。一旦執行group by,後面的所有步驟只能得到vt5中的列(group by的子句包含的列)和聚合函式。
-
執行聚合函式,生成vt6;
-
執行having表示式,篩選vt6中的資料。having是唯一一個在分組後的條件篩選,生成vt7;
-
從vt7中篩選列,生成vt8;
-
執行distinct,對vt8去重,生成vt9。其實執行過group by後就沒必要再去執行distinct,因為分組後,每組只會有一條資料,並且每條資料都不相同。
-
對vt9進行排序,此處返回的不是一個虛擬表,而是一個遊標,記錄了資料的排序順序,此處可以使用別名;
-
執行limit語句,將結果返回給客戶端
補充:
on和where的區別?
簡單地說,當有外關聯表時,on主要是針對外關聯表進行篩選,主表保留,當沒有關聯表時,二者作用相同。
例如在左外連時,首先執行on,篩選掉外連表中不符合on表示式的資料,而where的篩選是對主表的篩選。
四、七種JOIN
先準備兩張表
CREATE TABLE `tbl_emp` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(20) DEFAULT NULL,
`deptId` int(11) DEFAULT NULL,
PRIMARY KEY (`id`) ,
KEY `fk_dept_id`(`deptId`)
)ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8;
CREATE TABLE `tbl_dept` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`deptName` varchar(30) DEFAULT NULL,
`locAdd` varchar(40) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8;
INSERT INTO tbl_dept(deptName,locAdd) VALUES("RD",11);
INSERT INTO tbl_dept(deptName,locAdd) VALUES("HR",12);
INSERT INTO tbl_dept(deptName,locAdd) VALUES("MK",13);
INSERT INTO tbl_dept(deptName,locAdd) VALUES("MIS",14);
INSERT INTO tbl_dept(deptName,locAdd) VALUES("FD",15);
INSERT INTO tbl_emp(NAME,deptId) VALUES('z3',1);
INSERT INTO tbl_emp(NAME,deptId) VALUES('z4',1);
INSERT INTO tbl_emp(NAME,deptId) VALUES('z5',1);
INSERT INTO tbl_emp(NAME,deptId) VALUES('w5',2);
INSERT INTO tbl_emp(NAME,deptId) VALUES('w6',2);
INSERT INTO tbl_emp(NAME,deptId) VALUES('s7',3);
INSERT INTO tbl_emp(NAME,deptId) VALUES('s8',4);
INSERT INTO tbl_emp(NAME,deptId) VALUES('s9',51);
- 獲取A、B共有部分
SELECT * FROM tbl_emp a INNER JOIN tbl_dept b ON a.`deptId`=b.`id`;
我們可以看到沒有deptId為51的員工以及id為5的部門.
- A、B共有以及A的私有
SELECT * FROM tbl_emp a LEFT JOIN tbl_dept b ON a.`deptId`=b.`id` ;
通過左外連線,A表中所有的資料都被查詢了出來.
- A、B共有以及B私有
SELECT * FROM tbl_emp a RIGHT JOIN tbl_dept b ON a.`deptId`=b.`id` ;
通過右外連線,B表中所有的資料都被查詢了出來.
-
A私有
SELECT * FROM tbl_emp a LEFT JOIN tbl_dept b ON a.`deptId`=b.`id` WHERE b.`id` IS NULL;
- B私有
SELECT * FROM tbl_emp a RIGHT JOIN tbl_dept b ON a.`deptId`=b.`id` WHERE a.`id` IS NULL;
- AB全有
SELECT * FROM tbl_emp a LEFT JOIN tbl_dept b ON a.`deptId`=b.`id`
UNION
SELECT * FROM tbl_emp a RIGHT JOIN tbl_dept b ON a.`deptId`=b.`id` ;
補充:因為MySQL不支援全查詢,所以我們無法通過full join的方式去查詢,但是可以通過UNION關鍵字來進行查詢,
MySQL UNION 操作符用於連線兩個以上的 SELECT 語句的結果組合到一個結果集合中。多個 SELECT 語句會刪除重複的資料。
- A私有和B私有
select * from tbl_emp a left join tbl_dept b on a.`deptId`=b.`id` where b.`id` is null
union
SELECT * FROM tbl_emp a RIGHT JOIN tbl_dept b ON a.`deptId`=b.`id` WHERE a.`id` IS NULL;
五、索引
什麼是索引?
MySQL 官方對索引的定義為: 索引(Index) 是幫助 MySQL 高效獲取資料的資料結構。 可以得到索引的本質:索引是資料結構。 可以簡單理解為排好序的快速查詢資料結構。 在資料之外, 資料庫系統還維護著滿足特定查詢演算法的資料結構, 這些資料結構以某種方式引用(指向) 資料,這樣就可以在這些資料結構上實現高階查詢演算法。 這種資料結構, 就是索引。 下圖就是一種可能的索引方式示例:
左邊是資料表, 一共有兩列七條記錄, 最左邊的是資料記錄的實體地址。 為了加快 Col2 的查詢, 可以維護一個右邊所示的二叉查詢樹, 每個節點分別包含索引鍵值和一個指向對應資料記錄實體地址的指 針, 這樣就可以運用二叉查詢在一定的複雜度內獲取到相應資料, 從而快速的檢索出符合條件的記錄。一般來說索引本身也很大, 不可能全部儲存在記憶體中, 因此索引往往以索引檔案的形式儲存的磁碟上。
優缺點
優點:
- 提高資料檢索的效率,降低資料庫的IO成本
- 通過索引列對資料進行排序,降低資料排序的成本,降低了CPU的消耗
缺點:
- 雖然索引大大提高了查詢速度,同時卻會降低更新表的速度,如對錶進行INSERT、UPDATE和DELETE,因為更新表時,MySQL不僅要儲存資料,還要儲存一下索引檔案每次更新添加了索引列的欄位,都會調整因為更新所帶來的鍵值變化後的索引資訊。
- 實際上索引也是一張表,該表儲存了主鍵與索引欄位,並指向了實體表的記錄,所以索引列也是要佔用空間的。
索引分類
-
單值索引:一個索引只包含單個列,一個表可以有多個單列索引
-
唯一索引:索引列的值必須唯一,但允許有空值
-
主鍵索引:設定為主鍵後會自動建立索引,InnoDB為聚簇索引
-
複合索引:即一個索引包含多個列
-
全文索引: 通過建立
倒排索引
,可以極大的提升檢索效率,解決判斷欄位是否包含的問題. 例如: 有title欄位,需要查詢所有包含 "政府"的記錄. 需要 like "%政府%"方式查詢,查詢速度慢,當查詢包含"政府" OR "中國"的需要是,sql難以簡單滿足.全文索引就可以實現這個功能.倒排索引(英語:Inverted index),也常被稱為反向索引、置入檔案或反向檔案,是一種索引方法,被用來儲存在全文搜尋下某個單詞在一個文件或者一組文件中的儲存位置的對映。它是文件檢索系統中最常用的資料結構。
建索引語句:
CREATE INDEX idx_customer_name ON customer(customer_name); # 單值索引
CREATE UNIQUE INDEX idx_customer_no ON customer(customer_no); # 唯一索引
ALTER TABLE customer add PRIMARY KEY customer(customer_no); # 單獨建立主鍵索引
ALTER TABLE customer drop PRIMARY KEY ; # 刪除主鍵索引 主鍵索引一般隨著建表語句而設定
CREATE INDEX idx_no_name ON customer(customer_no,customer_name);# 複合索引
create fulltext index content_tag_fulltext on fulltext_test(content,tag); # 全文索引
alter table fulltext_test add fulltext index content_tag_fulltext(content,tag);#全文索引
索引的建立時機
適合建立索引的情況
- 主鍵自動建立唯一索引
- 頻繁作為查詢條件的欄位應該建立索引
- 查詢中與其它表關聯的欄位,外來鍵關係應該建立索引
- 單鍵/組合索引的選擇問題,組合索引價效比更好
- 查詢中排序的欄位,排序欄位若通過索引去訪問將大大提高排序速度
- 查詢中統計或者分組欄位
不適合建立索引的情況
- 表記錄太少
- 頻繁更新的欄位或表
- where條件裡用不到的欄位不用建索引
- 過濾性不好的不適合建立索引
B-Tree與B+Tree
筆者認為自己目前水平有限,沒有完全理解,所以找了兩篇不錯的部落格,後續有時間的話會更新。