sql性能優化

索引,索引!!!爲常常查詢的字段建索引!!mysql

但也不能過多地建索引。insert和delete等改變表記錄的操做會致使索引重排,增長數據庫負擔。sql

優化目標

1.減小 IO 次數

IO永遠是數據庫最容易瓶頸的地方,這是由數據庫的職責所決定的,大部分數據庫操做中超過90%的時間都是 IO 操做所佔用的,減小 IO 次數是 SQL 優化中須要第一優先考慮,固然,也是收效最明顯的優化手段。 數據庫

2.下降 CPU 計算

除了 IO 瓶頸以外,SQL優化中須要考慮的就是 CPU 運算量的優化了。order by, group by,distinct … 都是消耗 CPU 的大戶(這些操做基本上都是 CPU 處理內存中的數據比較運算)。當咱們的 IO 優化作到必定階段以後,下降 CPU 計算也就成爲了咱們 SQL 優化的重要目標 緩存

優化方法

改變 SQL 執行計劃

明確了優化目標以後,咱們須要肯定達到咱們目標的方法。對於 SQL 語句來講,達到上述2個目標的方法其實只有一個,那就是改變 SQL 的執行計劃,讓他儘可能「少走彎路」,儘可能經過各類「捷徑」來找到咱們須要的數據,以達到 「減小 IO 次數」 和 「下降 CPU 計算」 的目標 服務器

分析複雜的SQL語句

explain

例如:
mysql> explain select * from (select * from ( select * from t3 where id=3952602) a) b;
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
| id | select_type | table      | type   | possible_keys     | key     | key_len | ref  | rows | Extra |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
|  1 | PRIMARY     | <derived2> | system | NULL              | NULL    | NULL    | NULL |    1 |       |
|  2 | DERIVED     | <derived3> | system | NULL              | NULL    | NULL    | NULL |    1 |       |
|  3 | DERIVED     | t3         | const  | PRIMARY,idx_t3_id | PRIMARY | 4       |      |    1 |       |
+----+-------------+------------+--------+-------------------+---------+---------+------+------+-------+
很顯然這條SQL是從裏向外的執行,就是從id=3 向上執行. 架構

show

show tables或show tables from database_name; // 顯示當前數據庫中全部表的名稱 併發

show databases; // 顯示mysql中全部數據庫的名稱 函數

show columns from table_name from database_name; 或MySQL show columns from database_name.table_name; // 顯示錶中列名稱 高併發

show grants for user_name@localhost; // 顯示一個用戶的權限,顯示結果相似於grant 命令 性能

show index from table_name; // 顯示錶的索引

show status; // 顯示一些系統特定資源的信息,例如,正在運行的線程數量

show variables; // 顯示系統變量的名稱和值
show processlist; // 顯示系統中正在運行的全部進程,也就是當前正在執行的查詢。

show table status; // 顯示當前使用或者指定的database中的每一個表的信息。信息包括表類型和表的最新更新時間

show privileges; // 顯示服務器所支持的不一樣權限

show create database database_name; // 顯示create database 語句是否可以建立指定的數據庫

show create table table_name; // 顯示create database 語句是否可以建立指定的數據庫

show engies; // 顯示安裝之後可用的存儲引擎和默認引擎。

show innodb status; // 顯示innoDB存儲引擎的狀態

show logs; // 顯示BDB存儲引擎的日誌

show warnings; // 顯示最後一個執行的語句所產生的錯誤、警告和通知

show errors; // 只顯示最後一個執行語句所產生的錯誤

關於enum

存在爭議。

對於取值有限且固定的字段,推薦使用enum而非varchar。可是!!其餘數據庫可能不支持,致使了難於遷移的問題。

開啓緩存查詢

對於徹底相同的sql,使用已經存在的執行計劃,從而跳過解析和生成執行計劃的過程。

應用場景:有一個不常常變動的表,且服務器收到該表的大量相同查詢。對於頻繁更新的表,查詢緩存是不適合的

Mysql 判斷是否命中緩存的辦法很簡單,首先會將要緩存的結果放在引用表中,而後使用查詢語句,數據庫名稱,客戶端協議的版本等因素算出一個hash值,這個hash值與引用表中的結果相關聯。若是在執行查詢時,根據一些相關的條件算出的hash值能與引用表中的數據相關聯,則表示查詢命中

查詢必須是徹底相同的(逐字節相同)纔可以被認爲是相同的。另外,一樣的查詢字符串因爲其它緣由可能認爲是不一樣的。使用不一樣的數據庫、不一樣的協議版本或者不一樣 默認字符集的查詢被認爲是不一樣的查詢而且分別進行緩存。

下面sql查詢緩存認爲是不一樣的:

 
 
 
 
 
  1. SELECT * FROM tbl_name 
  2. Select * from tbl_name 

緩存機制失效的場景

若是查詢語句中包含一些不肯定因素時(例如包含 函數Current()),該查詢不會被緩存,不肯定因素主要包含如下狀況

·        引用了一些返回值不肯定的函數

·        引用自定義函數(UDFs)。

·        引用自定義變量。

·        引用mysql系統數據庫中的表。

·        下面方式中的任何一種:

SELECT ...IN SHARE MODE

SELECT ...FOR UPDATE

SELECT ...INTO OUTFILE ...

SELECT ...INTO DUMPFILE ...

SELECT * FROM ...WHERE autoincrement_col IS NULL

·        使用TEMPORARY表。

·        不使用任何表。

·        用戶有某個表的列級別權限。

額外的消耗

若是使用查詢緩存,在進行讀寫操做時會帶來額外的資源消耗,消耗主要體如今如下幾個方面

·        查詢的時候會檢查是否命中緩存,這個消耗相對較小

·        若是沒有命中查詢緩存,MYSQL會判斷該查詢是否能夠被緩存,並且系統中尚未對應的緩存,則會將其結果寫入查詢緩存

·        若是一個表被更改了,那麼使用那個表的全部緩衝查詢將再也不有效,而且從緩衝區中移出。這包括那些映射到改變了的表的使用MERGE表的查詢。一個表能夠被許多類型的語句更改,例如INSERT、UPDATE、DELETE、TRUNCATE、ALTER TABLE、DROP TABLE或DROP DATABASE。

對於InnoDB而言,事物的一些特性還會限制查詢緩存的使用。當在事物A中修改了B表時,由於在事物提交以前,對B表的修改對其餘的事物而言是不可見的。爲了保證緩存結果的正確性,InnoDB採起的措施讓全部涉及到該B表的查詢在事物A提交以前是不可緩存的。若是A事物長時間運行,會嚴重影響查詢緩存的命中率

查詢緩存的空間不要設置的太大。

由於查詢緩存是靠一個全局鎖操做保護的,若是查詢緩存配置的內存比較大且裏面存放了大量的查詢結果,當查詢緩存失效的時候,會長時間的持有這個全局鎖。由於查詢緩存的命中檢測操做以及緩存失效檢測也都依賴這個全局鎖,因此可能會致使系統僵死的狀況

靜態錶速度更快

定長類型和變長類型

CHAR(M)定義的列的長度爲固定的,M取值能夠爲0~255之間,當保存CHAR值時,在它們的右邊填充空格以達到指定的長度。當檢索到CHAR值時,尾部的空格被刪除掉。在存儲或檢索過程當中不進行大小寫轉換。CHAR存儲定長數據很方便,CHAR字段上的索引效率級高,好比定義char(10),那麼不論你存儲的數據是否達到了10個字節,都要佔去10個字節的空間,不足的自動用空格填充。

VARCHAR(M)定義的列的長度爲可變長字符串,M取值能夠爲0~65535之間,(VARCHAR的最大有效長度由最大行大小和使用的字符集肯定。總體最大長度是65,532字節)。VARCHAR值保存時只保存須要的字符數,另加一個字節來記錄長度(若是列聲明的長度超過255,則使用兩個字節)。VARCHAR值保存時不進行填充。當值保存和檢索時尾部的空格仍保留,符合標準SQL。varchar存儲變長數據,但存儲效率沒有CHAR高。

若是一個字段可能的值是不固定長度的,咱們只知道它不可能超過10個字符,把它定義爲 VARCHAR(10)是最合算的。VARCHAR類型的實際長度是它的值的實際長度+1。空間上考慮,用varchar合適;從效率上考慮,用char合適,關鍵是根據實際狀況找到權衡點。

VARCHAR和TEXT、BlOB類型

VARCHAR,BLOB和TEXT類型是變長類型,對於其存儲需求取決於列值的實際長度(在前面的表格中用L表示),而不是取決於類型的最大可能尺寸。

BLOB和TEXT類型須要1,2,3或4個字節來記錄列值的長度,這取決於類型的最大可能長度。VARCHAR須要定義大小,有65535字節的最大限制;TEXT則不須要。若是你把一個超過列類型最大長度的值賦給一個BLOB或TEXT列,值被截斷以適合它。

一個BLOB是一個能保存可變數量的數據的二進制的大對象。4個BLOB類型TINYBLOB、BLOB、MEDIUMBLOB和LONGBLOB僅僅在他們能保存值的最大長度方面有所不一樣。

BLOB 能夠儲存圖片,TEXT不行,TEXT只能儲存純文本文件。

在BLOB和TEXT類型之間的惟一差異是對BLOB值的排序和比較以大小寫敏感方式執行,而對TEXT值是大小寫不敏感的。換句話說,一個TEXT是一個大小寫不敏感的BLOB。

效率來講基本是char>varchar>text,可是若是使用的是Innodb引擎的話,推薦使用varchar代替char

char和varchar能夠有默認值,text不能指定默認值

靜態表和動態表

靜態表字段長度固定,自動填充,讀寫速度很快,便於緩存和修復,但比較佔硬盤,動態表是字段長度不固定,節省硬盤,但更復雜,容易產生碎片,速度慢,出問題後不容易重建。

當只須要一條數據的時候,使用limit 1

 

表記錄中的一行儘可能不要超過一個IO單元

 

區分in和exist

select * from 表A where id in (select id from 表B)
這句至關於
select * from 表A where exists(select * from 表B where 表B.id=表A.id)
對於表A的每一條數據,都執行select * from 表B where 表B.id=表A.id的存在性判斷,若是表B中存在表A當前行相同的id,則exists爲真,該行顯示,不然不顯示

區分in和exists主要是形成了驅動順序的改變(這是性能變化的關鍵),若是是exists,那麼之外層表爲驅動表,先被訪問,若是是IN,那麼先執行子查詢

因此IN適合於外表大而內表小的狀況;EXISTS適合於外表小而內表大的狀況

複雜多表儘可能少用join

MySQL 的優點在於簡單,但這在某些方面其實也是其劣勢。MySQL 優化器效率高,可是因爲其統計信息的量有限,優化器工做過程出現誤差的可能性也就更多。對於複雜的多表 Join,一方面因爲其優化器受限,再者在 Join 這方面所下的功夫還不夠,因此性能表現離 Oracle 等關係型數據庫前輩仍是有必定距離。但若是是簡單的單表查詢,這一差距就會極小甚至在有些場景下要優於這些數據庫前輩。

儘可能用join代替子查詢

雖然 Join 性能並不佳,可是和 MySQL 的子查詢比起來仍是有很是大的性能優點。

MySQL須要爲內層查詢語句的查詢結果創建一個臨時表。而後外層查詢語句在臨時表中查詢記錄。查詢完畢後,MySQL須要插銷這些臨時表。因此在MySQL中可使用鏈接查詢來代替子查詢。鏈接查詢不須要創建臨時表,其速度比子查詢要快。

儘可能少排序

排序操做會消耗較多的 CPU 資源,因此減小排序能夠在緩存命中率高等 IO 能力足夠的場景下會較大影響 SQL 的響應時間。

對於MySQL來講,減小排序有多種辦法,好比:

上面誤區中提到的經過利用索引來排序的方式進行優化

減小參與排序的記錄條數

非必要不對數據進行排序

儘可能避免select *

大多數關係型數據庫都是按照行(row)的方式存儲,而數據存取操做都是以一個固定大小的IO單元(被稱做 block 或者 page)爲單位,通常爲4KB,8KB… 大多數時候,每一個IO單元中存儲了多行,每行都是存儲了該行的全部字段(lob等特殊類型字段除外)。

因此,咱們是取一個字段仍是多個字段,實際上數據庫在表中須要訪問的數據量實際上是同樣的。

也有例外狀況,那就是咱們的這個查詢在索引中就能夠完成,也就是說當只取 a,b兩個字段的時候,不須要回表,而c這個字段不在使用的索引中,須要回表取得其數據。在這樣的狀況下,兩者的IO量會有較大差別。

儘可能少or

當 where 子句中存在多個條件以「或」並存的時候,MySQL 的優化器並無很好的解決其執行計劃優化問題,再加上 MySQL 特有的 SQL 與 Storage 分層架構方式,形成了其性能比較低下,不少時候使用 union all 或者是union(必要的時候)的方式來代替「or」會獲得更好的效果。

儘可能用 union all 代替 union

union 和 union all 的差別主要是前者須要將兩個(或者多個)結果集合並後再進行惟一性過濾操做,這就會涉及到排序,增長大量的 CPU 運算,加大資源消耗及延遲。因此當咱們能夠確認不可能出現重複結果集或者不在意重複結果集的時候,儘可能使用 union all 而不是 union。

儘可能早過濾

在 SQL 編寫中一樣可使用這一原則來優化一些 Join 的 SQL。好比咱們在多個表進行分頁數據查詢的時候,咱們最好是可以在一個表上先過濾好數據分好頁,而後再用分好頁的結果集與另外的表 Join,這樣能夠儘量多的減小沒必要要的 IO 操做,大大節省 IO 操做所消耗的時間。

避免類型轉換

這裏所說的「類型轉換」是指 where 子句中出現 column 字段的類型和傳入的參數類型不一致的時候發生的類型轉換:

人爲在column_name 上經過轉換函數進行轉換直接致使 MySQL(實際上其餘數據庫也會有一樣的問題)沒法使用索引,若是非要轉換,應該在傳入的參數上進行轉換,由數據庫本身進行轉換,

若是咱們傳入的數據類型和字段類型不一致,同時咱們又沒有作任何類型轉換處理,MySQL 可能會本身對咱們的數據進行類型轉換操做,也可能不進行處理而交由存儲引擎去處理,這樣一來,就會出現索引沒法使用的狀況而形成執行計劃問題。

優先優化高併發的 SQL,而不是執行頻率低某些「大」SQL

對於破壞性來講,高併發的 SQL 老是會比低頻率的來得大,由於高併發的 SQL 一旦出現問題,甚至不會給咱們任何喘息的機會就會將系統壓跨。而對於一些雖然須要消耗大量 IO 並且響應很慢的 SQL,因爲頻率低,即便遇到,最多就是讓整個系統響應慢一點,但至少可能撐一下子,讓咱們有緩衝的機會。

從全局出發優化,而不是片面調整

尤爲是在經過調整索引優化 SQL 的執行計劃的時候,千萬不能顧此失彼,因小失大。

儘量對每一條運行在數據庫中的SQL進行 explain

知道 SQL 的執行計劃才能判斷是否有優化餘地,才能判斷是否存在執行計劃問題。在對數據庫中運行的 SQL 進行了一段時間的優化以後,很明顯的問題 SQL 可能已經不多了,大多都須要去發掘,這時候就須要進行大量的 explain 操做收集執行計劃,並判斷是否須要進行優化。

儘可能避免where子句中對字段進行null值的判斷

會致使引擎放棄索引,進而進行全表掃描。

儘可能不要給數據庫留null值,儘量地使用not null填充數據庫。能夠爲每一個null型的字段設置一個和null對應的實際內容表述。

避免在where中使用!=, >, <操做符

不然引擎放棄使用索引,進行全表掃描。

經常使用查詢字段建索引

避免在where中使用or

image

in和not in關鍵詞慎用,容易致使全表掃面

對連續的數值儘可能用between

通配符查詢也容易致使全表掃描

避免在where子句中使用局部變量

sql只有在運行時才解析局部變量。而優化程序必須在編譯時訪問執行計劃,這時並不知道變量值,因此沒法做爲索引的輸入項。

image

避免在where子句中對字段進行表達式操做

會致使引擎放棄使用索引

image

避免在where子句中對字段進行函數操做

image

不要where子句的‘=’左邊進行函數、算術運算或其餘表達式運算

系統可能沒法正確使用索引

避免update所有字段

只update須要的字段。頻繁調用會引發明顯的性能消耗,同時帶來大量日誌。

索引不是越多越好

一個表的索引數最好不要超過6個

儘可能使用數字型字段而非字符型

由於處理查詢和鏈接時會逐個比較字符串的每一個字符,而對於數字型而言只須要比較一次就夠了。

儘量用varchar/nvarchar代替char/nchar

變長字段存儲空間小,對於查詢來講,在一個相對較小的字段內搜索效率更高。。。?

避免頻繁建立和刪除臨時表,減小系統表資源消耗

select into和create table

新建臨時表時,若是一次性插入數據量很大,使用select into代替create table,避免形成大量log,以提升速度。

若是數據量不大,爲了緩和系統表的資源,先create table,再insert。

拆分大的DELETE和INSERT語句

由於這兩個操做是會鎖表的,對於高訪問量的站點來講,鎖表時間內積累的訪問數、數據庫鏈接、打開的文件數等等,可能不只僅讓WEB服務崩潰,還會讓整臺服務器立刻掛了。

因此,必定要拆分,使用LIMIT條件休眠一段時間,批量處理。

 

 

 

 

參考資料

http://blog.csdn.net/eric_sunah/article/details/17510939

http://ju.outofmemory.cn/entry/89569

相關文章
相關標籤/搜索