一個高級的J2E工程師須要面對MySQL要有那些基本功夫呢<上>

1. MySQL的架構介紹
1.1 MySQL簡介:
  MySQL是一個關係型數據庫管理系統,由瑞典MySQL AB公司開發,目前屬於Oracle公司。
  MySQL是一種關聯數據庫管理系統,將數據保存在不一樣的表中,而不是將全部數據放在一個大倉庫內,這樣就增長了速度並提升了靈活性。
  Mysql是開源的,因此你不須要支付額外的費用。
  Mysql支持大型的數據庫。能夠處理擁有上千萬條記錄的大型數據庫。
  MySQL使用標準的SQL數據語言形式。
  Mysql能夠容許於多個系統上,而且支持多種語言。這些編程語言包括C、C++、Python、Java、Perl、PHP、Eiffel、Ruby和Tcl等。
  Mysql對PHP有很好的支持,PHP是目前最流行的Web開發語言。
  MySQL支持大型數據庫,支持5000萬條記錄的數據倉庫,32位系統表文件最大可支持4GB,64位系統支持最大的表文件爲8TB。
  Mysql是能夠定製的,採用了GPL協議,你能夠修改源碼來開發本身的Mysql系統(其實阿里巴巴的「去IOE」中,使用的數據庫就是本身開發的阿里版的MySQL)。
1.2 一個高級的J2E工程師須要面對MySQL要有那些基本功夫呢?
  首先,你要逐步理解mysql內核;其次,sql優化、mysql服務器的優化和各類參數常量設定與各類參數常量設定。而後,要會主從複製、容災備份、sql編程和相應的軟硬件升級。固然,這只是一個高級的J2E工程師面對MySQL要有的基本功夫。由於完整的mysql優化須要很深的功底,大公司甚至有專門的DBA工程師進行負責。html

2. 工欲善其事必先利其器(Mysql Linux版的安裝)mysql

   我我的是比較推薦去官網下載軟件和對應的文檔的。官網下載地址:http://dev.mysql.com/downloads/mysql/linux

 

 

MySQL Serverios

MySQL Client算法

 

 

 

下載完成之後,準備安裝之前首先,檢查當前系統是否安裝過mysql:sql

  查詢命令:rpm -qa|grep -i mysql      刪除命令:rpm -e RPM軟件包名(該名字是上一個命令查出來的名字)數據庫

 

  在安裝過程當中,安裝mysql服務端(注意提示),而後再安裝mysql客戶端。安裝完成之後,能夠查看MySQL安裝時建立的mysql用戶和mysql組:編程

  安裝完畢之後,咱們能夠根據本身的須要來進行mysql服務的啓和停:windows

1 # ps -ef|grep mysql   緩存

2 # service mysql start 

3 # service mysql stop  

 

  mysql服務啓動後,開始鏈接(注意這裏,由於MySQL默認沒有密碼,因此這裏咱們沒有輸入密碼就直接連上了):

 

  因此咱們在按照安裝Server時,根據提示修改登陸密碼便可。

  若是咱們常常要使用MySQL來進行開發,就須要將其設置爲自啓動mysql服務:

 

  只要是搞開發過來的,確定遇到過亂碼之類的問題。因此怎麼解決呢?


  1 查看字符集

1 show variables like 'character%';

2 show variables like '%char%';     
  看看出現的結果:

 

  默認的是客戶端和服務器都用了latin1,因此會亂碼。


2 修改

 1 [client]  2 #password = your_password  3 port = 3306
 4 socket = /var/lib/mysql/mysql.sock  5 default-character-set=utf8  6 
 7 # The MySQL server  8 [mysqld]  9 port = 3306
10 character_set_server=utf8 11 character_set_client=utf8 12 collation-server=utf8_general_ci 13 socket = /var/lib/mysql/mysql.sock 14 skip-external-locking 15 key_buffer_size = 384M 16 max_allowed_packet = 1M 17 table_open_cache = 512
18 sort_buffer_size = 2M 19 read_buffer_size = 2M 20 read_rnd_buffer_size = 8M 21 myisam_sort_buffer_size = 64M 22 thread_cache_size = 8
23 query_cache_size = 32M 24 # Try number of CPU's*2 for thread_concurrency
25 thread_concurrency = 8
26 
27 [mysql] 28 no-auto-rehash 29 default-character-set=utf8

 

  3 重啓mysql

1 service mysql stop; 

2 service mysql start; 

  4 從新鏈接後從新create databse並使用新建庫,而後再從新建表試試

  5 仍是亂碼的話就設值init_connect='SET NAMES utf8' ##設定鏈接mysql是UTF8編碼

  在windows系統下咱們能夠很快找到MySQL的安裝路徑,那在linux下呢?

    這其實也很簡單,在linux下查看安裝目錄的命令以下:

 1  ps -ef|grep mysql       

 

 3. Mysql主要配置文件含義及如何配置

3.1 二進制日誌log-bin (主從複製)

3.2 錯誤日誌log-error

  默認是關閉的,記錄嚴重的警告和錯誤信息,每次啓動和關閉的詳細信息等。

3.3 查詢日誌log

  默認關閉,記錄查詢的sql語句,若是開啓會減低mysql的總體性能,由於記錄日誌也是須要消耗系統資源的

3.4 數據文件

  frm文件:存放表結構

  myd文件:存放表數據

  myi文件:存放表索引

 

4. Mysql邏輯架構簡單聊聊

4.1 整體概覽

  和其它數據庫相比,MySQL有點不同凡響,它的架構能夠在多種不一樣場景中應用併發揮良好做用。主要體如今存儲引擎的架構上,
插件式的存儲引擎架構將查詢處理和其它的系統任務以及數據的存儲提取相分離。這種架構能夠根據業務的需求和實際須要選擇合適的存儲引擎。

 

 


4.1.1.鏈接層
  最上層是一些客戶端和鏈接服務,包含本地sock通訊和大多數基於客戶端/服務端工具實現的相似於tcp/ip的通訊。主要完成一些相似於鏈接處理、受權認證、及相關的安全方案。在該層上引入了線程池的概念,爲經過認證安全接入的客戶端提供線程。一樣在該層上能夠實現基於SSL的安全連接。服務器也會爲安全接入的每一個客戶端驗證它所具備的操做權限。

4.1.2.服務層
  第二層架構主要完成核心服務功能,如SQL接口,並完成緩存的查詢,SQL的分析和優化及部份內置函數的執行。全部跨存儲引擎的功能也在這一層實現,如過程、函數等。在該層,服務器會解析查詢並建立相應的內部解析樹,並對其完成相應的優化如肯定查詢表的順序,是否利用索引等,最後生成相應的執行操做。若是是select語句,服務器還會查詢內部的緩存。若是緩存空間足夠大,這樣在解決大量讀操做的環境中可以很好的提高系統的性能。

4.1.3.引擎層
  存儲引擎層,存儲引擎真正的負責了MySQL中數據的存儲和提取,服務器經過API與存儲引擎進行通訊。不一樣的存儲引擎具備的功能不一樣,這樣咱們能夠根據本身的實際須要進行選取(MyISAM和InnoDB)。

4.1.4.存儲層
  數據存儲層,主要是將數據存儲在運行於裸設備的文件系統之上,並完成與存儲引擎的交互。

 

4.2 查詢說明

  首先,mysql的查詢流程大體是:
  mysql客戶端經過協議與mysql服務器建鏈接,發送查詢語句,先檢查查詢緩存,若是命中,直接返回結果,不然進行語句解析有一系列預處理,好比檢查語句是否寫正確了,而後是查詢優化(好比是否使用索引掃描,若是是一個不可能的條件,則提早終止),生成查詢計劃,而後查詢引擎啓動,開始執行查詢,從底層存儲引擎調用API獲取數據,最後返回給客戶端。怎麼存數據、怎麼取數據,都與存儲引擎有關。而後,mysql默認使用的BTREE索引,而且一個大方向是,不管怎麼折騰sql,至少在目前來講,mysql最多隻用到表中的一個索引。

5. Mysql存儲引擎

1 #看你的mysql如今已提供什麼存儲引擎:  mysql> show engines;                         

2 #看你的mysql當前默認的存儲引擎: 5 mysql> show variables like '%storage_engine%';   

 

6. 阿里巴巴、淘寶用哪一個???

  Percona 爲 MySQL 數據庫服務器進行了改進,在功能和性能上較 MySQL 有着很顯著的提高。該版本提高了在高負載狀況下的 InnoDB 的性能、爲 DBA 提供一些很是有用的性能診斷工具;另外有更多的參數和命令來控制服務器行爲。

  該公司新建了一款存儲引擎叫xtradb徹底能夠替代innodb,而且在性能和併發上作得更好,阿里巴巴大部分mysql數據庫其實使用的percona的原型加以修改。

 

7. 影響mysql的性能因素

7.1 業務需求對mysql的影響(合適合度)

  舉一個例子吧:


【要求】
統計對該產品的評價帖子,要實時的!

【問題】
假如這個數據量很小OK,能夠實時查詢,假如愈來愈多(例如淘寶店),積累了好多萬客戶評論帖子
1 select count(*)對於剛開始數量很小時能夠,若是數據增大已經有千萬級的,一個查詢就哭吧,成爲系統性能瓶頸。
2 你正在統計時候也有買家賣家修改增刪評論,你不能要求用戶說我統計的時候大家不準動。

【解決】
單獨一個表,單獨一個字段來保存這個帖子數目。 每個買家新增一條評論就是須要更新一個這個數字,各買家是多個(高併發)不定時的在發佈帖子,實時修改更新後再統計select count(*)。 就算咱們使用的是Innodb存儲引擎,一個update數字他就是行鎖,高併發的瓶頸出現了

【結論】
不少的統計信息都是準實時的而不是實時統計,網站的一些數量信息、分頁信息、排序信息、點擊率信息等等通常都不是實時的而是準實時的。

7.2 存儲定位對mysql的影響

 

7.3 Schema設計對系統的性能影響

  兩個儘可能原則:儘可能減小對數據庫訪問的請求;儘可能減小無用數據的查詢請求

7.4 硬件環境對系統性能的影響

7.4.1 典型OLTP應用系統

  什麼是OLTP:OLTP即聯機事務處理,就是咱們常常說的關係數據庫,意即記錄即時的增、刪、改、查,就是咱們常常應用的東西,這是數據庫的基礎

對於各類數據庫系統環境中你們最多見的OLTP系統,其特色是併發量大,總體數據量比較多,但每次訪問的數據比較少,且訪問的數據比較離散,活躍數據佔整體數據的比例不是太大。對於這類系統的數據庫其實是最難維護,最難以優化的,對主機總體性能要求也是最高的。由於不只訪問量很高,數據量也不小。

針對上面的這些特色和分析,咱們能夠對OLTP的得出一個大體的方向。
  雖然系統整體數據量較大,可是系統活躍數據在數據總量中所佔的比例不大,那麼咱們能夠經過擴大內存容量來儘量多的將活躍數據cache到內存中;雖然IO訪問很是頻繁,可是每次訪問的數據量較少且很離散,那麼咱們對磁盤存儲的要求是IOPS 【 (Input/Output Operations Per Second),即每秒進行讀寫(I/O)操做的次數】表現要很好,吞吐量是次要因素;併發量很高,CPU每秒所要處理的請求天然也就不少,因此CPU處理能力須要比較強勁;雖然與客戶端的每次交互的數據量並非特別大,可是網絡交互很是頻繁,因此主機與客戶端交互的網絡設備對流量能力也要求不能太弱。

7.4.2 典型OLAP應用系統

  用於數據分析的OLAP系統的主要特色就是數據量很是大,併發訪問很少,但每次訪問所須要檢索的數據量都比較多,並且數據訪問相對較爲集中,沒有太明顯的活躍數據概念。

  什麼是OLAP:OLAP即聯機分析處理,是數據倉庫的核心部心,所謂數據倉庫是對於大量已經由OLTP造成的數據的一種分析型的數據庫,用於處理商業智能、決策支持等重要的決策信息;數據倉庫是在數據庫應用到必定程序以後而對歷史數據的加工與分析
  基於OLAP系統的各類特色和相應的分析,針對OLAP系統硬件優化的大體策略以下:
    數據量很是大,因此磁盤存儲系統的單位容量須要儘可能大一些;
    單次訪問數據量較大,並且訪問數據比較集中,那麼對IO系統的性能要求是須要有儘量大的每秒IO吞吐量,因此應該選用每秒吞吐量儘量大的磁盤;
    雖然IO性能要求也比較高,可是併發請求較少,因此CPU處理能力較難成爲性能瓶頸,因此CPU處理能力沒有太苛刻的要求;


  雖然每次請求的訪問量很大,可是執行過程當中的數據大都不會返回給客戶端,最終返回給客戶端的數據量都較小,因此和客戶端交互的網絡設備要求並非過高;

  此外,因爲OLAP系統因爲其每次運算過程較長,能夠很好的並行化,因此通常的OLAP系統都是由多臺主機構成的一個集羣,而集羣中主機與主機之間的數據交互量通常來講都是很是大的,因此在集羣中主機之間的網絡設備要求很高。

 

8. 查詢與索引優化分析

8.1 爲何要優化?

現象:性能降低SQL慢、執行時間長、等待時間長。 

緣由:

查詢語句寫的爛

索引失效(單值、複合)

 關聯查詢太多join(設計缺陷或不得已的需求)

關聯查詢太多join(設計缺陷或不得已的需求)

 

9. 常見通用的Join查詢

9.1 SQL執行順序

手寫:

 

機讀:

 

結論:

9.2 Join圖

1 A、B兩表共有 select * from tbl_emp a inner join tbl_dept b on a.deptId = b.id; 2 A、B兩表共有+A的獨有 select * from tbl_emp a left join tbl_dept b on a.deptId = b.id; 3 A、B兩表共有+B的獨有 select * from tbl_emp a right join tbl_dept b on a.deptId = b.id; 4 A的獨有 select * from tbl_emp a left join tbl_dept b on a.deptId = b.id where b.id is null; 5 B的獨有 select * from tbl_emp a right join tbl_dept b on a.deptId = b.id where a.deptId is null; #B的獨有 6 AB全有 #MySQL Full Join的實現 由於MySQL不支持FULL JOIN,下面是替代方法 #left join + union(可去除重複數據)+ right join
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 7 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.`deptId` IS NULL;

 

9.4 建表SQL

 1 CREATE TABLE `tbl_dept` (  2  `id` INT(11) NOT NULL AUTO_INCREMENT,  3  `deptName` VARCHAR(30) DEFAULT NULL,  4  `locAdd` VARCHAR(40) DEFAULT NULL,  5  PRIMARY KEY (`id`)  6 ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;  7 
 8 CREATE TABLE `tbl_emp` (  9  `id` INT(11) NOT NULL AUTO_INCREMENT, 10  `name` VARCHAR(20) DEFAULT NULL, 11  `deptId` INT(11) DEFAULT NULL, 12  PRIMARY KEY (`id`), 13  KEY `fk_dept_id` (`deptId`) 14  #CONSTRAINT `fk_dept_id` FOREIGN KEY (`deptId`) REFERENCES `tbl_dept` (`id`) 15 ) ENGINE=INNODB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; 16 
17 
18 
19 INSERT INTO tbl_dept(deptName,locAdd) VALUES('RD',11); 20 INSERT INTO tbl_dept(deptName,locAdd) VALUES('HR',12); 21 INSERT INTO tbl_dept(deptName,locAdd) VALUES('MK',13); 22 INSERT INTO tbl_dept(deptName,locAdd) VALUES('MIS',14); 23 INSERT INTO tbl_dept(deptName,locAdd) VALUES('FD',15); 24 
25 
26 INSERT INTO tbl_emp(NAME,deptId) VALUES('z3',1); 27 INSERT INTO tbl_emp(NAME,deptId) VALUES('z4',1); 28 INSERT INTO tbl_emp(NAME,deptId) VALUES('z5',1); 29 
30 INSERT INTO tbl_emp(NAME,deptId) VALUES('w5',2); 31 INSERT INTO tbl_emp(NAME,deptId) VALUES('w6',2); 32 
33 INSERT INTO tbl_emp(NAME,deptId) VALUES('s7',3); 34 
35 INSERT INTO tbl_emp(NAME,deptId) VALUES('s8',4); 36 
37 INSERT INTO tbl_emp(NAME,deptId) VALUES('s9',51); 38  

 

10. 什麼是索引

10.1 是什麼

  MySQL官方對索引的定義爲:索引(Index)是幫助MySQL高效獲取數據的數據結構。能夠獲得索引的本質:索引是數據結構。

  索引的目的在於提升查詢效率,能夠類比字典,若是要查「mysql」這個單詞,咱們確定須要定位到m字母,而後從下往下找到y字母,再找到剩下的sql。若是沒有索引,那麼你可能須要a----z,若是我想找到Java開頭的單詞呢?或者Oracle開頭的單詞呢?是否是以爲若是沒有索引,這個事情根本沒法完成?因此你能夠簡單理解爲「排好序的快速查找結構」。

  在數據以外,數據庫系統還維護着知足特定查找算法的數據結構,這些數據結構以某種方式引用(指向)數據,
這樣就能夠在這些數據結構上實現高級查找算法。這種數據結構,就是索引。下圖就是一種可能的索引方式示例:

 

  左邊是數據表,一共有兩列七條記錄,最左邊的是數據記錄的物理地址
  爲了加快Col2的查找,能夠維護一個右邊所示的二叉查找樹,每一個節點分別包含索引鍵值和一個指向對應數據記錄物理地址的指針,這樣就能夠運用二叉查找在必定的複雜度內獲取到相應數據,從而快速的檢索出符合條件的記錄。

  通常來講索引自己也很大,不可能所有存儲在內存中,所以索引每每以索引文件的形式存儲的磁盤上。咱們日常所說的索引,若是沒有特別指明,都是指B+樹結構組織的索引。其中彙集索引,次要索引,覆蓋索引,複合索引,前綴索引,惟一索引默認都是使用B+樹索引,統稱索引。固然,除了B+樹這種類型的索引以外,還有哈稀索引(hash index)等。

10.2 優缺點

  優勢:經過索引列對數據進行排序,下降數據排序的成本,下降了CPU的消耗;提升數據檢索的效率,下降數據庫的IO成本

  缺點:實際上索引也是一張表,該表保存了主鍵與索引字段,並指向實體表的記錄,因此索引列也是要佔用空間的;雖然索引大大提升了查詢速度,同時卻會下降更新表的速度,如對錶進行INSERT、UPDATE和DELETE。由於更新表時,MySQL不只要保存數據,還要保存一下索引文件每次更新添加了索引列的字段,都會調整由於更新所帶來的鍵值變化後的索引信息;索引只是提升效率的一個因素,若是你的MySQL有大數據量的表,就須要花時間研究創建最優秀的索引,或優化查詢語句

 10.3 mysql索引分類

  單值索引:即一個索引只包含單個列,一個表能夠有多個單列索引。

  惟一索引:索引列的值必須惟一,但容許有空值。

  複合索引:即一個索包含多個列。

基本語法:

  建立:

  1. CREATE  [UNIQUE ] INDEX indexName ON mytable(columnname(length));      -->若是是CHAR,VARCHAR類型,length能夠小於字段實際長

度;若是是BLOB和TEXT類型,必須指定length。

  2. ALTER mytable ADD  [UNIQUE ]  INDEX [indexName] ON (columnname(length)) 

  刪除:

  DROP INDEX [indexName] ON mytable; 

  查看:

  SHOW INDEX FROM table_name

  使用ALTER命令(有四種方式來添加數據表的索引):

  1. ALTER TABLE tbl_name ADD PRIMARY KEY (column_list): 該語句添加一個主鍵,這意味着索引值必須是惟一的,且不能爲NULL。

  2. ALTER TABLE tbl_name ADD UNIQUE index_name (column_list): 這條語句建立索引的值必須是惟一的(除了NULL外,NULL可能會出現屢次)。

  3. ALTER TABLE tbl_name ADD INDEX index_name (column_list): 添加普通索引,索引值可出現屢次。

  4. ALTER TABLE tbl_name ADD FULLTEXT index_name (column_list):該語句指定了索引爲 FULLTEXT ,用於全文索引。

10.4 mysql索引結構(BTree索引、Hash索引、full-text全文索引、R-Tree索引)

  BTree索引檢索原理:

 

【初始化介紹】
  一顆b+樹,淺藍色的塊咱們稱之爲一個磁盤塊,能夠看到每一個磁盤塊包含幾個數據項(深藍色所示)和指針(黃色所示),
如磁盤塊1包含數據項17和35,包含指針P一、P二、P3,
P1表示小於17的磁盤塊,P2表示在17和35之間的磁盤塊,P3表示大於35的磁盤塊。
真實的數據存在於葉子節點即三、五、九、十、1三、1五、2八、2九、3六、60、7五、7九、90、99。
非葉子節點只不存儲真實的數據,只存儲指引搜索方向的數據項,如1七、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,顯然成本很是很是高。

 

10.5 哪些狀況須要建立索引

  1. 主鍵自動創建惟一索引

  2. 頻繁做爲查詢條件的字段應該建立索引

  3. 查詢中與其它表關聯的字段,外鍵關係創建索引

  4. 頻繁更新的字段不適合建立索引(由於每次更新不僅僅是更新了記錄還會更新索引,加劇了IO負擔) 

  5. Where條件裏用不到的字段不建立索引

  6. 單鍵/組合索引的選擇問題,who?(在高併發下傾向建立組合索引)

  7. 查詢中排序的字段,排序字段若經過索引去訪問將大大提升排序速度

  8. 查詢中統計或者分組字段

 

10.6 哪些狀況不要建立索引

  1. 表記錄太少

  2. 常常增刪改的表(由於提升了查詢速度,同時卻會下降更新表的速度,如對錶進行INSERT、UPDATE和DELETE。由於更新表時,MySQL不只要保存數據,還要保存一下索引文件)

   3. 數據重複且分佈平均的表字段,所以應該只爲最常常查詢和最常常排序的數據列創建索引。注意,若是某個數據列包含許多重複的內容,爲它創建索引就沒有太大的實際效果。

 

11. MySql Query Optimizer

  11.1 Mysql中有專門負責優化SELECT語句的優化器模塊,主要功能:經過計算分析系統中收集到的統計信息,爲客戶端請求的Query提供他認爲最優的執行計劃(他認爲最優的數據檢索方式,但不見得是DBA認爲是最優的,這部分最耗費時間)

  11.2 當客戶端向MySQL 請求一條Query,命令解析器模塊完成請求分類,區別出是 SELECT 並轉發給MySQL Query Optimizer時,MySQL Query Optimizer 首先會對整條Query進行優化,處理掉一些常量表達式的預算,直接換算成常量值。並對 Query 中

的查詢條件進行簡化和轉換,如去掉一些無用或顯而易見的條件、結構調整等。而後分析 Query 中的 Hint 信息(若是有),看顯示Hint信息是否能夠徹底肯定該Query 的執行計劃。若是沒有 Hint 或Hint 信息還不足以徹底肯定執行計劃,則會讀取所涉及對象的統計信

息,根據 Query 進行寫相應的計算分析,而後再得出最後的執行計劃。

  11.3 MySQL常見瓶頸

  CPU:CPU在飽和的時候通常發生在數據裝入內存或從磁盤上讀取數據時候;

  IO:磁盤I/O瓶頸發生在裝入數據遠大於內存容量的時候;

  服務器硬件的性能瓶頸:能夠經過(Linux命令)top,free, iostat和vmstat命令來查看系統的性能狀態。

  11.4 EXPLAIN關鍵字

  使用EXPLAIN關鍵字能夠模擬優化器執行SQL查詢語句,從而知道MySQL是如何處理你的SQL語句的。分析你的查詢語句或是表結構的性能瓶頸。經過它能夠獲得表的讀取順序、數據讀取操做的操做類型、哪些索引可使用、哪些索引被實際使用、表之間的引用、每張表有多少行被優化器查詢。

  那怎麼用呢?

  其實,Explain + SQL語句便可。

  上圖各字段解釋(執行計劃包含的信息)以下:

  1. id

  select查詢的序列號,包含一組數字,表示查詢中執行select子句或操做表的順序。

  三種狀況:

  1.1. id相同,執行順序由上至下

  1.2. id不一樣,若是是子查詢,id的序號會遞增,id值越大優先級越高,越先被執行


  若是是子查詢,id的序號會遞增,id值越大優先級越高,越先被執行。

  1.3. id相同不一樣,同時存在

  id若是相同,能夠認爲是一組,從上往下順序執行;在全部組中,id值越大,優先級越高,越先執行。(衍生 = DERIVED)

  2. select_type

  有哪些?

 

  有什麼做用?

  查詢的類型,主要是用於區別普通查詢、聯合查詢、子查詢等的複雜查詢

  SIMPLE:簡單的 select 查詢,查詢中不包含子查詢或者UNION;

  PRIMARY:查詢中若包含任何複雜的子部分,最外層查詢則被標記爲;

  SUBQUERY:在SELECT或WHERE列表中包含了子查詢;

  DERIVED:在FROM列表中包含的子查詢被標記爲DERIVED(衍生),MySQL會遞歸執行這些子查詢, 把結果放在臨時表裏;

  UNION:若第二個SELECT出如今UNION以後,則被標記爲UNION;若UNION包含在FROM子句的子查詢中,外層SELECT將被標記爲:DERIVED;

  UNION RESULT:從UNION表獲取結果的SELECT。

  3. table

  顯示這一行的數據是關於哪張表的

  4. type

  

  訪問類型排列:

  type顯示的是訪問類型,是較爲重要的一個指標,結果值從最好到最壞依次是:

    system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL ,

    通常來講,得保證查詢至少達到range級別,最好能達到ref。

  顯示查詢使用了何種類型,從最好到最差依次是:
    system>const>eq_ref>ref>range>index>ALL

    system:表只有一行記錄(等於系統表),這是const類型的特列,平時不會出現,這個也能夠忽略不計;

    const:表示經過索引一次就找到了,const用於比較primary key或者unique索引。由於只匹配一行數據,因此很快如將主鍵置於where列表中,MySQL就能將該查詢轉換爲一個常量;

 

    eq_ref:惟一性索引掃描,對於每一個索引鍵,表中只有一條記錄與之匹配。常見於主鍵或惟一索引掃描;

 

    ref:非惟一性索引掃描,返回匹配某個單獨值的全部行.本質上也是一種索引訪問,它返回全部匹配某個單獨值的行,然而,它可能會找到多個符合條件的行,因此他應該屬於查找和掃描的混合體;

 

     range:只檢索給定範圍的行,使用一個索引來選擇行。key 列顯示使用了哪一個索引通常就是在你的where語句中出現了between、<、>、in等的查詢這種範圍掃描索引掃描比全表掃描要好,由於它只須要開始於索引的某一點,而結束語另外一點,不用掃描所有索引;

 

    index:Full Index Scan,index與ALL區別爲index類型只遍歷索引樹。這一般比ALL快,由於索引文件一般比數據文件小。(也就是說雖然all和Index都是讀全表,但index是從索引中讀取的,而all是從硬盤中讀的);

 

     all:Full Table Scan,將遍歷全表以找到匹配的行。

 

  備註:通常來講,得保證查詢至少達到range級別,最好能達到ref。

  5. possible_keys

    顯示可能應用在這張表中的索引,一個或多個。查詢涉及到的字段上若存在索引,則該索引將被列出,但不必定被查詢實際使用

  6. key

    實際使用的索引。若是爲NULL,則沒有使用索引。查詢中若使用了覆蓋索引,則該索引僅出如今key列表中(以下圖所示:)

  

  7. key_len

  表示索引中使用的字節數,可經過該列計算查詢中使用的索引的長度。在不損失精確性的狀況下,長度越短越好。key_len顯示的值爲索引字段的最大可能長度,並不是實際使用長度,即key_len是根據表定義計算而得,不是經過表內檢索出的

 

  8. ref

  顯示索引的哪一列被使用了,若是可能的話,是一個常數。哪些列或常量被用於查找索引列上的值(示例以下:)

由key_len可知t1表的idx_col1_col2被充分使用,col1匹配t2表的col1,col2匹配了一個常量,即 'ac'

  9. rows

  根據表統計信息及索引選用狀況,大體估算出找到所需的記錄所須要讀取的行數

  10. Extra

  包含不適合在其餘列中顯示但十分重要的額外信息

   Using filesort :

 

  說明mysql會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。MySQL中沒法利用索引完成的排序操做稱爲「文件排序」。

 

  Using temporary:

  

  

  使了用臨時表保存中間結果,MySQL在對查詢結果排序時使用臨時表。常見於排序 order by 和分組查詢 group by。

  USING index:

  

  表示相應的select操做中使用了覆蓋索引(Covering Index),避免訪問了表的數據行,效率不錯!若是同時出現using where,代表索引被用來執行索引鍵值的查找;若是沒有同時出現using where,代表索引用來讀取數據而非執行查找動做。

  覆蓋索引(Covering Index):

    覆蓋索引(Covering Index),一說爲索引覆蓋。
    理解方式一:就是select的數據列只用從索引中就可以取得,沒必要讀取數據行,MySQL能夠利用索引返回select列表中的字段,而沒必要根據索引再次讀取數據文件,換句話說查詢列要被所建的索引覆蓋。

    理解方式二:索引是高效找到行的一個方法,可是通常數據庫也能使用索引找到一個列的數據,所以它沒必要讀取整個行。畢竟索引葉子節點存儲了它們索引的數據;當能經過讀取索引就能夠獲得想要的數據,那就不須要讀取行了。一個索引包含了(或覆蓋了)知足查詢結果的數據就叫作覆蓋索引。

  注意:
    若是要使用覆蓋索引,必定要注意select列表中只取出須要的列,不可select *,由於若是將全部字段一塊兒作索引會致使索引文件過大,查詢性能降低。

  Using where:

    代表使用了where過濾。

  using join buffer:

    使用了鏈接緩存;

  impossible where:

    

    where子句的值老是false,不能用來獲取任何元組;

  select tables optimized away:

    在沒有GROUPBY子句的狀況下,基於索引優化MIN/MAX操做或者對於MyISAM存儲引擎優化COUNT(*)操做,沒必要等到執行階段再進行計算,查詢執行計劃生成的階段即完成優化。

  distinct:

    優化distinct操做,在找到第一匹配的元組後即中止找一樣值的動做。

 

  舉個例子,熱熱身吧!講了那麼多,是否是有些暈了。不要緊,讓咱們經過一個小例子看看。

  

  第一行(執行順序4):id列爲1,表示是union裏的第一個select,select_type列的primary表 示該查詢爲外層查詢,table列被標記爲<derived3>,表示查詢結果來自一個衍生表,其中derived3中3表明該查詢衍生自第三個select查詢,即id爲3的select。【select d1.name......】
  第二行(執行順序2):id爲3,是整個查詢中第三個select的一部分。因查詢包含在from中,因此爲derived。【select id,name from t1 where other_column=''】
  第三行(執行順序3):select列表中的子查詢select_type爲subquery,爲整個查詢中的第二個select。【select id from t3】
  第四行(執行順序1):select_type爲union,說明第四個select是union裏的第二個select,最早執行【select name,id from t2】
  第五行(執行順序5):表明從union的臨時表中讀取行的階段,table列的<union1,4>表示用第一個和第四個select的結果進行union操做。【兩個結果union操做】

 

12. 索引優化

12.1 索引分析

  1. 單表

    建表SQL:  

 1 CREATE TABLE IF NOT EXISTS `article` (  2 `id` INT(10) UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,  3 `author_id` INT(10) UNSIGNED NOT NULL,  4 `category_id` INT(10) UNSIGNED NOT NULL,  5 `views` INT(10) UNSIGNED NOT NULL,  6 `comments` INT(10) UNSIGNED NOT NULL,  7 `title` VARBINARY(255) NOT NULL,  8 `content` TEXT NOT NULL
 9 ); 10 
11 INSERT INTO `article`(`author_id`, `category_id`, `views`, `comments`, `title`, `content`) VALUES
12 (1, 1, 1, 1, '1', '1'), 13 (2, 2, 2, 2, '2', '2'), 14 (1, 1, 3, 3, '3', '3'); 15 
16 SELECT * FROM article;

    案例分析:

 1 #查詢 category_id 爲 1 且 comments 大於 1 的狀況下,views 最多的 article_id。  2 EXPLAIN SELECT id,author_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1;  3 
 4 #結論:很顯然,type 是 ALL,即最壞的狀況。Extra 裏還出現了 Using filesort,也是最壞的狀況。優化是必須的。  5 
 6 
 7 #開始優化:  8 # 1.1 新建索引+刪除索引  9 #ALTER TABLE `article` ADD INDEX idx_article_ccv ( `category_id` , `comments`, `views` ); 10 create index idx_article_ccv on article(category_id,comments,views); 11 DROP INDEX idx_article_ccv ON article 12 
13 
14 # 1.2 第2次EXPLAIN 15 EXPLAIN SELECT id,author_id FROM `article` WHERE category_id = 1 AND comments >1 ORDER BY views DESC LIMIT 1; 16 EXPLAIN SELECT id,author_id FROM `article` WHERE category_id = 1 AND comments =3 ORDER BY views DESC LIMIT 1
17 #結論: 18 #type 變成了 range,這是能夠忍受的。可是 extra 裏使用 Using filesort 還是沒法接受的。 19 #可是咱們已經創建了索引,爲啥沒用呢? 20 #這是由於按照 BTree 索引的工做原理, 21 # 先排序 category_id, 22 # 若是遇到相同的 category_id 則再排序 comments,若是遇到相同的 comments 則再排序 views。 23 #當 comments 字段在聯合索引裏處於中間位置時, 24 #因comments > 1 條件是一個範圍值(所謂 range), 25 #MySQL 沒法利用索引再對後面的 views 部分進行檢索,即 range 類型查詢字段後面的索引無效。 26 
27 
28 # 1.3 刪除第一次創建的索引 29 DROP INDEX idx_article_ccv ON article; 30 
31 # 1.4 第2次新建索引 32 #ALTER TABLE `article` ADD INDEX idx_article_cv ( `category_id` , `views` ) ; 33 create index idx_article_cv on article(category_id,views); 34 
35 # 1.5 第3次EXPLAIN 36 EXPLAIN SELECT id,author_id FROM article WHERE category_id = 1 AND comments > 1 ORDER BY views DESC LIMIT 1; 37 #結論:能夠看到,type 變爲了 ref,Extra 中的 Using filesort 也消失了,結果很是理想。 38 DROP INDEX idx_article_cv ON article;

  2. 兩表

    建表SQL:

 1 CREATE TABLE IF NOT EXISTS `class` (  2 `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,  3 `card` INT(10) UNSIGNED NOT NULL,  4 PRIMARY KEY (`id`)  5 );  6 CREATE TABLE IF NOT EXISTS `book` (  7 `bookid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,  8 `card` INT(10) UNSIGNED NOT NULL,  9 PRIMARY KEY (`bookid`) 10 ); 11 
12 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 13 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 14 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 15 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 16 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 17 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 18 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 19 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 20 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 21 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 22 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 23 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 24 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 25 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 26 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 27 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 28 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 29 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 30 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 31 INSERT INTO class(card) VALUES(FLOOR(1 + (RAND() * 20))); 32 
33 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 34 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 35 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 36 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 37 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 38 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 39 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 40 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 41 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 42 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 43 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 44 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 45 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 46 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 47 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 48 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 49 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 50 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 51 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 52 INSERT INTO book(card) VALUES(FLOOR(1 + (RAND() * 20))); 53 
54 
55  

    案例分析:

 1 # 下面開始explain分析  2 EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card;  3 #結論:type 有All  4 
 5 # 添加索引優化  6 ALTER TABLE `book` ADD INDEX Y ( `card`);  7 
 8 # 第2次explain  9 EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card; 10 #能夠看到第二行的 type 變爲了 ref,rows 也變成了優化比較明顯。 11 #這是由左鏈接特性決定的。LEFT JOIN 條件用於肯定如何從右表搜索行,左邊必定都有, 12 #因此右邊是咱們的關鍵點,必定須要創建索引。 13 
14 # 刪除舊索引 + 新建 + 第3次explain 15 DROP INDEX Y ON book; 16 ALTER TABLE class ADD INDEX X (card); 17 EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card = book.card; 18 
19 
20 # 而後來看一個右鏈接查詢: 21 #優化較明顯。這是由於 RIGHT JOIN 條件用於肯定如何從左表搜索行,右邊必定都有,因此左邊是咱們的關鍵點,必定須要創建索引。 22 EXPLAIN SELECT * FROM class RIGHT JOIN book ON class.card = book.card; 23 DROP INDEX X ON class; 24 ALTER TABLE book ADD INDEX Y (card); 25 # 右鏈接,基本無變化 26 EXPLAIN SELECT * FROM class RIGHT JOIN book ON class.card = book.card; 27  

  3. 三表

    建表SQL:

 1 CREATE TABLE IF NOT EXISTS `phone` (  2 `phoneid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,  3 `card` INT(10) UNSIGNED NOT NULL,  4 PRIMARY KEY (`phoneid`)  5 ) ENGINE = INNODB;  6 
 7 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));  8 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));  9 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 10 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 11 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 12 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 13 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 14 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 15 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 16 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 17 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 18 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 19 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 20 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 21 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 22 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 23 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 24 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 25 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20))); 26 INSERT INTO phone(card) VALUES(FLOOR(1 + (RAND() * 20)));

    案例:

 1 ALTER TABLE `phone` ADD INDEX z ( `card`);  2 
 3 ALTER TABLE `book` ADD INDEX Y ( `card`);#上一個case建過一個一樣的  4 
 5 
 6 EXPLAIN SELECT * FROM class LEFT JOIN book ON class.card=book.card LEFT JOIN phone ON book.card = phone.card;  7 
 8 # 後 2 行的 type 都是 ref 且總 rows 優化很好,效果不錯。所以索引最好設置在須要常常查詢的字段中。  9 ==================================================================================
10 【結論】 11 Join語句的優化 12 
13 儘量減小Join語句中的NestedLoop的循環總次數;「永遠用小結果集驅動大的結果集」。 14 優先優化NestedLoop的內層循環; 15 保證Join語句中被驅動表上Join條件字段已經被索引; 16 當沒法保證被驅動表的Join條件字段被索引且內存資源充足的前提下,不要太吝惜JoinBuffer的設置;

12.2 索引失效(應該避免)

  建表SQL:

 1 CREATE TABLE staffs (  2   id INT PRIMARY KEY AUTO_INCREMENT,  3   NAME VARCHAR (24) NOT NULL DEFAULT '' COMMENT '姓名',  4   age INT NOT NULL DEFAULT 0 COMMENT '年齡',  5   pos VARCHAR (20) NOT NULL DEFAULT '' COMMENT '職位',  6   add_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入職時間'
 7 ) CHARSET utf8 COMMENT '員工記錄表' ;  8 
 9 
10 INSERT INTO staffs(NAME,age,pos,add_time) VALUES('z3',22,'manager',NOW()); 11 INSERT INTO staffs(NAME,age,pos,add_time) VALUES('July',23,'dev',NOW()); 12 
13 SELECT * FROM staffs; 14 
15 ALTER TABLE staffs ADD INDEX idx_staffs_nameAgePos(name, age, pos);

  案例(索引失效):

  1. 全值匹配我最愛

1 EXPLAIN SELECT * FROM staffs WHERE NAME = 'July'; 2 EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age = 25; 3 EXPLAIN SELECT * FROM staffs WHERE NAME = 'July' AND age = 25 AND pos = 'dev';

  2. 最佳左前綴法則

  若是索引了多列,要遵照最左前綴法則。指的是查詢從索引的最左前列開始而且不跳過索引中的列。

1 EXPLAIN SELECT * FROM staffs WHERE age = 25 AND pos = 'dev'; 2 
3 EXPLAIN SELECT * FROM staffs WHERE pos = 'dev';

  

  3. 不在索引列上作任何操做(計算、函數、(自動or手動)類型轉換),會致使索引失效而轉向全表掃描

  

1 EXPLAIN SELECT * FROM staffs WHERE left(NAME,4) = 'July'; 2 
3 索引列上使用了表達式,如where substr(a, 1, 3) = 'hhh'where a = a + 1,表達式是一大忌諱,再簡單mysql也不認。 4 有時數據量不是大到嚴重影響速度時,通常能夠先查出來,好比先查全部有訂單記錄的數據,再在程序中去篩選

  4. 存儲引擎不能使用索引中範圍條件右邊的列

  

  5. 儘可能使用覆蓋索引(只訪問索引的查詢(索引列和查詢列一致)),減小select *

  

  6. mysql 在使用不等於(!= 或者<>)的時候沒法使用索引會致使全表掃描

  

  7. is null ,is not null 也沒法使用索引

  

  8. like以通配符開頭('%abc...')mysql索引失效會變成全表掃描的操做

  

  9. 字符串不加單引號索引失效

  

  10. 少用or,用它來鏈接時會索引失效

  

 

因此咱們來一個小小的總結:

  

  接着咱們以一個例子來深入體會體會:

    SQL以下:

【建表語句】 create table test03( id int primary key not null auto_increment, c1 char(10), c2 char(10), c3 char(10), c4 char(10), c5 char(10) ); insert into test03(c1,c2,c3,c4,c5) values('a1','a2','a3','a4','a5'); insert into test03(c1,c2,c3,c4,c5) values('b1','b2','b3','b4','b5'); insert into test03(c1,c2,c3,c4,c5) values('c1','c2','c3','c4','c5'); insert into test03(c1,c2,c3,c4,c5) values('d1','d2','d3','d4','d5'); insert into test03(c1,c2,c3,c4,c5) values('e1','e2','e3','e4','e5'); select * from test03; 【建索引】 create index idx_test03_c1234 on test03(c1,c2,c3,c4); show index from test03; 問題:咱們建立了複合索引idx_test03_c1234 ,根據如下SQL分析下索引使用狀況? explain select * from test03 where c1='a1'; explain select * from test03 where c1='a1' and c2='a2'; explain select * from test03 where c1='a1' and c2='a2' and c3='a3'; explain select * from test03 where c1='a1' and c2='a2' and c3='a3' and c4='a4'; 1) explain select * from test03 where c1='a1' and c2='a2' and c3='a3' and c4='a4'; 2) explain select * from test03 where c1='a1' and c2='a2' and c4='a4' and c3='a3'; 3) explain select * from test03 where c1='a1' and c2='a2' and c3>'a3' and c4='a4'; 4) explain select * from test03 where c1='a1' and c2='a2' and c4>'a4' and c3='a3'; 5) explain select * from test03 where c1='a1' and c2='a2' and c4='a4' order by c3; c3做用在排序而不是查找 6) explain select * from test03 where c1='a1' and c2='a2' order by c3; 7) explain select * from test03 where c1='a1' and c2='a2' order by c4; 出現了filesort 88.1 explain select * from test03 where c1='a1' and c5='a5' order by c2,c3; 只用c1一個字段索引,可是c二、c3用於排序,無filesort 8.2 explain select * from test03 where c1='a1' and c5='a5' order by c3,c2; 出現了filesort,咱們建的索引是1234,它沒有按照順序來,3 2 顛倒了 9) explain select * from test03 where c1='a1' and c2='a2' order by c2,c3; 10) explain select * from test03 where c1='a1' and c2='a2' and c5='a5' order by c2,c3; 用c一、c2兩個字段索引,可是c二、c3用於排序,無filesort explain select * from test03 where c1='a1' and c2='a2' and c5='a5' order by c3,c2; 本例有常量c2的狀況,和8.2對比 explain select * from test03 where c1='a1' and c5='a5' order by c3,c2; filesort 11) explain select * from test03 where c1='a1' and c4='a4' group by c2,c3; 12) explain select * from test03 where c1='a1' and c4='a4' group by c3,c2;    Using where; Using temporary; Using filesort 

  咱們不可貴出:

    定值、範圍仍是排序,通常order by是給個範圍,group by 基本上都須要進行排序,會有臨時表產生。

12.3 Show Profile

  是什麼:是mysql提供能夠用來分析當前會話中語句執行的資源消耗狀況。能夠用於SQL的調優的測量。默認狀況下,參數處於關閉狀態,並保存最近15次的運行結果。

  分析步驟;

    1. 是否支持,看看當前的mysql版本是否支持 

      Show variables like 'profiling';  //默認是關閉,使用前須要開啓

    

    2. 開啓功能,默認是關閉,使用前須要開啓

      

    3. 運行SQL 

    4. 查看結果,show profiles;

      

    5. 診斷SQL,show profile cpu,block io for query 上一步前面的問題SQL數字號碼;

      

  6. 平常開發須要注意的地方總結(若有遺漏,還望同行不吝賜教)

     converting HEAP to MyISAM 查詢結果太大,內存都不夠用了往磁盤上搬了。

     create tmp table 建立臨時表,這個要注意。

     Copying to tmp table on disk   把內存臨時表複製到磁盤。

     locked 是否上鎖,這個也要注意。

12.4 索引優化的須要注意的一些

  對於單鍵索引,儘可能選擇針對當前query過濾性更好的索引;

  在選擇組合索引的時候,當前Query中過濾性最好的字段在索引字段順序中,位置越靠前越好。

  在選擇組合索引的時候,儘可能選擇能夠可以包含當前query中的where字句中更多字段的索引。

  儘量經過分析統計信息和調整query的寫法來達到選擇合適索引的目的。

  少用Hint強制索引。

13. 查詢優化

13.1 永遠小表驅動大表,相似嵌套循環Nested Loop

   

13.2 order by關鍵字優化

  1. ORDER BY子句,儘可能使用Index方式排序,避免使用FileSort方式排序 

    建表SQL: 

 1 CREATE TABLE tblA(  2   #id int primary key not null auto_increment,  3   age INT,  4   birth TIMESTAMP NOT NULL
 5 );  6 
 7 INSERT INTO tblA(age,birth) VALUES(22,NOW());  8 INSERT INTO tblA(age,birth) VALUES(23,NOW());  9 INSERT INTO tblA(age,birth) VALUES(24,NOW()); 10 
11 CREATE INDEX idx_A_ageBirth ON tblA(age,birth); 12 
13 SELECT * FROM tblA; 

    案例:

  

  

  MySQL支持二種方式的排序,FileSort和Index,Index效率高。它指MySQL掃描索引自己完成排序。FileSort方式效率較低。

   ORDER BY知足兩狀況,會使用Index方式排序:

    ORDER BY 語句使用索引最左前列;使用Where子句與Order BY子句條件列組合知足索引最左前列

  2. 儘量在索引列上完成排序操做,遵守索引建的最佳左前綴

  3. 若是不在索引列上,filesort有兩種算法:mysql就要啓動雙路排序和單路排序

  雙路排序:MySQL 4.1以前是使用雙路排序,字面意思就是兩次掃描磁盤,最終獲得數據,讀取行指針和orderby列,對他們進行排序,而後掃描已經排序好的列表,按照列表中的值從新從列表中讀取對應的數據輸出。從磁盤取排序字段,在buffer進行排序,再從磁盤取其餘字段。

  取一批數據,要對磁盤進行了兩次掃描,衆所周知,I\O是很耗時的,因此在mysql4.1以後,出現了第二種改進的算法,就是單路排序。

  單路排序:從磁盤讀取查詢須要的全部列,按照order by列在buffer對它們進行排序,而後掃描排序後的列表進行輸出,它的效率更快一些,避免了第二次讀取數據。而且把隨機IO變成了順序IO,可是它會使用更多的空間,由於它把每一行都保存在內存中了。

  4. 結論及引伸出的問題

  因爲單路是後出的,整體而言好過雙路;可是用單路有問題(在sort_buffer中,方法B比方法A要多佔用不少空間,由於方法B是把全部字段都取出, 因此有可能取出的數據的總大小超出了sort_buffer的容量,致使每次只能取sort_buffer容量大小的數據,進行排

序(建立tmp文件,多路合併),排完再取取sort_buffer容量大小,再排……從而屢次I/O。原本想省一次I/O操做,反而致使了大量的I/O操做,反而得不償失。)

  5. 單路有問題,因此咱們要用一些優化策略

    增大max_length_for_sort_data參數的設置;

    增大sort_buffer_size參數的設置; 

提升Order By的速度

1. Order by時select * 是一個大忌只Query須要的字段, 這點很是重要。在這裏的影響是:
  1.1 當Query的字段大小總和小於max_length_for_sort_data 並且排序字段不是 TEXT|BLOB 類型時,會用改進後的算法——單路排序, 不然用老算法——多路排序。
  1.2 兩種算法的數據都有可能超出sort_buffer的容量,超出以後,會建立tmp文件進行合併排序,致使屢次I/O,可是用單路排序算法的風險會更大一些,因此要提升sort_buffer_size。

2. 嘗試提升 sort_buffer_size
無論用哪一種算法,提升這個參數都會提升效率,固然,要根據系統的能力去提升,由於這個參數是針對每一個進程的

3. 嘗試提升 max_length_for_sort_data
提升這個參數, 會增長用改進算法的機率。可是若是設的過高,數據總容量超出sort_buffer_size的機率就增大,明顯症狀是高的磁盤I/O活動和低的處理器使用率. 

  6. 總結

13.3 GROUP BY關鍵字優化

  1. group by實質是先排序後進行分組,遵守索引建的最佳左前綴

  2. 當沒法使用索引列,增大max_length_for_sort_data參數的設置+增大sort_buffer_size參數的設置

  3. where高於having,能寫在where限定的條件就不要去having限定了。

                    /***一個高級的J2E工程師須要面對MySQL要有那些基本功夫呢<上>***/ 

   一個高級的J2E工程師須要面對MySQL要有那些基本功夫呢?這個系列第一篇就到此結束了,第一篇對mysql的架構、mysql性能因素進行介紹,並對查詢與索引優化進行了分析。再接下來的中、下兩篇中,我再會爲介紹大數據量處理理論、分區分庫分表、MySql鎖機制和主從複製等。不要急,敬請期待下一篇博文!

                    /***一個高級的J2E工程師須要面對MySQL要有那些基本功夫呢<上>***/ 

 

本文爲博主原創文章,轉載請註明出處!

http://www.cnblogs.com/libingbin/

感謝您的閱讀

相關文章
相關標籤/搜索