1. 程式人生 > >sql性能優化(摘自網絡)

sql性能優化(摘自網絡)

而不是 頻繁 距離 ssl 數據庫名 協議 時間 就會 算術

索引,索引!!!為經常查詢的字段建索引!!

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

優化目標

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 [email protected]; // 顯示一個用戶的權限,顯示結果類似於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

技術分享圖片

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

對連續的數值盡量用between

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

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

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

技術分享圖片

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

會導致引擎放棄使用索引

技術分享圖片

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

技術分享圖片

不要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

摘自: http://www.cnblogs.com/lddbupt/

sql性能優化(摘自網絡)