MySQL 一些內部原理


1. MySQL 體系結構

如下圖:

 

Mysql是由SQL接口,解析器,優化器,緩存,存儲引擎組成的(SQL Interface、 Parser、 Optimizer、Caches&Buffers、Pluggable Storage Engines)

    1. Connectors指的是不同語言中與SQL的交互

    2. Management Serveices & Utilities: 系統管理和控制工具,例如備份恢復、Mysql復制、集群等

    3. Connection Pool: 連接池:管理緩沖用戶連接、用戶名、密碼、權限校驗、線程處理等需要

    4. SQL Interface: SQL接口:接受用戶的SQL命令,並且返回用戶需要查詢的結果。比如select from就是調用SQL Interface

    5. Parser: 解析器, SQL命令傳遞到解析器的時候會被解析器驗證和解析。主要功能: 
      a . 將SQL語句分解成數據結構,並將這個結構傳遞到后續步驟,以后SQL語句的傳遞和處理就是基於這個結構的 
      b. 如果在分解構成中遇到錯誤,那么就說明這個sql語句是不合理的

    6. Optimizer: 查詢優化器, SQL語句在查詢之前會使用查詢優化器對查詢進行優化

    7. Cache和Buffer(高速緩存區): 查詢緩存,如果查詢緩存有命中的查詢結果,查詢語句就可以直接去查詢緩存中取數據。 
      通過LRU算法將數據的冷端溢出,未來得及時刷新到磁盤的數據頁,叫臟頁。 
      這個緩存機制是由一系列小緩存組成的。比如表緩存,記錄緩存, key緩存,權限緩存等

    8. Engine :存儲引擎。存儲引擎是MySql中具體的與文件打交道的子系統。也是Mysql最具有特色的一個地方。

    9. MySQL相關底層文件

 

2. MySQL 文件

構成MySQL整個數據庫的是所有的相關文件,這些文件有: 
參數文件my.cnf:告訴MySQL實例在啟動的時候去哪里找數據庫文件,並指定初始化文件參數,包括定義內存緩沖池大小等等

日志文件:用來記錄MySQL實例對某些條件作出響應時寫入的文件,包括錯誤日志文件,二進制日志文件,慢查詢日志文件,查詢日志文件等

Socket文件:當用Unix套接字方式連接時使用的文件

Pid文件:MySQL實例的進程ID文件

MySQL表結構文件:用來存放表結構定義的文件

存儲引擎相關文件:每個存儲引擎都有自己相關的文件來保存各種數據,包括表數據和索引數據等等

參數文件:當MySQL實例啟動時,數據庫會先去讀一個配置參數文件,用來尋找數據庫的各種文件所在位置以及指定的初始化參數

2.1 MySQL 參數文件

數據庫參數其實是一個鍵值對(key/value),比如innodb_buffer_pool_size=1G。 
可以通過show variables命令來查看所有的參數,也可以通過like關鍵詞來過濾特定的 
參數,還可以通過performance_schema.global_variables視圖(5.7.6版本以后)來查看參數 

show variables like '%innodb_buffer_pool%'
select * from performance_schema.global_variables where
variable_name='innodb_buffer_pool_size';

  

MySQL數據庫中的參數可以分為動態參數和靜態參數兩種 

https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html #官方文檔, 對一些參數的詳細解釋 

2.1.1 動態參數

參數的詳細解釋 
動態參數是指在數據庫運行的過程中可以動態修改的參數,可以通過set命令對動態參數進行修改

用法:

mysql> help set
Name: 'SET'
Description:
Syntax:
SET variable_assignment [, variable_assignment] ...

variable_assignment:
      user_var_name = expr
    | param_name = expr
    | local_var_name = expr
    | [GLOBAL | SESSION]
        system_var_name = expr
    | [@@global. | @@session. | @@]
        system_var_name = expr

  

Global和session是指該參數的修改是基於當前會話還是基於整個實例的生命周期 
設置為global參數修改,並不影響my.cnf中的變化,當數據庫下次重啟依然是參數文件中的配置

注: 如果不重啟mysql 的情況下動態修改參數,先看看該參數是不是動態參數, 如果是動態參數 則可以用set global 方式修改 , 修改完動態參數文文件,再加入到my.cnf 
例如autocommit 是dynamic 動態參數
 

2.1.2 靜態參數

靜態參數是指在數據庫運行過程中不能修改的參數,必須在配置文件my.cnf中修改並且數據庫重啟后才能生效 
比如datadir參數,如果使用動態參數修改方式,則會報錯:

mysql> set global datadir='/usr/local/mysql/data2';
ERROR 1238 (HY000): Variable 'datadir' is a read only variable

2.2 MySQL 日志文件

MySQL日志文件包含以下幾種: 
錯誤日志(error log) 
二進制日志(binlog) 
慢查詢日志(slow log) 
查詢日志(general_log)

2.2.1 錯誤日志

對MySQL的啟動,運行和關閉過程進行了記錄。遇到問題時首先應該查詢此文件以便定位問題。 
可以通過show variables like ‘log_error’ 命令來定位文件位置

默認情況下錯誤日志的文件名是該服務器的主機名

2.2.2慢查詢日志

可以定位可能存在性能問題的SQL語句,從而進行SQL語句層面的優化。 
通過設置long_query_time參數來設置一個閾值,將運行時間超過該值的所有SQL語句都記錄到慢查詢日志文件中。

 

show variables like '%long_query%';
show variables like '%slow_query%';

  

 

 另一個和慢查詢日志相關的參數是log_queries_not_using_indexes參數,如果運行的SQL語句沒有使用索引,則會把這條SQL語句記錄到慢查詢日志中

例如

#一個會話窗口,實時查看日志
tail -f /usr/local/mysql/data/master01-slow.log
#另一個窗口
執行一個做慢查詢模擬

 

 

 慢查詢日志中不光會記錄select語句,對數據庫修改語句如果符合條件也會記錄 
執行sql語句的時間 比 long_query_time 大都會被記錄

隨着MySQL數據庫服務器運行時間的增加,會有越來越多的SQL語句記錄到慢查詢日志中,此時分析該文件顯得不那么簡單和直觀,這個時候可以使用

mysqldumpslow命令來協助分析慢查詢日志 也可以通過pt 工具來分析,推薦用pt的工具。

例如提取執行時間最長的3條SQL 語句

mysqldumpslow -s t -n 3  -a master01-slow.log

如果慢查詢的日志文件查詢看着不舒服 想通過sql 方式的來看 
哪可以通過動態修改log_output參數將慢查詢輸出到mysql庫下的表中 
默認是以文件的輸出的方式

 

show variables like 'log_output';
修改為
set  global log_output='table';
查看一下表結構
desc mysql.slow_log;

  

 

 模擬慢查詢 就會輸出到mysql.slow_log表中

 

 作用 : 分析常用sql  ,看懂sql的具體作用,是否可以優化

 

2.2.3 查詢日志

查詢日志記錄了所有對MySQL數據庫請求的信息。 
通過兩個參數來啟動:

general_log=on
general_log_file=/usr/local/mysql/data/general_log

  

開啟了這個文件 會記錄 mysql 會話請求連接中 會實時記錄所操作 的 ddl ,dml 語句 ,會導致這個文件變得,不一會就會變得很大, 影響存儲。 
開啟這個查詢日志一般都是用於排除一些異常才會開啟。

2.2.4 二進制日志文件

二進制日志binary log記錄了對MySQL數據庫執行更改的所有操作,但不包括select和show這類操作。其主要作用為: 
恢復:例如在一個數據庫全備文件恢復后,用戶可以通過二進制日志進行增量恢復 
復制:通過執行二進制日志使遠程的一台MySQL數據庫與本數據庫進行數據同步 
審計:用戶可以通過二進制日志中的信息來進行審計,判斷是否有對數據庫進行注入攻擊 

 

通過配置參數log-bin[=name]可以啟動二進制日志,如果不指定name,則默認二進制日志文件名為主機名,后綴名為二進制日志的序列號

比如在服務器上的mysql-bin.000015為一個二進制日志文件,mysql-bin.index文件為二進制的索引文件,用來存儲過往產生的二進制日志序號 

 

 影響二進制日志文件的其他參數:

Max_binlog_size:指定了單個二進制日志文件的最大值,如果超過該值,則產生新的二進制日志文件,后綴名+1,並記錄到.index文件中,默認是1G

 binlog_cache_size:對InnoDB來說,所有未提交的事務的二進制日志都會先寫入到緩存中,只有當事務提交時將緩存中的二進制日志寫入到日志文件中。而緩存的大小由binlog_cache_size決定,默認是32K。當一個線程開啟一個事務時,會自動分配32K的大小的binlog緩存空間,當事務的記錄大於32K大小的時候,則會把緩存中的日志寫入到臨時文件中,可以通過查詢binlog_cache_disk_use參數查看寫入到臨時文件的次數

 

 在默認情況下由於緩存的存在,所以每個事務並不是在發起的時候寫入到二進制日志中,所以當數據庫在事務執行過程中宕機,則會有部分二進制日志未寫入到文件的情況,參數sync_binlog=[N]用來控制此行為。 N參數表示每寫多少次緩存就同步數據到磁盤,如果設置為1,則表示將緩存的內容同步寫入到磁盤中 

 sync_binlog默認取值為1 在5.7.x 
5.6 版本好像是0

binlog_do_db和binlog_ignore_db表示需要寫入和忽略哪些庫的二進制日志的寫入,默認是空,表示所有數據庫的二進制日志都要寫入

Log_slave_update參數用來將從master上取得並執行的二進制日志寫入到自己的二進制日志文件中去,通常在需要搭建master=>slave=>slave (一主多從,多主多從)架構的復制時,需要設置該參數

Binlog_format參數決定了二進制日志文件的內容格式,其取值可以是statement,row或者是mixed

 

2.3 MySQL 套接字文件

套接字文件:在unix系統下本地連接MySQL可以采用unix域套接字方式,這種方式需要一個套接字(socket)文件,其位置和名稱由參數socket控制,一般在/tmp目錄下,名為mysql.sock

show variables like 'socket';

  

對mysql.sock來說,其作用是程序與mysqlserver處於同一台機器, 發起本地連接時可用。

例如你無須定義連接host的具體IP地址,只要為空或localhost就可以。 在此種情況下,即使你改變mysql的外部port也是一樣可能正常連接

2.4 MySQL進程文件

Pid文件:當MySQL實例啟動時,會將自己的進程ID寫入到一個文件中,該文件由參數pid_file控制,默認是在數據庫目錄下,文件名為主機名.pid

show variables like 'pid_file';

  

2.5 MySQL 表結構文件

表結構定義文件:MySQL無論表采用哪種存儲引擎,都會產生一個以frm為后綴名的文件,這個文件記錄了該表的表結構定義

frm還用來存放視圖定義,該文件是文本文件,可以直接使用cat命令來查看視圖定義

只有視圖的的frm 可以直接查看該結構的文件

 

2.6 MySQL存儲引擎文件

InnoDB存儲引擎文件包括以下幾種: 
表空間文件: 

分為共享表空間文件(ibdata1)和獨立表空間 
由innodb_data_file_path參數控制,所有基於InnoDB存儲引擎的表的數據都會記錄到該共享表空間中 
而如果設置了innodb_file_per_table參數,則每個innodb表都會產生一個獨立的表空間,獨立表空間的命令規則為表名.ibd,通過這種方式,用戶不用將所有數據都存放在默認表空間中。 

需要說明的是獨立表空間文件僅存儲該表的數據、索引等信息,其余信息還是存放在共享表空間中, 
例如undo_log ,buffer,Innodb表的元數據都放在ibdata1 里面 等等

 

InnoDB存儲引擎文件包括以下幾種: 

重做日志文件:默認情況下,在InnoDB存儲引擎的數據目錄下會有兩個名ib_logfile0和ib_logfile1的文件,叫重做日志文件,記錄列對於InnoDB存儲引擎的事務日志,當數據庫實例重啟時,InnoDB存儲引擎會使用重做日志恢復到重啟前的時刻,以此來保證數據的完整性

 

重做日志和二進制日志的區別在於: 

二進制日志會記錄所有MySQL數據庫有關的日志記錄,而重做日志僅記錄有關InnoDB存儲引擎本身的事務日志

二進制日志的內容是每個事務的具體操作內容,而重做日志文件記錄的是關於每個數據頁的更改情況

 

3 InnoDB 體系結構

 

 

4 Mysql 后台線程

mysql> use performance_schema
mysql> select name,count(*) from threads group by name;
+----------------------------------------+----------+
| name                                   | count(*) |
+----------------------------------------+----------+
| thread/innodb/buf_dump_thread          |        1 |
| thread/innodb/dict_stats_thread        |        1 |
| thread/innodb/io_ibuf_thread           |        1 |
| thread/innodb/io_log_thread            |        1 |
| thread/innodb/io_read_thread           |        4 |
| thread/innodb/io_write_thread          |        4 |
| thread/innodb/page_cleaner_thread      |        1 |
| thread/innodb/srv_error_monitor_thread |        1 |
| thread/innodb/srv_lock_timeout_thread  |        1 |
| thread/innodb/srv_master_thread        |        1 |
| thread/innodb/srv_monitor_thread       |        1 |
| thread/innodb/srv_purge_thread         |        1 |
| thread/innodb/srv_worker_thread        |        3 |
| thread/sql/compress_gtid_table         |        1 |
| thread/sql/main                        |        1 |
| thread/sql/one_connection              |        1 |
| thread/sql/signal_handler              |        1 |
| thread/sql/thread_timer_notifier       |        1 |
+----------------------------------------+----------+

  

 

 

 

Master主線程 

1、 Master thread線程的優先級最高,內部主要是4個循環loop組成:主循環、后台循環、刷新循環、暫停循環。 
2、在master thread線程里,每1秒或每10秒會觸發1oop(循環體)工作,loop為主循環,大多數情況下都運行在這個循環體。 loop通過sleep()來實現定時的操作,所以操作時間不精准。負載高的情況下可能會有延遲; 
3、dirty page:當事務(Transaction)需要修改某條記錄(row)時,InnoDB需要將該數據所在的page從disk讀到buffer pool中,事務提交后,InnoDB修改page中的記錄(row)。這時buffer pool中的page就已經和disk中的不一樣了,我們稱buffer pool中的被修改過的page為dirty page。 Dirty page等待flush到disk上。 
4、insert buffer merge: 
innodb使用insert buffer” 欺騙”數據庫:對於為非唯一索引,輔助索引的修改操作並非實時更新索引的葉子頁,而是把若干對同一頁面的更新緩存起來做合並(merge)為一次性更新操作,轉化隨機IO為順序IO,這樣可以避免隨機IO帶來性能損耗,提高數據庫的寫性能。 
(1)Insert Buffer是Innodb處理非唯一索引更新操作時的一個優化。最早的Insert Buffer,僅僅實現Insert操作的Buffer,這也是Insert Buffer名稱的由來。在后續版本中,Innodb多次對Insert Buffer進行增強,到Innodb 5.5版本,Insert Buffer除了支持Insert,還新增了包括Update/Delete/Purge等操作的buffer功能,Insert Buffer也隨之更名為Change Buffer。

(2)insert buffer merge分為主動給merge和被動merge。 
(2.1)master thread線程里的insert buffer merge是主動merge,原理是: 
a、若過去1秒內發生的IO小於系統IO能力的5%,則主動進行一次insert buffer merge(merge的頁面數為系統IO能力的5%且讀取page采用async io模式)。 
b、每10秒,必須觸發一次insert buffer merge(merge的頁面數仍舊為系統IO能力的5%)

(2.2)被動Merge,則主要是指在用戶線程執行的過程中,由於種種原因,需要將insert buffer的修改merge到page之中。被動Merge由用戶線程完成,因此用戶能夠感知到merge操作帶來的性能影響。 
例如: 
a、 Insert操作,導致頁面空間不足,需要分裂。由於insert buffer只能針對單頁面,不能buffer page split,因此引起頁面的被動Merge; 
b、 insert操作,由於其他各種原因,insert buffer優化返回失敗,需要真正讀取page時,也需要進行被動Merge; 
c、在進行insert buffer操作時,發現insert buffer已經太大,需要壓縮insert buffer。 
5、 check point: 
(1)checkpoint干的事情:將緩沖池中的臟頁刷新到磁盤 
(2)checkpoint解決的問題: 
a、縮短數據庫的恢復時間(數據庫宕機時,不需要重做所有的日志,因checkpoint之前的頁都已經刷新回磁盤啦) 
b、緩沖池不夠用時,將臟頁刷新到磁盤(緩沖池不夠用時,根 
據LRU算法算出最近最少使用的頁,若此頁為臟頁,需要強制執行checkpoint將臟也刷回磁盤) 
c、重做日志不可用時,刷新臟頁(采用循環使用的,並不是無限增大。當重用時,此時的重做日志還需要使用,就必須強制執行checkpoint將臟頁刷回磁盤)

IO thread 
在innodb存儲引擎中大量使用AIO來處理IO請求,這樣可以極大提高數據庫的性能,而IO thread的工作就是負責這些IO請求的回調處理(call back); 
lock monitor thread

error monitor thread

purge thread 
1、 事務被提交后,其所使用的undo log可能將不再需要,因此需要purge thread來回收已經使用並分配的undo頁; 
2、從mysql5.5開始,purge操作不再做主線程的一部分,而作為獨立線程。 
3、開啟這個功能:innodb_purge_threads=1。調整innodb_purge_batch_size來優化purge操作,batch size指一次處理多少undo log pages, 調大這個參數可以加塊undo log清理(類似oracle的undo_retention)。 
從mysql5.6開始,innodb_purge_threads調整范圍從0–1到0–32,支持多線程purge,innodb-purgebatch-size會被多線程purge共享

page cleaner thread 
page cleaner thread是在innodb1.2.x中引用的,作用是將之前版本中臟頁的刷新操作都放入到單獨的線程中來完成,其目的是為了減輕master thread的工作及對於用戶查詢線程的阻塞,進一步提高innodb存儲引擎的性能。

 

 

5 MySQL語句執行過程

mysql執行一個查詢的過程,執行的步驟包括:

 

    1. 客戶端發送一條查詢給服務器;

    2. 服務器先檢查查詢緩存,如果命中了緩存,則立刻返回存儲在緩存中的結果。否則進入下一階段。

    3. 服務器段進行SQL解析、預處理,在優化器生成對應的執行計划;

    4. mysql根據優化器生成的執行計划,調用存儲引擎的API來執行查詢。

    5. 將結果返回給客戶端。 
      簡單的來說: 
      SQL權限的檢查 –>SQL語法語意分析 –> 查詢緩存 –> 服務器SQL解析 –> 執行

 

 

5.1 查詢狀態

對於mysql連接,任何時刻都有一個狀態,該狀態表示了mysql當前正在做什么。 
使用show full processlist命令查看當前狀態。在一個查詢生命周期中,狀態會變化很多次,下面是這些狀態的解釋: 

sleep:線程正在等待客戶端發送新的請求;
query:線程正在執行查詢或者正在將結果發送給客戶端; 
locked:在mysql服務器層,該線程正在等待表鎖。 
analyzing and statistics:線程正在收集存儲引擎的統計信息,並生成查詢的執行計划; 
copying to tmp table:線程在執行查詢,並且將其結果集復制到一個臨時表中,這種狀態一般要么是做group by操作,要么是文件排序操作,或者union操作。如果這個狀態后面還有on disk標記,那表示mysql正在將一個內存臨時表放到磁盤上。 
sorting Result:線程正在對結果集進行排序。 
sending data:線程可能在多個狀態間傳送數據,或者在生成結果集,或者在想客戶端返回數據。

5.2 查詢緩存

在解析一個查詢語句之前,如果查詢緩存是打開的,那么mysql會優先檢查這個查詢是否命中查詢緩存中的數據。這個檢查是通過一個對大小寫敏感的哈希查找實現的。這個檢查是通過一個對大小寫敏感的哈希查找實現的。 

如果當前的查詢恰好命中了查詢緩存,那么在返回查詢結果之前mysql會檢查一次用戶權限。這仍然是無須解析查詢SQL語句的,因為在查詢緩存中已經存放了當前查詢需要訪問的表信息。如果權限沒有問題,mysql會跳過所有其他階段,直接從緩存中拿到結果並返回給客戶端。沒權限這種情況下,查詢不會被解析,不用生成執行計划,不會被執行。

5.3 查詢優化處理

查詢的生命周期的下一步是將一個SQL轉換成一個執行計划mysql在依照這個執行計划和存儲引擎進行交互。這包含多個子階段解析SQL、預處理、優化SQL執行計划這個過程中任何錯誤都可能終止查詢 
語法解析器和預處理:首先mysql通過關鍵字將SQL語句進行解析,並生成一顆對應的“解析樹”。 mysql 
解析器將使用mysql語法規則驗證和解析查詢;預處理器則根據一些mysql規則進一步檢查解析樹是否合法。 

查詢優化器:當語法樹被認為是合法的了,並且由優化器將其轉化成執行計划。 一條查詢SQL語句可以有很多種執行方式,最后都返回相同的結果。優化器的作用就是找到這其中最好的執行計划。

 

執行計划:mysql不會生成查詢字節碼來執行查詢,mysql生成查詢的一棵指令樹,然后通過存儲引擎執行完成這棵指令樹並返回結果。最終的執行計划包含了重構查詢的全部信息。

5.4 查詢執行引擎

在解析和優化階段,mysql將生成查詢對應的執行計划,mysql的查詢執行引擎則根據這個執行計划來完成整個查詢。這里執行計划是一個數據結構,而不是和很多其他的關系型數據庫那樣對應的字節碼 
mysql簡單的根據執行計划給出的指令逐步執行。在根據執行計划逐步執行的過程中,有大量的操作需要通過調用存儲引擎實現的接口來完成。為了執行查詢,mysql只需要重復執行計划中的各個操作,直到完成所有的數據查詢。

 

 

5.5 返回結果給客戶端

查詢執行的最后一個階段是將結果返回給客戶端。即使查詢不需要返回結果給客戶端,mysql仍然會返回這個查詢的一些信息,如該查詢影響到的行數。如果查詢可以被緩存,那么mysql在這個階段也會將結果放到查詢緩存中。 

mysql將結果集返回客戶端是一個增量、逐步返回的過程。這樣有兩個好處:

    1. 服務器端無須存儲太多的結果,也就不會因為返回太多結果而消耗太多的內存;

    2. 這樣處理也讓mysql客戶端第一時間獲得返回的結果。 
      結果集中的每一行都會以一個滿足mysql客戶端/服務器通信協議的包發送,再通過tcp協議進行傳輸,在tcp傳輸的過程中,可能對mysql的封包進行緩存然后批量傳輸。

 

 

6 MySQL查詢優化器

MySQL采用了基於開銷的優化器,以確定處理查詢的最解方式,也就是說執行查詢之前,都會先選擇一條自以為最優的方案,然后執行這個方案來獲取結果。 在很多情況下, MySQL能夠計算最佳的可能查詢計划,但在某些情況下, MySQL沒有關於數據的足夠信息,或者是提供太多的相關數據信息,估測就不那么友好了

MySQL優化器中,一個主要的目標是只要可能就是用索引,而且使用條件最嚴格的索引來盡可能多、盡可能快地排除那些不符合索引條件的數據行,說白了就是選擇怎樣使用索引,當然優化器還受其他的影響。

 

6.1 影響優化器的使用有哪些

  1. 強制索引 
    通過FORCE INDEX(索引1[,索引2])或者使用USE INDEX(索引1[,索引2]),來指定使用哪個索引,也可以指定多個索引,讓 
    優化器從中挑選。

  2. 忽略索引 
    可以使用IGNORE INDEX(索引1[,索引2])來忽略一些索引,這樣優化器,就不會考慮使用這些所有,減少優化器優化時間。

  3. STRAGHT_JOIN 
    這個會優化器使用數據表的順序 
    一般情況下,MySQL優化器會自行決定按照哪種順序掃描數據表才能最快地檢索出數據,但是我們可以通過STRAGHT_JOIN強制優化器按特定的順序使用數據表,畢竟優化器做的判斷不一定都是最優的。 
    使用原則是: 
    讓限制最強的選取操作最先執行。 STRAIGHT_JOIN可以放在SELECT后面,也可以放在FROM子句中。

注:STRAIGHT_JOIN只適用於inner join,並不使用與left join,right join。

 

6.2 查詢優化器所做的事情

    1. 常量轉化 
      它能夠對sql語句中的常量進行轉化,比如下面的表達式: WHERE col1 = col2 AND col2 = ‘x’; 依據傳遞性:如果A=B and B=C,那么就能得出A=C。所以上面的表達式mysql查詢優化器能進行如下的優化:WHERE col1 = ‘x’ AND col2 = ‘x’

    2. 無效代碼的排除 
      查詢優化器會對一些無用的條件進行過濾,比如說 WHERE 0=0 AND column1=’y’ 因為第一個條件是始終為true的,所以可以移除該條件,變為:WHERE column1=’y’再見如下表達式:WHERE (0=1 AND s1=5) OR s1=7因為前一個括號內的表達式始終為false,因此可以移除該表達式,變為:WHERE s1=7 
      一些情況下甚至可 以將整個WHERE子句去掉,見下面的表達式:WHERE (0=1 AND s1=5)我們可以看到, WHERE子句始終為FALASE,那么WHERE條件是不可能發生的。當然我們也可以講,WHERE條件被優化掉了

    3. 常量計算 
      如下表達式:WHERE col1 = 1 + 2轉化為:WHERE col1 = 3 Mysql會對常量表達進行計算,然后將結果生成條件

    4. 存取類型 
      當我們評估一個條件表達式,MySQL判斷該表達式的存取類型。下面是一些存取類型,按照從最優到最差的順序進行排列: 
      system系統表,並且是常量表 
      const 常量表 
      eq_ref unique/primary索引,並且使用的是’=’進行存取 
      ref 索引使用’=’進行存取 
      ref_or_null 索引使用’=’進行存取,並且有可能為NULL 
      range 索引使用BETWEEN、 IN、 >=、 LIKE等進行存取 
      ALL 表全掃描 
      優化器根據存取類型選擇合適的驅動表達式。考慮如下的查詢語句:以下是引用片段:

SELECT * FROM Table1 WHERE indexed_column=5 AND unindexed_column=6

  

因為indexed_column擁有更好的存取類型,所以更有可能使用該表達式做為驅動表達式。考慮到這個查詢語句有兩種可能的執行方法: 
1) 不好的執行路徑:讀取表的每一行(稱為“全表掃描” ),對於讀取到的每一行,檢查相應的值是否滿足indexed_column以及 unindexed_column對應的條件。 
2) 好的執行路徑:通過鍵值indexed_column=5查找B樹,對於符合該條件的每一行,判斷是否滿足unindexed_column對應的條件。 
一般情況下,索引查找比全表掃描需要更少的存取路徑,尤其當表數據量很大,並且索引的類型是UNIQUE的時候。因此稱它為好的執行路徑,使用 indexed_column列作為驅動表達式。

  1. 范圍存取類型 
    一些表達式可以使用索引,但是屬於索引的范圍查找。這些表達式通常對應的操作符是:>、 >=、 <、 <=、IN、 LIKE、 BETWEEN。 
    對優化器而言,如下表達式: 
    column1 IN (1,2,3) 
    該表達式與下面的表達式是等價的: 
    column1 = 1 OR column1 = 2 OR column1 = 3 
    並且 MySQL也是認為它們是等價的,所以沒必要手動將IN改成OR,或者把OR改成IN。 
    優化器將會對下面的表達式使用索引范圍查找:column1 LIKE ‘x%’,但對下面的表達式就不會使用到索引了:column1 LIKE ‘%x’,這是因為當首字符是通配符的時候, 沒辦法使用到索引進行范圍查找。 
    對優化器而言,如下表達式: 
    column1 BETWEEN 5 AND 7 
    該表達式與下面的表達式是等價的: 
    column1 >= 5 AND column1 <= 7同樣, 
    MySQL也認為它們是等價的。

  2. 索引存取類型 
    考慮如下的查詢語句:SELECT column1 FROM Table1;如果column1是索引列, 優化器更有可能選擇索引全掃描,而不是采用表全掃描。這是因為該索引覆蓋了我們所需要查詢的列。 
    再考慮如下的查詢語句: 
    SELECT column1,column2 FROM Table1; 如果索引的定義如下,那么就可以使用索引全掃描: 
    CREATE INDEX … ON Table1(column1,column2); 
    也就是說,所有需要查詢的列必須在索引中出現。但是如下的查詢就只能走全表掃描了: select col3 from Table1;由於col3沒有建立索引所以只能走全表掃描。

  3. 轉換 
    MySQL對簡單的表達式支持轉換。比如下面的語法: 
    WHERE -5 = column1轉換為: WHERE column1 = -5 盡管如此,對於有數學運算存在的情況不會進行轉換。 
    比如下面的語法: 
    WHERE 5 = -column1不會轉換為:WHERE column1 = -5,所以盡量減少列上的運算,而將運算放到常量上

  4. AND 
    帶AND的查詢的格式為: AND ,考慮如下的查詢語句: 
    WHERE column1=’x’ AND column2=’y’

優化的步驟: 
1) 如果兩個列都沒有索引,那么使用全表掃描。 
2) 否則,如果其中一個列擁有更好的存取類型(比如,一個具有索引,另外一個沒有索引;再或者,一個是唯一索引,另外一個是非唯一索引),那么使用該列作為驅動表達式

    1. OR 
      帶OR的查詢格式為: OR ,考慮如下的查詢語句:WHERE column1=’x’ OR column2=’y’ 
      優化器做出的選擇是采用全表掃描。當然,在一些特定的情況,可以使用索引合並,這里不做闡述。如果兩個條件里面設計的列是同一列,那么又是另外一種情況,考慮如下的查詢語句:WHERE column1=’x’ OR column1=’y’在這種情況下,該查詢語句采用索引范圍查找

    2. UNION 
      所有帶UNION的查詢語句都是單獨優化的,考慮如下的查詢語句:以下是引用片段:

SELECT * FROM Table1 WHERE column1='x'
UNION ALL SELECT * FROM Table1 WHER column2='y'

  

 

  1. order by 
    一般而言,ORDER BY的作用是使結果集按照一定的順序排序,如果可以不經過此操作就能產生順序的結果,可以跳過ORDER BY操作。考慮如下的查詢 語句: 
    SELECT column1 FROM Table1 ORDER BY ‘x’;優化器將去除該ORDER BY子句,因為此處的ORDER BY 子句沒有意義。再考慮另外的一個查詢語句:SELECT column1 FROM Table1 ORDER BY column1;在這種情況下,如果column1類上存在索引,優化器將使用該索引進行全掃描,這樣產生的結果集是有序的,從而不需要進行ORDER BY操作。

再考慮另外的一個查詢語句:SELECT column1 FROM Table1 ORDER BY column1+1; 假設column1上存在索引,我 們也許會覺得優化器會對column1索引進行全掃描,並且不進行ORDER BY操作。實際上,情況並不是這樣,優化器是使用column1列上的索引進行全掃表,僅僅是因為索引全掃描的效率高於表全掃描。對於索引全掃描的結果集 仍然進行ORDER BY排序操作

  1. GROUP BY 
    這里列出對GROUP BY子句以及相關集函數進行優化的方法: 
    1) 如果存在索引,GROUP BY將使用索引。 
    2) 如果沒有索引,優化器將需要進行排序,一般情況下會使用HASH表的方法 
    3) 如果情況類似於“GROUP BY x ORDER BY x”,優化器將會發現ORDER BY子句是沒有必要的,因為GROUP BY產生的結果集是按照x進行排序的 
    4) 盡量將HAVING子句中的條件提升中WHERE子句中。 
    5) 對於MyISAM表,“SELECT COUNT(*) FROM Table1;” 
    直接返回結果,而不需要進行表全掃描。但是對於InnoDB表,則不適合該規則。補充一點,如果column1的定義是NOT NULL的,那么語句“SELECT COUNT(column1) FROM Table1;” 等價於“SELECT COUNT(*) FROM Table1;” 。 
    6) 考慮MAX()以及MIN()的優化情況。考慮下面的查詢語句:以下是引用片段: 
    SELECT MAX(column1) FROM Table1 WHERE column1<’a’; 如果column1列上存在索引,優化器使用’a’進行索引定位,然后返回前一條記錄。

       7) 考慮如下的查詢語句: 

SELECT DISTINCT column1 FROM Table1;在特定的情況下,語句可以轉化為: 
SELECT column1 FROM Table1 GROUP BY column1;轉換的前提條件是:column1上存 在索引, FROM上只有一個單表,沒有WHERE條件並且沒有LIMIT條件

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM