MySQL高级--个人笔记
?學習視頻:尚硅谷MySQL數據庫高級,mysql優化,數據庫優化_嗶哩嗶哩_bilibili
文檔是復制尚硅谷的復習資料,個人只是為了方便復習。
?目錄
Mysql 邏輯架構簡介
1. 整體架構圖
1.1 連接層
1.2 服務層
1.3.引擎層
1.4.存儲層
2. show profile
2.1 開啟 profile
2.2 使用 profile
?2.3 大致的查詢流程
2.4 SQL 的執行順序
2.5 MyISAM 和 InnoDB?
第 4 章 SQL 預熱?
1. 常見的 Join 查詢圖
?2. Join 示例
2.1 建表語句
2.2 案例
?第 5 章 索引優化分析
1. 索引的概念
1.1 是什么
1.2 優缺點
2. Mysql 的索引
2.1 Btree 索引
2.2 B+tree 索引 ?
2.3 聚簇索引和非聚簇索引
2.4 時間復雜度(擴展)
3. Mysql 索引分類
3.1 單值索引
3.2 唯一索引
3.3 主鍵索引
3.4 復合索引
3.5 基本語法
4. 索引的創建時機
4.1 適合創建索引的情況
4.2 不適合創建索引的情況
5. 性能分析
5.1 MySql Query Optimizer
5.2 MySQL常見瓶頸
第 6 章 Explain 性能分析
1. 概念
2. Explain 準備工作
3. id
4. select_type
4.1 SIMPLE
4.2 PRIMARY
4.3 DERIVED
4.4 SUBQUERY
4.5 DEPENDENT SUBQUERY
4.6 UNCACHEABLE SUBQUREY
4.7 UNION
4.8 UNION RESULT
5. table
6. type
6.1 system
6.2 const
6.3 eq_ref
6.4 ref
6.5 range
6.6 index
6.8 index_merge
6.9 ref_or_null
6.10 index_subquery?
??6.11 unique_subquery
7. possible_keys
8. key
9. key_len
10. ref
11. rows
12. Extra
12.1 Using filesort
12.2 Using temporary
?12.3 Using index
12.4 Using where
12.5 Using join buffer
12.6 impossible where
?12.7 select tables optimized away
?第 7 章 批量數據腳本
1. 插入數據
1.1 建表語句
1.2 設置參數
1.3 編寫隨機函數
1.4 創建存儲過程
1.5 調用存儲過程
1.6 批量刪除某個表上的所有索引
第 8 章 單表使用索引常見的索引失效
1. 全值匹配我最愛
1.1 有以下 SQL 語句
1.2 建立索引
?2. 最佳左前綴法則
3. 不要在索引列上做任何計算
3.1 在查詢列上使用了函數
3.2 在查詢列上做了轉換
4. 索引列上不能有范圍查詢
5. 盡量使用覆蓋索引
?6. 使用不等于(!= 或者<>)的時候
7. 字段的 is not null 和 is null?
?8. like 的前后模糊匹配
9. 減少使用 or
10. 練習
11. 口訣
第 9 章 關聯查詢優化
1. 建表語句
2. 案例
2.1 left join
?2.2 inner join
??2.3 四個關聯查詢案例分析
?第 10 章 子查詢優化
1. 案例?
第 11 章 排序分組優化
1. 無過濾不索引
2. 順序錯,必排序
3. 方向反,必排序
4. 索引的選擇?
?
5. using filesort
5.1 mysql 的排序算法
5.2 如何優化
6. 使用覆蓋索引
?7. group by
?第 12 章 練習
1. 案例一
2. 案例二
3. 案例三?
4. 案例四?
5. 案例五?
6. 案例六
7. 案例七
MySQL?邏輯架構簡介
1. 整體架構圖
和其它數據庫相比, MySQL 有點與眾不同,它的架構可以在多種不同場景中應用并發揮良好作用。主要體現在 存儲引擎的架構上,插件式的存儲引擎架構將查詢處理和其它的系統任務以及數據的存儲提取相分離。這種架構可以根據業務的需求和實際需要選擇合適的存儲引擎。 各層介紹:1.1 連接層
最上層是一些客戶端和連接服務,包含本地 sock 通信和大多數基于客戶端 / 服務端工具實現的類似于 tcp/ip 的通信。主要完成一些類似于連接處理、授權認證、及相關的安全方案。在該層上引入了線程池的概念,為通過認證安全接入的客戶端提供線程。同樣在該層上可以實現基于 SSL 的安全鏈接。服務器也會為安全接入的每個客戶端驗證它所具有的操作權限。1.2 服務層
| SQL Interface: | SQL 接口。接受用戶的 SQL 命令,并且返回用戶需要查詢的結果。比如 select from 就是調用 SQL Interface |
| Parser | 解析器。 SQL 命令傳遞到解析器的時候會被解析器驗證和解析 |
| Optimizer | 查詢優化器。 SQL 語句在查詢之前會使用查詢優化器對查詢進行優化,比如有 where 條件時,優化器來決定先投影還是先過濾。 |
| Cache 和 Buffer | 查詢緩存。如果查詢緩存有命中的查詢結果,查詢語句就可以直接去查詢緩存中取數據。這個緩存機制是由一系列小緩存組成的。比如表緩存,記錄緩存,key 緩存, 權限緩存等 |
1.3.引擎層
存儲引擎層,存儲引擎真正的負責了 MySQL 中數據的存儲和提取,服務器通過 API 與存儲引擎進行通信。不同的存儲引擎具有的功能不同,這樣我們可以根據自己的實際需要進行選取。1.4.存儲層
數據存儲層,主要是將數據存儲在運行于裸設備的文件系統之上,并完成與存儲引擎的交互。2. show profile
利用 show profile 可以查看 sql 的執行周期!2.1 開啟 profile
查看 profile 是否開啟: show variables like '%profiling%'? 如果沒有開啟,可以執行 set profiling=1 開啟!2.2 使用 profile
執行 show prifiles 命令,可以查看最近的幾次查詢。?根據 Query_ID,可以進一步執行 show profile cpu,block io for query Query_id 來查看 sql 的具體執行步驟。
?2.3 大致的查詢流程
MySQL 的查詢流程大致是: ????????MySQL?客戶端通過協議與 MySQL 服務器建連接,發送查詢語句,先檢查查詢緩存,如果命中,直接返回結果,否則進行語句解析, 也就是說,在解析查詢之前,服務器會先訪問查詢緩存(query cache)——它存儲 SELECT 語句以及 相應的查詢結果集。如果某個查詢結果已經位于緩存中,服務器就不會再對查詢進行解析、優化、以及執行。它僅僅將緩存中的結果返回給用戶即可,這將大大提高系統的性能。 ????????語法解析器和預處理:首先MySQL通過關鍵字將 SQL 語句進行解析,并生成一顆對應的“解析樹”。MySQL解析器將使用 MySQL語法規則驗證和解析查詢;預處理器則根據一些MySQL規則進一步檢查解析樹是否合法。 ????????查詢優化器當解析樹被認為是合法的了,并且由優化器將其轉化成執行計劃。一條查詢可以有很多種執行方式, 最后都返回相同的結果。優化器的作用就是找到這其中最好的執行計劃。然后,MySQL 默認使用的 BTREE 索引,并且一個大致方向是 : 無論怎么折騰 sql,至少在目前來說,MySQL最多只用到表中的一個索引。2.4 SQL 的執行順序
手寫的順序: 真正執行的順序: ????????隨著 MySQL? 版本的更新換代,其優化器也在不斷的升級,優化器會分析不同執行順序產生的性能消耗不同而動態調整執行順序。下面是經常出現的查詢順序:?
2.5 MyISAM 和 InnoDB?
| 對比項 | MyISAM | InnoDB |
| 外鍵 | 不支持 | 支持 |
| 事務 | 不支持 | 支持 |
| 行表鎖 | 表鎖,即使操作一條記錄也會鎖住整個表, 不適合高并發的操作 | 行鎖, 操作時只鎖某一行,不對其它行有影響, 適合高并發 的操作 |
| 緩存 | 只緩存索引,不緩存真實數據 | 不僅緩存索引還要緩存真實數據,對內存要求較高,而且內存大小對性能有決定性的影響 |
| 關注點 | 讀性能 | 并發寫、事務、資源 |
| 默認安裝 | Y | Y |
| 默認使用 | N | Y |
| 自 帶 系 統 表 使用 | Y | N |
?show engines:查看所有的數據庫引擎
show variables like '%storage_engine%' 查看默認的數據庫引擎?
第 4 章 SQL 預熱?
1. 常見的 Join 查詢圖
?2. Join 示例
2.1 建表語句
CREATE TABLE `t_dept` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `deptName` VARCHAR(30) DEFAULT NULL, `address` VARCHAR(40) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; CREATE TABLE `t_emp` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `name` VARCHAR(20) DEFAULT NULL, `age` INT(3) DEFAULT NULL, `deptId` INT(11) DEFAULT NULL, empno int not null, PRIMARY KEY (`id`), KEY `idx_dept_id` (`deptId`) #CONSTRAINT `fk_dept_id` FOREIGN KEY (`deptId`) REFERENCES `t_dept` (`id`) ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; INSERT INTO t_dept(deptName,address) VALUES(' 華山 ',' 華山 '); INSERT INTO t_dept(deptName,address) VALUES(' 丐幫 ',' 洛陽 '); INSERT INTO t_dept(deptName,address) VALUES(' 峨眉 ',' 峨眉山 '); INSERT INTO t_dept(deptName,address) VALUES(' 武當 ',' 武當山 '); INSERT INTO t_dept(deptName,address) VALUES(' 明教 ',' 光明頂 '); INSERT INTO t_dept(deptName,address) VALUES(' 少林 ',' 少林寺 '); INSERT INTO t_emp(NAME,age,deptId,empno) VALUES(' 風清揚 ',90,1,100001); INSERT INTO t_emp(NAME,age,deptId,empno) VALUES(' 岳不群 ',50,1,100002); INSERT INTO t_emp(NAME,age,deptId,empno) VALUES(' 令狐沖 ',24,1,100003); INSERT INTO t_emp(NAME,age,deptId,empno) VALUES(' 洪七公 ',70,2,100004); INSERT INTO t_emp(NAME,age,deptId,empno) VALUES(' 喬峰 ',35,2,100005); INSERT INTO t_emp(NAME,age,deptId,empno) VALUES(' 滅絕師太 ',70,3,100006); INSERT INTO t_emp(NAME,age,deptId,empno) VALUES(' 周芷若 ',20,3,100007); INSERT INTO t_emp(NAME,age,deptId,empno) VALUES(' 張三豐 ',100,4,100008); INSERT INTO t_emp(NAME,age,deptId,empno) VALUES(' 張無忌 ',25,5,100009); INSERT INTO t_emp(NAME,age,deptId,empno) VALUES(' 韋小寶 ',18,null,100010);2.2 案例
1.所有有部門人員的信息(要求顯示部門名稱)select * from t_emp a inner join t_dept b on a.deptId = b.id
?2. 列出所有人員及其部門信息
select * from t_emp a left join t_dept b on a.deptId = b.id
?3. 列出所有部門的人員信息
select * from t_emp a right join t_dept b on a.deptId = b.id
? 4. 列出沒有部門的人員信息
select * from t_emp a left join t_dept b on a.deptId = b.id where b.id is null
?5. 列出沒有人員的部門信息
select * from t_emp a right join t_dept b on a.deptId = b.id where a.id is null
??6. 所有人員和部門的對應關系
select * from t_emp a left join t_dept b on a.deptId = b.id
union
select * from t_emp a right join t_dept b on a.deptId = b.id
?7. 所有沒有入部門的人員和沒人入的部門
select * from t_emp a left join t_dept b on a.deptId = b.id where b.id is null
union
select * from t_emp a right join t_dept b on a.deptId = b.id where a.id is null
?
第 5 章 索引優化分析
1. 索引的概念
1.1 是什么
MySQL 官方對索引的定義為:索引(Index )是幫助 MySQL 高效獲取數據的數據結構??梢缘玫剿饕谋举|: 索引是數據結構。可以簡單理解為 排好序的快速查找數據結構 。 在數據之外,數據庫系統還維護著滿足特定查找算法的數據結構,這些數據結構以某種方式引用(指向)數據,這樣就可以在這些數據結構上實現高級查找算法。這種數據結構,就是索引。下圖就是一種可能的索引方式示例: 左邊是數據表,一共有兩列七條記錄,最左邊的是數據記錄的物理地址 。 為了加快 Col2 的查找,可以維護一個右邊所示的二叉查找樹,每個節點分別包含索引鍵值和一個指向對應數據記錄物理地址的指針,這樣就可以運用二叉查找在一定的復雜度內獲取到相應數據,從而快速的檢索出符合條件的記錄。 一般來說索引本身也很大,不可能全部存儲在內存中,因此索引往往以索引文件的形式存儲的磁盤上。1.2 優缺點
優勢 :- 提高數據檢索的效率,降低數據庫的IO成本。
- 通過索引列對數據進行排序,降低數據排序的成本,降低了CPU的消耗。
- 雖然索引大大提高了查詢速度,同時卻會降低更新表的速度,如對表進行INSERT、UPDATE和DELETE。因為更新表時,MySQL不僅要保存數據,還要保存一下索引文件每次更新添加了索引列的字段,都會調整因為更新所帶來的鍵值變化后的索引信息。
- 實際上索引也是一張表,該表保存了主鍵與索引字段,并指向實體表的記錄,所以索引列也是要占用空間的
2. MySQL?的索引
2.1 Btree 索引
MySQL 使用的是 Btree 索引。 【初始化介紹】 一顆 b 樹,淺藍色的塊我們稱之為一個磁盤塊,可以看到每個磁盤塊包含幾個數據項(深藍色所示)和指針(黃色所示) 如磁盤塊 1 包含數據項 17 和 35 ,包含指針 P1 、 P2 、 P3 , P1 表示小于 17 的磁盤塊, P2 表示在 17 和 35 之間的磁盤塊, P3 表示大于 35 的磁盤塊。 真實的數據存在于葉子節點即 3 、 5 、 9 、 10 、 13 、 15 、 28 、 29 、 36 、 60 、 75 、 79 、 90 、 99 。 非葉子節點不存儲真實的數據,只存儲指引搜索方向的數據項,如 17 、 35 并不真實存在于數據表中。 【查找過程】 如果要查找數據項 29 ,那么首先會把磁盤塊 1 由磁盤加載到內存,此時發生一次 IO ,在內存中用二分查找確定 29在 17 和 35 之間,鎖定磁盤塊 1 的 P2 指針,內存時間因為非常短(相比磁盤的 IO)可以忽略不計,通過磁盤塊 1 的 P2 指針的磁盤地址把磁盤塊 3 由磁盤加載到內存,發生第二次 IO , 29 在 26 和 30 之間,鎖定磁盤塊 3 的 P2 指 針,通過指針加載磁盤塊 8 到內存,發生第三次 IO,同時內存中做二分查找找到 29 ,結束查詢,總計三次 IO 。 真實的情況是, 3 層的 b+ 樹可以表示上百萬的數據,如果上百萬的數據查找只需要三次 IO ,性能提高將是巨大的,如果沒有索引,每個數據項都要發生一次 IO ,那么總共需要百萬次的 IO ,顯然成本非常非常高。2.2 B+tree 索引 ?
B+Tree 與 B-Tree 的區別 1 ) B- 樹的關鍵字和記錄是放在一起的,葉子節點可以看作外部節點,不包含任何信息; B+ 樹的非葉子節點中只有關鍵字和指向下一個節點的索引,記錄只放在葉子節點中。 2 )在 B- 樹中,越靠近根節點的記錄查找時間越快,只要找到關鍵字即可確定記錄的存在;而 B+ 樹中每個記錄的查找時間基本是一樣的,都需要從根節點走到葉子節點,而且在葉子節點中還要再比較關鍵字。從這個角度看 B- 樹的性能好像要比 B+ 樹好,而在實際應用中卻是 B+ 樹的性能要好些。因為 B+ 樹的非葉子節點不存放實際的數據, 這樣每個節點可容納的元素個數比 B- 樹多,樹高比 B- 樹小,這樣帶來的好處是減少磁盤訪問次數。盡管 B+ 樹找到 一個記錄所需的比較次數要比 B- 樹多,但是一次磁盤訪問的時間相當于成百上千次內存比較的時間,因此實際中 B+ 樹的性能可能還會好些,而且 B+ 樹的葉子節點使用指針連接在一起,方便順序遍歷(例如查看一個目錄下的所有 文件,一個表中的所有記錄等),這也是很多數據庫和文件系統使用 B+ 樹的緣故。 思考:為什么說 B+ 樹比 B- 樹更適合實際應用中操作系統的文件索引和數據庫索引? 1) B+ 樹的磁盤讀寫代價更低 B+ 樹的內部結點并沒有指向關鍵字具體信息的指針。因此其內部結點相對 B 樹更小。如果把所有同一內部結點的關鍵字存放在同一盤塊中,那么盤塊所能容納的關鍵字數量也越多。一次性讀入內存中的需要查找的關鍵字也就 越多。相對來說 IO 讀寫次數也就降低了。 2) B+ 樹的查詢效率更加穩定 由于非終結點并不是最終指向文件內容的結點,而只是葉子結點中關鍵字的索引。所以任何關鍵字的查找必須走一條從根結點到葉子結點的路。所有關鍵字查詢的路徑長度相同,導致每一個數據的查詢效率相當。2.3 聚簇索引和非聚簇索引
聚簇索引并不是一種單獨的索引類型,而是一種數據存儲方式。術語‘聚簇’表示數據行和相鄰的鍵值聚簇的存儲在一起。如下圖,左側的索引就是聚簇索引,因為數據行在磁盤的排列和索引排序保持一致。 聚簇索引的好處: 按照聚簇索引排列順序,查詢顯示一定范圍數據的時候,由于數據都是緊密相連,數據庫不用從多個數據塊中提取數據,所以節省了大量的 io 操作。 聚簇索引的限制: 對于 MySQL? 數據庫目前只有 innodb 數據引擎支持聚簇索引,而 Myisam 并不支持聚簇索引。 由于數據物理存儲排序方式只能有一種,所以每個 Mysql 的表只能有一個聚簇索引。一般情況下就是該表的主鍵。 為了充分利用聚簇索引的聚簇的特性,所以 innodb 表的主鍵列盡量選用有序的順序 id ,而不建議用無序的 id ,比如 uuid 這種2.4 時間復雜度(擴展)
同一問題可用不同算法解決,而一個算法的質量優劣將影響到算法乃至程序的效率。算法分析的目的在于選擇合適算法和改進算法。 時間復雜度是指執行算法所需要的計算工作量,用大 O 表示記為: O(…)3. Mysql 索引分類
3.1 單值索引
概念: 即一個索引只包含單個列,一個表可以有多個單列索引 語法: 所表一起創建: CREATE TABLE customer ( id INT(10) UNSIGNED AUTO_INCREMENT , customer_no VARCHAR(200), customer_name VARCHAR(200), PRIMARY KEY(id), KEY (customer_name) ); 單獨建單值索引: CREATE INDEX idx_customer_name ON customer(customer_name);3.2 唯一索引
概念:索引列的值必須唯一,但允許有空值
隨表一起創建: CREATE TABLE customer ( id INT(10) UNSIGNED AUTO_INCREMENT , customer_no VARCHAR(200), customer_name VARCHAR(200), PRIMARY KEY(id), KEY (customer_name), UNIQUE (customer_no) ); 單獨建唯一索引: CREATE UNIQUE INDEX id x_customer_no ON customer(customer_no);3.3 主鍵索引
概念:設定為主鍵后數據庫會自動建立索引,innodb為聚簇索引
隨表一起建索引 CREATE TABLE customer (id INT(10) UNSIGNED AUTO_INCREMENT ,customer_no VARCHAR(200),customer_name VARCHAR(200), PRIMARY KEY(id) ); 單獨建主鍵索引: ALTER TABLE customer add PRIMARY KEY customer(customer_no); 刪除 建主鍵索引: ALTER TABLE customer drop PRIMARY KEY ; 修改 建主鍵索引: 必須先刪除掉 (drop) 原索引,再新建 (add) 索引3.4 復合索引
概念:即一個索引包含多個列
隨表一起建索引: CREATE TABLE customer ( id INT(10) UNSIGNED AUTO_INCREMENT , customer_no VARCHAR(200), customer_name VARCHAR(200), PRIMARY KEY(id), KEY (customer_name), UNIQUE (customer_name), KEY (customer_no,customer_name) ); 單獨建索引: CREATE INDEX idx_no_name ON customer(customer_no,customer_name);3.5 基本語法
| 創建 | CREATE [UNIQUE ] INDEX [indexName] ON table_name(column)) |
| 刪除 | DROP INDEX [indexName] ON mytable; |
| 查看 | SHOW INDEX FROM table_name\G |
| 使 用 Alter 命令 | ALTER TABLE tbl_name ADD PRIMARY KEY (column_list) : 該語句添加一個主鍵, 這意味著索引值必須是唯一 ,且不能為 NULL 。 |
| ALTER TABLE tbl_name ADD PRIMARY KEY (column_list)這條語句創建索引的值 必須要是唯一的 (除NULL外,NULL可能會出現多次) | |
| ALTER TABLE tbl_name ADD INDEX index_name (column_list): 添加普通索引, 索引值可出現多次。 | |
| ALTER TABLE tbl_name ADD FULLTEXT index_name (column_list): 該語句指定了索引為 FULLTEXT , 用于全文索 引。 |
4. 索引的創建時機
4.1 適合創建索引的情況
- ?主鍵自動建立唯一索引;
- 頻繁作為查詢條件的字段應該創建索引
- 查詢中與其它表關聯的字段,外鍵關系建立索引
- 單鍵/組合索引的選擇問題,組合索引性價比更高
- 查詢中排序的字段,排序字段若通過索引去訪問將大大提高排序速度
- 查詢中統計或者分組字段?
4.2 不適合創建索引的情況
- 表記錄太少
- 經常增刪改的表或者字段
- Where 條件里用不到的字段不創建索引
- 過濾性不好的不適合建索引
5. 性能分析
5.1 MySql Query Optimizer
1、Mysql中專門負責優化SELECT語句的優化器模塊,主要功能:通過計算分析系統中收集到的統計信息,為客戶端請求的Query提供他認為最優的執行計劃(他認為最優的數據檢索方式,但不見得DBA認為是最優的,這部分最耗費時間)
2、當客戶端向MySQL請求一條Query,命令解析器模塊完成請求分類,區別出示SELECT并轉發給MySQL Query Optimizer時,MySQL Query Optimizer首先會對整條Query進行優化,處理掉一些常量表達式的預算,直接換算成常量值。并對Query中查詢條件進行簡化和轉換,如去掉一些無用或顯而易見的條件、結構調整沒有Himt或Hint信息(如果有),則會讀取所涉及對象的統計信息,根據Query進行寫相應的計算分析,然后再得出最后的執行計劃。
5.2 MySQL常見瓶頸
第 6 章 Explain 性能分析
1. 概念
使用 EXPLAIN 關鍵字可以模擬優化器執行 SQL 查詢語句,從而知道 MySQL 是如何處理你的 SQL 語句的。分析你的查詢語句或是表結構的性能瓶頸。能干嘛
- 表的讀取順序
- 數據讀取操作的操作類型
- 哪些索引可以使用
- 哪些索引被實際使用
- 表之間的引用
- 每張表有多少行被優化器查詢
2. Explain 準備工作
CREATE TABLE t1( id INT(10) AUTO_INCREMENT, content VARCHAR(100) NULL , PRIMARY KEY (id) ); CREATE TABLE t2( id INT(10) AUTO_INCREMENT, content VARCHAR(100) NULL , PRIMARY KEY (id) ); CREATE TABLE t3( id INT(10) AUTO_INCREMENT, content VARCHAR(100) NULL , PRIMARY KEY (id) ); CREATE TABLE t4( id INT(10) AUTO_INCREMENT, content VARCHAR(100) NULL , PRIMARY KEY (id) ); INSERT INTO t1(content) VALUES(CONCAT('t1_',FLOOR(1+RAND()*1000))); INSERT INTO t2(content) VALUES(CONCAT('t2_',FLOOR(1+RAND()*1000))); INSERT INTO t3(content) VALUES(CONCAT('t3_',FLOOR(1+RAND()*1000))); INSERT INTO t4(content) VALUES(CONCAT('t4_',FLOOR(1+RAND()*1000)));3. id
select 查詢的序列號 , 包含一組數字,表示查詢中執行 select 子句或操作表的順序。 explain select t2.*from t1,t2,t3
where t1.id = t2.id and t1.id = t3.id
and t1.content = '' ① id 相同,執行順序由上至下
explain select t2.*
from t2
where id = (select id?
?? ??? ??? ??? ??? ??? ??? ??? ?from t1
?? ??? ??? ??? ??? ??? ??? ??? ?where id =(select t3.id
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?from t3?
?? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ??? ?where t3.content = ''))
?②id 不同, 如果是子查詢,id 的序號會遞增,id 值越大優先級越高,越先被執行
explain select t2.* from (
?? ?select t3.id
?? ?from t3
?? ?where t3.content = '') s1, t2
?? ?where s1.id = t2.id;?
4. select_type
select_type 代表查詢的類型,主要是用于區別普通查詢、聯合查詢、子查詢等的復雜查詢。4.1 SIMPLE
SIMPLE 代表單表查詢,查詢中不包含子查詢或者UNION;4.2 PRIMARY
查詢中若包含任何復雜的子部分,最外層查詢則被標記為 Primary 。4.3 DERIVED
在 FROM 列表中包含的子查詢被標記為 DERIVED( 衍生 ),MySQL 會遞歸執行這些子查詢 , 把結果放在臨時表里。4.4 SUBQUERY
在 SELECT 或 WHERE 列表中包含了子查詢。4.5 DEPENDENT SUBQUERY
在 SELECT 或 WHERE 列表中包含了子查詢 , 子查詢基于外層。 都是 where 后面的條件, subquery 是單個值, dependent subquery 是一組值。4.6 UNCACHEABLE SUBQUREY
當使用了 @@ 來引用系統變量的時候,不會使用緩存。4.7 UNION
若第二個 SELECT 出現在 UNION 之后,則被標記為 UNION ;若 UNION 包含在 FROM 子句的子查詢中 , 外層 SELECT將被標記為: DERIVED 。4.8 UNION RESULT
從 UNION 表獲取結果的 SELECT 。5. table
這個數據是基于哪張表的。6. type
type 是查詢的訪問類型。是較為重要的一個指標, 結果值從最好到最壞依次是 : system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL ,一般來說,得保證查詢至少達到 range 級別,最好能達到 ref 。6.1 system
表只有一行記錄(等于系統表),這是 const 類型的特列,平時不會出現,這個也可以忽略不計6.2 const
?表示通過索引一次就找到了,const 用于比較 primary key 或者 unique 索引。因為只匹配一行數據,所以很快 如將主鍵置于 where 列表中,MySQL 就能將該查詢轉換為一個常量。
6.3 eq_ref
?唯一性索引掃描,對于每個索引鍵,表中只有一條記錄與之匹配。常見于主鍵或唯一索引掃描。
6.4 ref
非唯一性索引掃描,返回匹配某個單獨值的所有行 . 本質上也是一種索引訪問,它返回所有匹配某個單獨值的行,然而,它可能會找到多個符合條件的行,所以他應該屬于查找和掃描的混合體。 沒用索引前:?建立索引后:
6.5 range
只檢索給定范圍的行 , 使用一個索引來選擇行。 key 列顯示使用了哪個索引一般就是在你的 where 語句中出現了 between 、 < 、 > 、 in 等的查詢這種范圍掃描索引掃描比全表掃描要好,因為它只需要開始于索引的某一點,而結束語另一點,不用掃描全部索引。6.6 index
出現 index 是 sql 使用了索引但是 沒用通過索引進行過濾 ,一般是使用了 覆蓋索引 或者是 利用索引進行了排序分組。?
6.7 all?
Full Table Scan,將遍歷全表以找到匹配的行。 ?
6.8 index_merge
在查詢過程中需要多個索引組合使用,通常出現在有 or 的關鍵字的 sql 中。6.9 ref_or_null
對于某個字段既需要關聯條件,也需要 null 值得情況下。查詢優化器會選擇用 ref_or_null 連接查詢。6.10 index_subquery?
利用索引來關聯子查詢,不再全表掃描。 ?
?6.11 unique_subquery
?該聯接類型類似于 index_subquery。 子查詢中的唯一索引。
7. possible_keys
顯示可能應用在這張表中的索引,一個或多個。查詢涉及到的字段上若存在索引,則該索引將被列出, 但不一 定被查詢實際使用。8. key
實際使用的索引。如果為 NULL ,則沒有使用索引。9. key_len
表示索引中使用的字節數,可通過該列計算查詢中使用的索引的長度。 key_len 字段能夠幫你檢查是否充分的利用上了索引。ken_len 越長,說明索引使用的越充分。?
如何計算: ①先看索引上字段的類型 + 長度比如 int=4 ; varchar( 20 ) = 20 ; char( 20 ) = 20 ②如果是 varchar 或者 char 這種字符串字段,視字符集要乘不同的值,比如 utf-8 要乘 3,GBK 要乘 2 , ③ varchar 這種動態字符串要加 2 個字節 ④允許為空的字段要加 1 個字節 第一組: key_len=age 的字節長度 +name 的字節長度 =4+1 + ( 20*3+2)=5+62=67 第二組: key_len=age 的字節長度 =4+1=5?
10. ref
顯示索引的哪一列被使用了,如果可能的話,是一個常數。哪些列或常量被用于查找索引列上的值。
11. rows
rows 列顯示 MySQL 認為它執行查詢時必須檢查的行數。越少越好! ?
12. Extra
其他的額外重要的信息。12.1 Using filesort
說明 mysql 會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。 MySQL 中無法利用索引 完成的排序操作稱為“ 文件排序 ” 。 出現 filesort 的情況:?優化后,不再出現 filesort 的情況:
查詢中排序的字段,排序字段若通過索引去訪問將大大提高排序速度。?
12.2 Using temporary
使了用臨時表保存中間結果 ,MySQL 在對查詢結果排序時使用臨時表。常見于排序 order by 和分組查詢 group by。 優化前:?優化后:
?12.3 Using index
Using index 代表表示相應的 select 操作中使用了覆蓋索引 (Covering Index) ,避免訪問了表的數據行,效率不錯! 如果同時出現 using where ,表明索引被用來執行索引鍵值的查找 ; 如果沒有同時出現 using where ,表明索引只是用來讀取數據而非利用索引執行查找。 利用索引進行了排序或分組。12.4 Using where
表明使用了 where 過濾。12.5 Using join buffer
?使用了連接緩存。
12.6 impossible where
where 子句的值總是 false ,不能用來獲取任何元組。?12.7 select tables optimized away
在沒有 GROUPBY 子句的情況下,基于索引優化 MIN/MAX 操作或者對于 MyISAM 存儲引擎優化 COUNT(*) 操作,不必等到執行階段再進行計算,查詢執行計劃生成的階段即完成優化。 在 innodb 中:?在 Myisam 中:
?第 7 章 批量數據腳本
1. 插入數據
1.1 建表語句
CREATE TABLE `dept` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `deptName` VARCHAR(30) DEFAULT NULL, `address` VARCHAR(40) DEFAULT NULL, ceo INT NULL , PRIMARY KEY (`id`) ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; CREATE TABLE `emp` ( `id` INT(11) NOT NULL AUTO_INCREMENT, `empno` INT NOT NULL , `name` VARCHAR(20) DEFAULT NULL, `age` INT(3) DEFAULT NULL, `deptId` INT(11) DEFAULT NULL, PRIMARY KEY (`id`) #CONSTRAINT `fk_dept_id` FOREIGN KEY (`deptId`) REFERENCES `t_dept` (`id`) ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;1.2 設置參數
在執行創建函數之前,首先請保證 log_bin_trust_function_creators 參數為 1 ,即 on 開啟狀態。 否則會報錯: 查詢: show variables like 'log_bin_trust_function_creators'; 設置: set global log_bin_trust_function_creators=1; 當然,如上設置只存在于當前操作,想要永久生效,需要寫入到配置文件中: 在 [mysqld] 中加上 log_bin_trust_function_creators=11.3 編寫隨機函數
創建函數,保證每條數據都不同。1.3.1 隨機產生字符串
DELIMITER $$ CREATE FUNCTION rand_string(n INT) RETURNS VARCHAR(255) BEGIN DECLARE chars_str VARCHAR(100) DEFAULT 'abcdefghijklmnopqrstuvwxyzABCDEFJHIJKLMNOPQRSTUVWXYZ'; DECLARE return_str VARCHAR(255) DEFAULT ''; DECLARE i INT DEFAULT 0; WHILE i < n DO SET return_str =CONCAT(return_str,SUBSTRING(chars_str,FLOOR(1+RAND()*52),1)); SET i = i + 1; END WHILE; RETURN return_str; END $$ 如果要刪除函數,則執行: drop function rand_string; # 用于隨機產生多少到多少的編號 DELIMITER $$ CREATE FUNCTION rand_num (from_num INT ,to_num INT) RETURNS INT(11) BEGIN DECLARE i INT DEFAULT 0; SET i = FLOOR(from_num +RAND()*(to_num -from_num+1)) ; RETURN i; END$$ 如果要刪除函數: drop function rand_num;1.4 創建存儲過程
1.4.1 創建往 emp 表中插入數據的存儲過程
DELIMITER $$ CREATE PROCEDURE insert_emp( START INT , max_num INT ) BEGIN DECLARE i INT DEFAULT 0; #set autocommit =0 把 autocommit 設置成 0 SET autocommit = 0; REPEAT SET i = i + 1; INSERT INTO emp (empno, NAME ,age ,deptid ) VALUES ((START+i) ,rand_string(6) , rand_num(30,50),rand_num(1,10000)); UNTIL i = max_num END REPEAT; COMMIT; END$$ # 刪除 # DELIMITER ; # drop PROCEDURE insert_emp;1.4.2 創建往 dept 表中插入數據的存儲過程
# 執行存儲過程,往 dept 表添加隨機數據 DELIMITER $$ CREATE PROCEDURE `insert_dept`( max_num INT ) BEGIN DECLARE i INT DEFAULT 0; SET autocommit = 0; REPEAT SET i = i + 1; INSERT INTO dept ( deptname,address,ceo ) VALUES (rand_string(8),rand_string(10),rand_num(1,500000)); UNTIL i = max_num END REPEAT; COMMIT; END$$1.5 調用存儲過程
1.5.1 添加數據到部門表
# 執行存儲過程,往 dept 表添加 1 萬條數據 DELIMITER ; CALL insert_dept(10000);1.5.2 添加數據到員工表
# 執行存儲過程,往 emp 表添加 50 萬條數據 DELIMITER ; CALL insert_emp(100000,500000);1.6 批量刪除某個表上的所有索引
1.6.1 刪除索引的存儲過程
DELIMITER $$ CREATE PROCEDURE `proc_drop_index`(dbname VARCHAR(200),tablename VARCHAR(200)) BEGIN DECLARE done INT DEFAULT 0; DECLARE ct INT DEFAULT 0; DECLARE _index VARCHAR(200) DEFAULT ''; DECLARE _cur CURSOR FOR SELECT index_name FROM information_schema.STATISTICS WHERE table_schema=dbname AND table_name=tablename AND seq_in_index=1 AND index_name <>'PRIMARY' ; DECLARE CONTINUE HANDLER FOR NOT FOUND set done=2 ; OPEN _cur; FETCH _cur INTO _index; WHILE _index<>'' DO SET @str = CONCAT("drop index ",_index," on ",tablename ); PREPARE sql_str FROM @str ; EXECUTE sql_str; DEALLOCATE PREPARE sql_str; SET _index=''; FETCH _cur INTO _index; END WHILE; CLOSE _cur; END$$1.6.2 執行存儲過程
調用: CALL proc_drop_index("dbname","tablename");第 8 章 單表使用索引常見的索引失效
1. 全值匹配我最愛
1.1 有以下 SQL 語句
EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE emp.age=30 EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE emp.age=30 and deptid=4 EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE emp.age=30 and deptid=4 AND emp.name = 'abcd1.2 建立索引
CREATE INDEX idx_age_deptid_name ON emp(age,deptid,NAME);?結論:全職匹配我最愛指的是,查詢的字段按照順序在索引中都可以匹配到!
SQL 中查詢字段的順序,跟使用索引中字段的順序,沒有關系。優化器會在不影響 SQL 執行結果的前提下,給你自動地優化。?2. 最佳左前綴法則
查詢字段與索引字段順序的不同會導致,索引無法充分使用,甚至索引失效! 原因:使用復合索引,需要遵循最佳左前綴法則,即如果索引了多列,要遵守最左前綴法則。指的是查詢從索引的 最左前列開始并且不跳過索引中的列 。 結論:過濾條件要使用索引必須按照索引建立時的順序,依次滿足,一旦跳過某個字段,索引后面的字段都無 法被使用。3. 不要在索引列上做任何計算
不在索引列上做任何操作(計算、函數、(自動 or 手動)類型轉換),會導致索引失效而轉向全表掃描。?
3.1 在查詢列上使用了函數
EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE age=30; EXPLAIN SELECT SQL_NO_CACHE * FROM emp WHERE LEFT(age,3)=30;?結論:等號左邊無計算!
3.2 在查詢列上做了轉換
| create index idx_name on emp(name); |
| explain select sql_no_cache * from emp where name='30000'; |
| explain select sql_no_cache * from emp where name=30000; |
結論:等號右邊無轉換!?
4. 索引列上不能有范圍查詢
| explain SELECT SQL_NO_CACHE * FROM emp WHERE emp.age=30 and deptid=5 AND emp.name = 'abcd'; |
| explain SELECT SQL_NO_CACHE * FROM emp WHERE emp.age=30 and deptid<=5 AND emp.name = 'abcd'; |
?建議:將可能做范圍查詢的字段的索引順序放在最后
5. 盡量使用覆蓋索引
即查詢列和索引列一直,不要寫 select *!| explain SELECT SQL_NO_CACHE * FROM emp WHERE emp.age=30 and deptId=4 and name='XamgXt'; |
| explain SELECT SQL_NO_CACHE age,deptId,name FROM emp WHERE emp.age=30 and deptId=4 and name='XamgXt'; |
?6. 使用不等于(!= 或者<>)的時候
mysql 在使用不等于 (!= 或者 <>) 時,有時會無法使用索引會導致全表掃描。7. 字段的 is not null 和 is null?
當字段允許為 Null 的條件下:
?is not null 用不到索引,is null 可以用到索引。
?8. like 的前后模糊匹配
前綴不能出現模糊匹配!?
9. 減少使用 or
使用 union all 或者 union 來替代:
10. 練習
假設 index(a,b,c) ;| where a = 3 | Y, 使用到 a |
| where a = 3 and b = 5 | Y, 使用到 a , b |
| where a = 3 and b = 5 and c = 4 | Y, 使用到 a,b,c |
| where b = 3 或者 where b = 3 and c = 4 或者 where c = 4 | N |
| where a = 3 and c = 5 | 使用到 a , 但是 c 不可以, b 中間斷了 |
| where a = 3 and b > 4 and c = 5 | 使用到 a 和 b , c 不能用在范圍之后, b 斷了 |
| where a is null and b is not null | is null 支持索引 但是 is not null 不支持 , 所 以 a 可以使用索引 , 但是 b 不可以使用 |
| where a <> 3 | 不能使用索引 |
| where? ?abs(a) =3 | 不能使用 索引 |
| where a = 3 and b like 'kk%' and c = 4 | Y, 使用到 a,b,c |
| where a = 3 and b like '%kk' and c = 4 | Y, 只用到 a |
| where a = 3 and b like '%kk%' and c = 4 | Y, 只用到 a |
| where a = 3 and b like 'k%kk%' and c = 4 | Y, 使用到 a,b,c |
11. 口訣
全職匹配我最愛,最左前綴要遵守; 帶頭大哥不能死,中間兄弟不能斷; 索引列上少計算,范圍之后全失效; LIKE 百分寫最右,覆蓋索引不寫 * ; 不等空值還有 OR ,索引影響要注意; VAR 引號不可丟, SQL 優化有訣竅。第 9 章 關聯查詢優化
1. 建表語句
CREATE TABLE IF NOT EXISTS `class` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `card` INT(10) UNSIGNED NOT NULL, PRIMARY KEY (`id`) ); CREATE TABLE IF NOT EXISTS `book` ( `bookid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `card` INT(10) UNSIGNED NOT NULL, PRIMARY KEY (`bookid`) ); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20)));2. 案例
2.1 left join
① EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card; ②如何優化?在哪個表上建立索引? ALTER TABLE `book` ADD INDEX idx_card( `card`); ③刪除 book 表的索引: drop index idx_card on book; 在 class 表上建立索引: alter table class add index idx_card(card); 結論: ①在優化關聯查詢時,只有在被驅動表上建立索引才有效! ② left join 時,左側的為驅動表,右側為被驅動表!?2.2 inner join
① EXPLAIN SELECT * FROM book inner join class on class.card=book.card;②兩個查詢字段調換順序,發現結果也是一樣的! ?
③在 book 表中,刪除 9 條記錄?
④結論: inner join 時, mysql 會自己幫你把小結果集的表選為驅動表。 ⑤ straight_join: 效果和 inner join 一樣,但是會強制將左側作為驅動表!?2.3 四個關聯查詢案例分析
EXPLAIN SELECT ed.name ' 人物 ',c.name ' 掌門 ' FROM (SELECT e.name,d.ceo from t_emp e LEFT JOIN t_dept d on e.deptid=d.id) ed LEFT JOIN t_emp c on ed.ceo= c.id; EXPLAIN SELECT e.name ' 人物 ',tmp.name ' 掌門 ' FROM t_emp e LEFT JOIN (SELECT d.id did,e.name FROM t_dept d LEFT JOIN t_emp e ON d.ceo=e.id)tmp ON e.deptId=tmp.did; 上述兩個案例,第一個查詢效率較高,且有優化的余地。第二個案例中,子查詢作為被驅動表,由于子查詢是虛表, 無法建立索引,因此不能優化。 結論 : 子查詢盡量不要放在被驅動表,有可能使用不到索引; left join時,盡量讓實體表作為被驅動表。 EXPLAIN SELECT e1.name ' 人物 ',e2.name ' 掌門 ' FROM t_emp e1 LEFT JOIN t_dept d on e1.deptid = d.id LEFT JOIN t_emp e2 on d.ceo = e2.id ; Explain SELECT e2.name ' 人物 ', (SELECT e1.name FROM t_emp e1 where e1.id= d.ceo) ' 掌門 ' from t_emp e2 LEFT JOIN t_dept d on e2.deptid=d.id;?結論:能夠直接多表關聯的盡量直接關聯,不用子查詢!
?第 10 章 子查詢優化
1. 案例?
取所有不為掌門人的員工,按年齡分組!
select age as '年齡', count(*) as '人數' from t_emp where id not in
(select ceo from t_dept where ceo is not null) group by age;
如何優化? ①解決 dept 表的全表掃描,建立 ceo 字段的索引:此時,再次查詢:?
②進一步優化,替換 not in 。 上述 SQL 可以替換為: select age as ' 年齡 ',count(*) as ' 人數 ' from emp e left join dept d on e.id=d.ceo where d.id is null group by age;?結論: 在范圍判斷時,盡量不要使用 not in 和 not exists,使用 left join on xxx is null 代替。
第 11 章 排序分組優化
where 條件和 on 的判斷這些過濾條件,作為優先優化的部門,是要被先考慮的!其次,如果有分組和排序,那么也要考慮 grouo by 和 order by 。1. 無過濾不索引
create index idx_age_deptid_name on emp (age,deptid,name); explain select * from emp where age=40 order by deptid; explain select * from emp order by age,deptid; explain select * from emp order by age,deptid limit 10;?
using filesort 說明進行了手工排序!原因在于沒有 where 作為過濾條件!?
?結論: 無過濾,不索引。where,limt 都相當于一種過濾條件,所以才能使用上索引!
2. 順序錯,必排序
① explain select * from emp where age=45 order by deptid,name;②explain select * from emp where age=45 order by deptid,empno;?
empno 字段并沒有建立索引,因此也無法用到索引,此字段需要排序! ③ explain select * from emp where age=45 order by name,deptid; where 兩側列的順序可以變換,效果相同,但是 order by 列的順序不能隨便變換! ④ explain select * from emp where deptid=45 order by age;deptid 作為過濾條件的字段,無法使用索引,因此排序沒法用上索引?
3. 方向反,必排序
① explain select * from emp where age=45 order by deptid desc, name desc ; 如果可以用上索引的字段都使用正序或者逆序,實際上是沒有任何影響的,無非將結果集調換順序。 ② explain select * from emp where age=45 order by deptid asc, name desc ;?
如果排序的字段,順序有差異,就需要將差異的部分,進行一次倒置順序,因此還是需要手動排序的!
4. 索引的選擇?
①首先,清除 emp 上面的所有索引,只保留主鍵索引!
drop index idx_age_deptid_name on emp;
②查詢:年齡為 30 歲的,且員工編號小于 101000 的用戶,按用戶名稱排序 explain SELECT SQL_NO_CACHE * FROM emp WHERE age =30 AND empno <101000 ORDER BY NAME ; ③全表掃描肯定是不被允許的,因此我們要考慮優化。 思路:首先需要讓 where 的過濾條件,用上索引;查詢中,age.empno 是查詢的過濾條件,而 name 則是排序的字段,因此我們來創建一個此三個字段的復合索引: create index idx_age_empno_name on emp(age,empno,name); 再次查詢,發現 using filesort 依然存在。 原因: empno 是范圍查詢,因此導致了索引失效,所以 name 字段無法使用索引排序。 所以,三個字段的符合索引,沒有意義,因為 empno 和 name 字段只能選擇其一! ④解決: 魚與熊掌不可兼得,因此,要么選擇 empno, 要么選擇 name drop index idx_age_empno_name on emp; create index idx_age_name on emp(age,name); create index idx_age_empno on emp(age,empno); 兩個索引同時存在, mysql 會選擇哪個? explain SELECT SQL_NO_CACHE * FROM emp use index(idx_age_name) WHERE age =30 AND empno <101000 ORDER BY NAME ; 原因:所有的排序都是在條件過濾之后才執行的,所以如果條件過濾了大部分數據的話,幾百幾千條數據進行排序其實并不是很消耗性能,即使索引優化了排序但實際提升性能很有限。 相對的 empno<101000 這個條件如果沒有用到索引的話,要對幾萬條的數據進行掃描,這是非常消耗性能的,使用 empno 字段的范圍查詢,過濾性更好(empno 從 100000 開始)! 結論: 當范圍條件和 group by 或者 order by 的字段出現二選一時 ,優先觀察條件字段的過濾數量,如果過濾的數據足夠多,而需要排序的數據并不多時,優先把索引放在范圍字段上。反之,亦然。5. using filesort
5.1 mysql 的排序算法
①雙路排序 MySQL 4.1 之前是使用雙路排序 , 字面意思就是兩次掃描磁盤,最終得到數據,讀取行指針和 orderby 列,對他們進行排序,然后掃描已經排序好的列表,按照列表中的值重新從列表中讀取對應的數據輸出。 從磁盤取排序字段,在 buffer 進行排序,再從磁盤取其他字段。 簡單來說,取一批數據,要對磁盤進行了兩次掃描,眾所周知, I\O 是很耗時的,所以在 mysql4.1 之后,出現了第二種改進的算法,就是單路排序。 ②單路排序 從磁盤讀取查詢需要的所有列,按照 order by 列在 buffer 對它們進行排序,然后掃描排序后的列表進行輸出,它的效率更快一些,避免了第二次讀取數據。并且把隨機 IO 變成了順序 IO, 但是它會使用更多的空間,因為它把每一行都保存在內存中了。 ③單路排序的問題 由于單路是后出的,總體而言好過雙路。但是存在以下問題: 在 sort_buffer 中,方法 B 比方法 A 要多占用很多空間,因為方法 B 是把所有字段都取出 , 所以有可能取出的數據的總大小超出了 sort_buffer 的容量,導致每次只能取 sort_buffer 容量大小的數據,進行排序(創建 tmp 文件,多路合并),排完再取取 sort_buffer 容量大小,再排……從而多次 I/O 。 結論: 本來想省一次 I/O 操作,反而導致了大量的 I/O 操作,反而得不償失。5.2 如何優化
①增大 sort_butter_size 參數的設置 不管用哪種算法,提高這個參數都會提高效率,當然,要根據系統的能力去提高,因為這個參數是針對每個進程的 1M-8M 之間調整。 ②增大 max_length_for_sort_data 參數的設置 mysql 使用單路排序的前提是排序的字段大小要小于 max_length_for_sort_data。 提高這個參數,會增加用改進算法的概率。但是如果設的太高,數據總容量超出 sort_buffer_size 的概率就增大, 明顯癥狀是高的磁盤 I/O 活動和低的處理器使用率。( 1024-8192 之間調整)。 ③減少 select 后面的查詢的字段。 當 Query 的字段大小總和小于 max_length_for_sort_data 而且排序字段不是 TEXT|BLOB 類型時,會用改進后的 算法——單路排序, 否則用老算法——多路排序。 兩種算法的數據都有可能超出 sort_buffer 的容量,超出之后,會創建 tmp 文件進行合并排序,導致多次 I/O , 但是用單路排序算法的風險會更大一些 , 所以要提高 sort_buffer_size 。6. 使用覆蓋索引
覆蓋索引: SQL 只需要通過索引就可以返回查詢所需要的數據,而不必通過二級索引查到主鍵之后再去查詢數據。?7. group by
group by 使用索引的原則幾乎跟 order by 一致 ,唯一區別是 groupby 即使沒有過濾條件用到索引,也可以直接使用索引。?第 12 章 練習
1. 案例一
列出自己的掌門比自己年齡小的人員 select e1.name empname,e1.age empage,e2.name ceoname,e2.age ceoage from t_emp e1 inner join t_dept d on e1.deptid=d.id inner join t_emp e2 on d.ceo=e2.id where e1.age>e2.age;更換為大表,進行分析:?
explain select e1.name empname,e1.age empage,e2.name ceoname,e2.age ceoage from emp e1 inner join dept d on e1.deptid=d.id inner join emp e2 on d.ceo=e2.id where e1.age>e2.age;兩次 inner join 的被驅動表都已經用上了索引。?
2. 案例二
列出所有年齡低于自己門派平均年齡的人員 思路: 先取門派的平均年齡,再跟自己的年齡做對比! select e1.name from t_emp e1 inner join (select deptid,AVG(age) avgage from t_emp group by deptid) tmp on e1.deptid=tmp.deptid where e1.age<tmp.avgage;更換為大表:?
explain select e1.name from emp e1 inner join (select deptid,AVG(age) avgage from emp group by deptid) tmp on e1.deptid=tmp.deptid where e1.age<tmp.avgage; 在沒有索引的前提下: 如何優化: ①首先在子查詢中,需要根據 deptid 做 groupby 操作,因此,需要在 deptid 上面建立索引; ②因為是 inner join, 因此會自動將小表作為驅動表,也就是說,分組后的 tmp 是驅動表,而 e1 是被驅動表; ③而在 e1 中,需要查詢 deptid 和 age 兩個字段,因此這兩個字段也需要建立索引 結果:創建 deptid 和 age 的符合索引 : create index idx_deptid_age on emp(deptid,age);3. 案例三?
列出至少有 2 個年齡大于 40 歲的成員的門派 思路: 先查詢大于 40 歲的成員,然后按照門派分組,然后再判斷至少有 2 個的門派! select d.deptName,count(*) from t_emp e inner join t_dept d on e.deptid=d.id where e.age>40 group by d.id,d.deptName having count(*)>=2大表優化:
explain select d.deptName,count(*) from emp e inner join dept d on e.deptid=d.id where e.age>40 group by d.id,d.deptName having count(*)>=2 優化: ①兩表關聯,我們可以考慮將小表作為驅動表。 ② group by 的字段 id,deptName 還可以建立索引 : create index idx_id_deptName on dept(id,deptName); ③被驅動表的 deptid 作為關聯字段,可以建立索引: create index idx_deptid on emp(deptid); create index idx_id_deptname on dept(id,deptName);?
4. 案例四?
至少有 2 位非掌門人成員的門派 select d2.deptName from t_emp e inner join t_dept d2 on e.deptid=d2.id left join t_dept d on e.id=d.ceo where d.id is null and e.deptid is not null group by d2.deptName,d2.id having count(*)>=2; 切換大表: explain select d2.deptName from emp e inner join dept d2 on e.deptid=d2.id left join dept d on e.id=d.ceo where d.id is null and e.deptid is not null group by d2.deptName,d2.id having count(*)>=2; 沒有索引的情況下: 優化分析: 三個表關聯,然后做 group by 分組! ① group by 的字段,可以加上索引: create index idx_deptname_id on dept(deptName,id); ②可以將部門表作為驅動表 ③第一次 join 時, e 表作為被驅動表,可以將 deptid 設置索引: create index idx_deptid on emp(deptid); ④最有一次 join 中,使用了 dept 表作為被驅動表,查詢 ceo 字段,因此可以在 ceo 上面建立索引 create index idx_ceo on dept(ceo);?
5. 案例五?
列出全部人員,并增加一列備注“是否為掌門”,如果是掌門人顯示是,不是掌門人顯示否 select e.name,case when d.id is null then ' 否 ' else ' 是 ' end ' 是否為掌門 ' from t_emp e left join t_dept d on e.id=d.ceo;大表關聯:
explain select e.name,case when d.id is null then ' 否 ' else ' 是 ' end ' 是否為掌門 ' from emp e left join dept d on e.id=d.ceo;?優化:在 d 表的 ceo 字段建立索引即可!
6. 案例六
列出全部門派,并增加一列備注“老鳥 or 菜鳥”,若門派的平均值年齡 >40 顯示“老鳥”,否則顯示“菜鳥” 思路: 先從 emp 表求出,各門派的平均年齡,分組,然后在關聯 dept 表,使用 if 函數進行判斷! select d.deptName,if(avg(age)>40,' 老鳥 ',' 菜鳥 ') from t_emp e inner join t_dept d on d.id=e.deptid group by d.deptName,d.id?切換大表:
explain select d.deptName,if(avg(age)>40,' 老鳥 ',' 菜鳥 ') from dept d inner join emp e on d.id=e.deptid group by d.deptName,d.id 優化: ①使用 dept 作為驅動表 ②在 dept 上建立 deptName 和 id 的索引: create index idx_deptName_id on dept(deptName,id); ③在 emp 上建立 deptid 字段的索引: create index index_deptid on emp(deptid);?
7. 案例七
顯示每個門派年齡最大的人 思路:先查詢 emp 表,求出每個門派年齡最大的人,并按照 deptid 分組;然后再次關聯 emp 表,關聯其他的信息! select * from t_emp e inner join (select deptid,max(age) maxage from t_emp group by deptid) tmp on tmp.deptid=e.deptid and tmp.maxage=e.age;?大表優化:
explain select * from emp e inner join (select deptid,max(age) maxage from emp group by deptid) tmp on tmp.deptid=e.deptid and tmp.maxage=e.age; 優化前: 優化思路: ①子查詢中, emp 表根據 deptid 進行分組,因此可以建立 deptid 字段的索引; ② inner join 查詢中,關聯了 age 和 deptid ,因此可以在 deptid,age 字段建立索引 create index idx_deptid_age on emp(deptid,age);?
?第 13 章 截取查詢分析
1. 慢查詢日志
1.1 是什么
(1) MySQL 的慢查詢日志是 MySQL 提供的一種日志記錄,它用來記錄在 MySQL 中響應時間超過閥值的語句,具體指運行時間超過long_query_time 值的 SQL ,則會被記錄到慢查詢日志中。 (2)具體指運行時間超過 long_query_time 值的 SQL ,則會被記錄到慢查詢日志中。 long_query_time 的默認值為10,意思是運行 10 秒以上的語句。 (3)由他來查看哪些 SQL 超出了我們的最大忍耐時間值,比如一條 sql 執行超過 5 秒鐘,我們就算慢 SQL ,希望能收集超過5 秒的 sql ,結合之前 explain 進行全面分析。1.2 怎么用
默認情況下, MySQL 數據庫沒有開啟慢查詢日志 ,需要我們手動來設置這個參數。 當然,如果不是調優需要的話,一般不建議啟動該參數 ,因為開啟慢查詢日志會或多或少帶來一定的性能影響。 慢查詢日志支持將日志記錄寫入文件。 (1) 開啟設置| SHOW VARIABLES LIKE '%slow_query_log%'; | 查看慢查詢日志是否開啟 | 默認情況下 slow_query_log 的值為 OFF , 表示慢查詢日志是禁用的 |
| set global slow_query_log=1; | 開啟慢查詢日志 | |
| SHOW VARIABLES LIKE 'long_query_time%'; | 查看慢查詢設定閾值 | 單位秒 |
| set long_query_time=1 | 設定慢查詢閾值 | 單位秒 |
1.2 日志分析工具 mysqldumpslow
(1) 查看mysqldumpslow 的幫助信息 命令: [root@cocoon ~]# mysqldumpslow --help?(2) 查看mysqldumpslow的幫助信息
得到返回記錄集最多的 10 個 SQL mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log 得到訪問次數最多的 10 個 SQL mysqldumpslow -s c -t 10 /var/lib/mysql/atguigu-slow.log 得到按照時間排序的前 10 條里面含有左連接的查詢語句 mysqldumpslow -s t -t 10 -g "left join" /var/lib/mysql/atguigu-slow.log 另外建議在使用這些命令時結合 | 和 more 使用 ,否則有可能出現爆屏情況 mysqldumpslow -s r -t 10 /var/lib/mysql/atguigu-slow.log | more2. SHOW PROCESSLIST
1.1 是什么
查詢 mysql 進程列表,可以殺掉故障進程1.2 怎么用
?第 14 章 工具和技巧拾遺
1.1 是什么
將一段查詢 sql 封裝為一個虛擬的表。 這個虛擬表只保存了 sql 邏輯,不會保存任何查詢結果。1.2 作用
(1)封裝復雜 sql 語句,提高復用性 (2)邏輯放在數據庫上面,更新不需要發布程序,面對頻繁的需求變更更靈活第 15 章 主從復制
1.1 復制的基本原理
(1) slave 會從 master 讀取 binlog 來進行數據同步 (2)三步驟 + 原理圖 MySQL 復制過程分成三步: master 將改變記錄到二進制日志(binary log)。這些記錄過程叫做二進制日志事件, binary log events ; slave 將 master 的 binary log events 拷貝到它的中繼日志(relay log); slave 重做中繼日志中的事件,將改變應用到自己的數據庫中。 MySQL 復制是異步的且串行化的1.2 復制的基本原則
(1)每個 slave 只有一個 master (2)每個 slave 只能有一個唯一的服務器 ID (3)每個 master 可以有多個 salve1.3 復制的最大問題
因為發生多次 IO ,存在延時問題?1.4 一主一從常見配置
(1) mysql 版本一致且后臺以服務運行 (2) 主從都配置在[mysqld] 結點下,都是小寫 ? ? ? ? ? 主機修改 my.ini 配置文件?
主服務器唯一 ID
server-id=1 啟用二進制日志 JAVAEE 課程系列 log-bin= 自己本地的路徑 /data/mysqlbin log-bin=D:/devSoft/MySQLServer5.5/data/mysqlbin 設置不要復制的數據庫 binlog-ignore-db=mysql 設置需要復制的數據庫 binlog-do-db= 需要復制的主數據庫名字 設置 logbin 格式 binlog_format=STATEMENT (默認)mysql 主從復制起始時,從機不繼承主機數據?
(3) logbin 格式 binlog_format=STATEMENT (默認) binlog_format=ROW binlog_format= MIXED?
(4) 從機配置文件修改 my.cnf 的 [mysqld] 欄位下 # 從機服務 id server-id = 2 # 注意 my.cnf 中有 server-id = 1 # 設置中繼日志 relay-log=mysql-relay(5) 因修改過配置文件,請主機+從機都重啟后臺 mysql 服務
(6) 主機從機都關閉防火墻、安全工具(騰訊管家等) (7) 在 Windows 主機上建立帳戶并授權 slave # 創建用戶,并授權 GRANT REPLICATION SLAVE ON *.* TO ' 備份賬號 '@' 從機器數據庫 IP' IDENTIFIED BY '123456';(8) 查詢 master 的狀態,并記錄下 File 和 Position 的值
# 查詢 master 的狀態 show master status;?執行完此步驟后不要再操作主服務器 MYSQL,防止主服務器狀態值變化
(9) 在 Linux 從機上配置需要復制的主機 # 查詢 master 的狀態 CHANGE MASTER TO MASTER_HOST=' 主機 IP',MASTER_USER=' 創建用戶名 ',MASTER_PASSWORD=' 創建的密碼 ', MASTER_LOG_FILE='File 名字 ',MASTER_LOG_POS=Position 數字 ;?(10) 啟動從服務器復制功能
start slave; show slave status\G;?下面兩個參數都是 Yes,則說明主從配置成功!
Slave_IO_Running: Yes Slave_SQL_Running: Yes(11) 主機新建庫、新建表、insert 記錄,從機復制
(12) 如何停止從服務復制功能stop slave;?
第 16 章 MYCAT?
1.1 是什么?
數據庫中間件,前身是阿里的 cobar?
1.2 做什么?
????????垂直拆分、水平拆分
????????垂直+水平拆分
3.多數據源整合?
1.3 MYCAT 原理
“攔截”:Mycat 的原理中最重要的一個動詞是“攔截”,它攔截了用戶發送過來的 SQL 語句,首先對 SQL 語句做了一些特定的分析:如分片分析、路由分析、讀寫分離分析、緩存分析等,然后將此 SQL 發往后端的真實數據庫,并將返回的結果做適當的處理,最終再返回給用戶。
?這種方式把數據庫的分布式從代碼中解耦出來,程序員察覺不出來后臺使用 mycat 還是 mysql。
1.4 安裝啟動?
? ? ? ? ? ? ? schema.xml 定義邏輯庫,表、分片節點等內容 rule.xml 定義分片規則
? ? ? ? ? ? ? server.xml 定義用戶以及系統相關變量,如端口等.
? ? ?3.啟動前先修改 schema.xml
<?xml version="1.0"?> <!DOCTYPE mycat:schema SYSTEM "schema.dtd"> <mycat:schema xmlns:mycat="http://io.mycat/"><!--邏輯庫 name 名稱, checkSQLschema sqlMaxLimit 末尾是否要加 limit xxx--> <schema name="TESTDB" checkSQLschema="false" sqlMaxLimit="100" dataNode="dn1"> </schema><!--邏輯庫 name 名稱, dataHost 引用的哪個 dataHost database:對應 mysql 的 database--> <dataNode name="dn1" dataHost="localhost1" database="db1" /> <dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"writeType="0" dbType="mysql" dbDriver="native" switchType="1" slaveThreshold="100"> <heartbeat>select user()</heartbeat> <!-- can have multi write hosts --><writeHost host="hostM1" url="localhost:3306" user="root" password="123456"></writeHost> </dataHost> </mycat:schema>? ? ? ? 4.?再修改 server.xml
<user name="root"> <property name="password">654321</property> <property name="schemas">TESTDB</property> </user>? ? ? ?5.?啟動程序
控制臺啟動 :去 mycat/bin 目錄下 mycat console 后臺啟動 :去 mycat/bin 目錄下 mycat start
? ? ? ? 6.啟動時可能出現報錯
域名解析失敗
用 vim 修改 /etc/hosts 文件,在 127.0.0.1 后面增加你的機器名?
?修改后重新啟動網絡服務
?
? ? ? ? 7. 后臺管理窗口登錄?
總結
以上是生活随笔為你收集整理的MySQL高级--个人笔记的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 入门学习爬取贴吧图片(附完整代码),20
- 下一篇: 第04章 逻辑架构【1.MySQL架构篇