SQL 優化——一般步驟、索引問題、優化方法(ANALYZE、CHECK、OPTIMIZE)、常用 SQL 的優化

一、優化 SQL 語句的一般步驟

1 通過 show status 命令瞭解各種 SQL 的執行頻率

  MySQL 客戶端連接成功後,通過【 show [session|global] status 】命令可以提供服務器狀態信息,也可以在操作系統上使用 mysqladmin extended-status 命令獲得這些消息。show [session|global] status 可以根據需要加上參數“session”或者“global”來顯示 session 級(當前連接)的統計結果和 global 級(自數據庫上次啓動至今)的統計結果。如果不寫,默認使用參數是“session”。

Com_xxx 表示每個 xxx 語句執行的次數,我們通常比較關心的是以下幾個統計參數。

  • Com_select:執行 select 操作的次數,一次查詢只累加 1。
  • Com_insert:執行 INSERT 操作的次數,對於批量插入的 INSERT 操作,只累加一次。
  • Com_update:執行 UPDATE 操作的次數。
  • Com_delete:執行 DELETE 操作的次數。

 

上面這些參數對於所有存儲引擎的表操作都會進行累計。

下面這幾個參數只是針對InnoDB 存儲引擎的,累加的算法也略有不同。

  • Innodb_rows_read:select 查詢返回的行數。
  • Innodb_rows_inserted:執行 INSERT 操作插入的行數。
  • Innodb_rows_updated:執行 UPDATE 操作更新的行數。
  • Innodb_rows_deleted:執行 DELETE 操作刪除的行數。

  通過以上幾個參數,可以很容易地瞭解當前數據庫的應用是以插入更新爲主還是以查詢操作爲主,以及各種類型的 SQL 大致的執行比例是多少。對於更新操作的計數,是對執行次數的計數,不論提交還是回滾都會進行累加。

  對於事務型的應用,通過 Com_commit 和 Com_rollback 可以瞭解事務提交和回滾的情況,對於回滾操作非常頻繁的數據庫,可能意味着應用編寫存在問題。

此外,以下幾個參數便於用戶瞭解數據庫的基本情況。

  •  Connections:試圖連接 MySQL 服務器的次數。
  •  Uptime:服務器工作時間。
  •  Slow_queries:慢查詢的次數。

 

2 定位執行效率較低的 SQL 語句

可以通過以下兩種方式定位執行效率較低的 SQL 語句。

  •  通過慢查詢日誌定位那些執行效率較低的 SQL 語句,用--log-slow-queries[=file_name]選項啓動時,mysqld 寫一個包含所有執行時間超過 long_query_time 秒的 SQL 語句的日誌文件。具體可以查看本書第 26 章中日誌管理的相關部分。
  • 慢查詢日誌在查詢結束以後才紀錄,所以在應用反映執行效率出現問題的時候查詢慢查詢日誌並不能定位問題,可以使用 show processlist 命令查看當前 MySQL 在進行的線程,包括線程的狀態、是否鎖表等,可以實時地查看 SQL 的執行情況,同時對一些鎖表操作進行優化。

 

3 通過 EXPLAIN 分析低效 SQL 的執行計劃

  通過以上步驟查詢到效率低的 SQL 語句後,可以通過 EXPLAIN 或者 DESC 命令獲取 MySQL如何執行 SELECT 語句的信息,包括在 SELECT 語句執行過程中表如何連接和連接的順序,比如想計算 2006 年所有公司的銷售額,需要關聯 sales 表和 company 表,並且對 moneys 字段做求和(sum)操作,相應 SQL 的執行計劃如下:

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

mysql> explain select sum(moneys) from sales a,company b where a.company_id = b.id and a.year= 2006\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: a

type: ALL

possible_keys: NULL

keyNULL

key_len: NULL

ref: NULL

rows: 1000

Extra: Using where

*************************** 2. row ***************************

id: 1

select_type: SIMPLE

table: b

type: ref

possible_keys: ind_company_id

key: ind_company_id

key_len: 5

ref: sakila.a.company_id

rows: 1

Extra: Using where; Using index

rows in set (0.00 sec)

每個列的簡單解釋如下:

  •  select_type:表示 SELECT 的類型,常見的取值有 SIMPLE(簡單表,即不使用表連接或者子查詢)、PRIMARY(主查詢,即外層的查詢)、UNION(UNION 中的第二個或者後面的查詢語句)、SUBQUERY(子查詢中的第一個 SELECT)等。
  •  table:輸出結果集的表。
  •  type:表示表的連接類型,性能由好到差的連接類型爲
    • system(表中僅有一行,即常量表)
    • const(單表中最多有一個匹配行,例如 primary key 或者 unique index)
    • eq_ref(對於前面的每一行,在此表中只查詢一條記錄,簡單來說,就是多表連接中使用 primary key 或者 unique index)
    • ref(與 eq_ref 類似,區別在於不是使用 primary key 或者 unique index,而是使用普通的索引)
    • ref_or_null(與 ref 類似,區別在於條件中包含對 NULL 的查詢)
    • index_merge(索引合併優化)
    • unique_subquery(in的後面是一個查詢主鍵字段的子查詢)
    • index_subquery (與 unique_subquery 類似,區別在於 in 的後面是查詢非唯一索引字段的子查詢)
    • range (單表中的範圍查詢)
    • index (對於前面的每一行,都通過查詢索引來得到數據)
    • all (對於前面的每一行,都通過全表掃描來得到數據)
  • possible_keys:表示查詢時,可能使用的索引。
  • key:表示實際使用的索引。
  • key_len:索引字段的長度。
  • rows:掃描行的數量。
  • Extra:執行情況的說明和描述。

 

 

4 確定問題並採取相應的優化措施

經過以上步驟,基本就可以確認問題出現的原因。此時用戶可以根據情況採取相應的措施,進行優化提高執行的效率。在上面的例子中,已經可以確認是對 a 表的全表掃描導致效率的不理想,那麼對 a 表的year 字段創建索引:

1

mysql> create index ind_sales2_year on sales2(year);

創建索引後,再看一下這條語句的執行計劃:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

mysql> explain select sum(moneys) from sales2 a,company2 b where a.company_id = b.id and a.year = 2006\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: a

type: ref

possible_keys: ind_sales2_year

key: ind_sales2_year

key_len: 2

ref: const

rows: 1

Extra: Using where

*************************** 2. row ***************************

id: 1

select_type: SIMPLE

table: b

type: ref

possible_keys: ind_company2_id

key: ind_company2_id

key_len: 5

ref: sakila.a.company_id

rows: 1

Extra: Using where; Using index

rows in set (0.00 sec)

 

可以發現建立索引後對 a 表需要掃描的行數明顯減少(從 1000 行減少到 1 行),可見索引的使用可以大大提高數據庫的訪問速度,尤其在表很龐大的時候這種優勢更爲明顯。

 

二、索引問題

索引是數據庫優化中最常用也是最重要的手段之一,通過索引通常可以幫助用戶解決大多數的 SQL 性能問題。

1 索引的存儲分類

  MyISAM 存儲引擎的表的數據和索引是自動分開存儲的,各自是獨立的一個文件; InnoDB存儲引擎的表的數據和索引是存儲在同一個表空間裏面,但可以有多個文件組成。

  MySQL 中索引的存儲類型目前只有兩種(BTREE 和 HASH),具體和表的存儲引擎相關:MyISAM 和 InnoDB 存儲引擎都只支持 BTREE 索引;MEMORY/HEAP 存儲引擎可以支持 HASH和 BTREE 索引。

  MySQL 目前不支持函數索引,但是能對列的前面某一部分進索引(前綴索引),例如 name 字段,可以只取 name 的前 4 個字符進行索引,這個特性可以大大縮小索引文件的大小,用戶在設計表結構的時候也可以對文本列根據此特性進行靈活設計。下面是創建前綴索引的一個例子:

1

mysql> create index ind_company2_name on company2(name(4));

 

2 MySQL 如何使用索引

  索引用於快速找出在某個列中有一特定值的行。對相關列使用索引是提高 SELECT 操作性能的最佳途徑。
  查詢要使用索引最主要的條件是查詢條件中需要使用索引關鍵字,如果是多列索引,那麼只有查詢條件使用了多列關鍵字最左邊的前綴時,纔可以使用索引,否則將不能使用索引。

 

1.使用索引

在 MySQL 中,下列幾種情況下有可能使用到索引:
(1)對於創建的多列索引,只要查詢的條件中用到了最左邊的列,索引一般就會被使用

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

#首先按 company_id,moneys 的順序創建一個複合索引,具體如下:

mysql> create index ind_sales2_companyid_moneys on sales2(company_id,moneys);

Query OK, 1000 rows affected (0.03 sec)

Records: 1000 Duplicates: 0 Warnings: 0

 

#然後按 company_id 進行表查詢,具體如下:

mysql> explain select from sales2 where company_id = 2006\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: sales2

type: ref

possible_keys: ind_sales2_companyid_moneys

208key: ind_sales2_companyid_moneys

key_len: 5

ref: const

rows: 1

Extra: Using where

1 row in set (0.00 sec)

 

#可以發現即便 where 條件中不是用的 company_id 與 moneys 的組合條件,索引仍然能用到,這就是索引的前綴特性。#但是如果只按 moneys 條件查詢表,那麼索引就不會被用到,具體如下:

mysql> explain select from sales2 where moneys = 1\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: sales2

type: ALL

possible_keys: NULL

keyNULL

key_len: NULL

ref: NULL

rows: 1000

Extra: Using where

1 row in set (0.00 sec)

 

(2)對於使用 like 的查詢,後面如果是常量並且只有%號不在第一個字符,索引纔可能會被使用:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

mysql> explain select from company2 where name like '%3'\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: company2

type: ALL

possible_keys: NULL

keyNULL

key_len: NULL

ref: NULL

rows: 1000

Extra: Using where

1 row in set (0.00 sec)

 

 

mysql> explain select from company2 where name like '3%'\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: company2

type: range

209possible_keys: ind_company2_name

key: ind_company2_name

key_len: 11

ref: NULL

rows: 103

Extra: Using where

1 row in set (0.00 sec)

#可以發現第一個例子沒有使用索引,而第二例子就能夠使用索引,

#區別就在於“%”的位置不同,前者把“%”放到第一位就不能用到索引,而後者沒有放到第一位就使用了索引。

#另外,如果如果 like 後面跟的是一個列的名字,那麼索引也不會被使用。

 

(3)如果對大的文本進行搜索,使用全文索引而不用使用 like ‘%...%’。

 

(4)如果列名是索引,使用 column_name is null 將使用索引。

1

2

3

4

5

6

7

8

9

10

11

12

13

mysql> explain select from company2 where name is null\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: company2

type: ref

possible_keys: ind_company2_name

key: ind_company2_name

key_len: 11

ref: const

rows: 1

Extra: Using where

1 row in set (0.00 sec)

 

2.存在索引但不使用索引

在下列情況下,雖然存在索引,但是 MySQL 並不會使用相應的索引。
(1)如果 MySQL 估計使用索引比全表掃描更慢,則不使用索引。例如,如果列key_part1 均勻分佈在 1 和 100 之間,下列查詢中使用索引就不是很好:

1

SELECT FROM table_name where key_part1 > 1 and key_part1 < 90;

 

(2)如果使用 MEMORY/HEAP 表並且 where 條件中不使用“=”進行索引列,那麼不會用到索引。heap 表只有在“=”的條件下才會使用索引。


(3)用 or 分割開的條件,如果 or 前的條件中的列有索引,而後面的列中沒有索引,那麼涉及到的索引都不會被用到,例如:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

mysql> show index from sales\G;

*************************** 1. row ***************************

Table: sales

Non_unique: 1

Key_name: ind_sales_year

Seq_in_index: 1

Column_name: year

210Collation: A

Cardinality: NULL

Sub_part: NULL

Packed: NULL

Null:

Index_type: BTREE

Comment:

1 row in set (0.00 sec)

 

從上面可以發現只有 year 列上面有索引,來看如下的執行計劃:

1

2

3

4

5

6

7

8

9

10

11

12

13

mysql> explain select from sales where year = 2001 or country = 'China'\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: sales

type: ALL

possible_keys: ind_sales_year

keyNULL

key_len: NULL

ref: NULL

rows: 12

Extra: Using where

1 row in set (0.00 sec)

 可見雖然在 year 這個列上存在索引 ind_sales_year,但是這個 SQL 語句並沒有用到這個索引,原因就是 or 中有一個條件中的列沒有索引。

 

(4)如果不是索引列的第一部分,如下例子:

1

2

3

4

5

6

7

8

9

10

11

12

13

mysql> explain select from sales2 where moneys = 1\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: sales2

type: ALL

possible_keys: NULL

keyNULL

key_len: NULL

ref: NULL

rows: 1000

Extra: Using where

1 row in set (0.00 sec)

 可見雖然在 money 上面建有複合索引,但是由於 money 不是索引的第一列,那麼在查詢中這個索引也不會被 MySQL 採用。

 

(5)如果 like 是以%開始,例如:

1

2

3

4

5

6

7

8

9

10

11

12

13

mysql> explain select from company2 where name like '%3'\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: company2

type: ALL

possible_keys: NULL

keyNULL

key_len: NULL

ref: NULL

rows: 1000

Extra: Using where

1 row in set (0.00 sec)

可見雖然在 name 上建有索引,但是由於 where 條件中 like 的值的“%”在第一位了,那麼MySQL 也不會採用這個索引。

 

(6) 如果列類型是字符串,那麼一定記得在 where 條件中把字符常量值用引號引起來,否則的話即便這個列上有索引,MySQL 也不會用到的,因爲,MySQL 默認把輸入的常量值進行轉換以後才進行檢索。如下面的例子中 company2 表中的 name 字段是字符型的,但是 SQL 語句中的條件值是一個數值型值,因此即便在 name 上有索引, MySQL 也不能正確地用上索引,而是繼續進行全表掃描。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

mysql> explain select from company2 where name = 294\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: company2

type: ALL

possible_keys: ind_company2_name

keyNULL

key_len: NULL

ref: NULL

rows: 1000

Extra: Using where

1 row in set (0.00 sec)

 

 

mysql> explain select from company2 where name '294'\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: company2

type: ref

possible_keys: ind_company2_name

key: ind_company2_name

key_len: 23

ref: const

rows: 1

Extra: Using where

1 row in set (0.00 sec)

 從上面的例子中可以看到,第一個 SQL 語句中把一個數值型常量賦值給了一個字符型的列name,那麼雖然在 name 列上有索引,但是也沒有用到;而第二個 SQL 語句就可以正確使用索引。

 

3 查看索引使用情況

  如果索引正在工作,Handler_read_key 的值將很高,這個值代表了一個行被索引值讀的次數,很低的值表明增加索引得到的性能改善不高,因爲索引並不經常使用。
  Handler_read_rnd_next 的值高則意味着查詢運行低效,並且應該建立索引補救。這個值的含義是在數據文件中讀下一行的請求數。如果正進行大量的表掃描,Handler_read_rnd_next 的值較高,則通常說明表索引不正確或寫入的查詢沒有利用索引,具體如下。

1

2

3

4

5

6

7

8

9

10

11

12

13

mysql> show status like 'Handler_read%';

+-----------------------+-------+

| Variable_name

| Value |

+-----------------------+-------+

| Handler_read_first | 0 |

| Handler_read_key | 5 |

| Handler_read_next | 0 |

| Handler_read_prev | 0 |

| Handler_read_rnd | 0 |

| Handler_read_rnd_next | 2055 |

+-----------------------+-------+

rows in set (0.00 sec)

從上面的例子中可以看出,目前使用的 MySQL 數據庫的索引情況並不理想。

 

三、兩個簡單實用的優化方法

1 定期分析表和檢查表

分析表的語法如下:

1

ANALYZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...

  本語句用於分析和存儲表的關鍵字分佈,分析的結果將可以使得系統得到準確的統計信息,使得 SQL 能夠生成正確的執行計劃。如果用戶感覺實際執行計劃並不是預期的執行計劃,執行一次分析表可能會解決問題。在分析期間,使用一個讀取鎖定對錶進行鎖定。這對於 MyISAM, BDB 和 InnoDB 表有作用。對於 MyISAM 表,本語句與使用 myisamchk -a 相當,下例中對錶 sales 做了表分析:

1

2

3

4

5

6

7

8

9

10

11

mysql> analyze table sales;

+--------------+---------+----------+----------+

Table

| Op

| Msg_type | Msg_text |

+--------------+---------+----------+----------+

| sakila.sales | analyze | status

| OK

|

+--------------+---------+----------+----------+

1 row in set (0.00 sec)

 

檢查表的語法如下:

1

CHECK TABLE tbl_name [, tbl_name] ... [option] ... option = {QUICK | FAST | MEDIUM | EXTENDED | CHANGED}

   檢查表的作用是檢查一個或多個表是否有錯誤。 CHECK TABLE 對 MyISAM 和 InnoDB 表有作用。對於 MyISAM 表,關鍵字統計數據被更新,例如:

1

2

3

4

5

6

7

8

9

10

11

mysql> check table sales;

+--------------+-------+----------+----------+

Table

| Op

| Msg_type | Msg_text |

+--------------+-------+----------+----------+

| sakila.sales | check | status

| OK

|

+--------------+-------+----------+----------+

1 row in set (0.00 sec)

CHECK TABLE 也可以檢查視圖是否有錯誤,比如在視圖定義中被引用的表已不存在,舉例如下。

(1)首先我們創建一個視圖。

1

2

mysql> create view sales_view3 as select from sales3;

Query OK, 0 rows affected (0.00 sec)

 

 (2)然後 CHECK 一下該視圖,發現沒有問題。

1

2

3

4

5

6

7

8

9

10

11

mysql> check table sales_view3;

+--------------------+-------+----------+----------+

Table

| Op

| Msg_type | Msg_text |

+--------------------+-------+----------+----------+

| sakila.sales_view3 | check | status

| OK

|

+--------------------+-------+----------+----------+

1 row in set (0.00 sec)

 

 
(3)現在刪除掉視圖依賴的表。

1

2

mysql> drop table sales3;

Query OK, 0 rows affected (0.00 sec)

 

 

(4)再來 CHECK 一下剛纔的視圖,發現報錯了。

1

2

3

4

5

6

7

8

mysql> check table sales_view3\G;

*************************** 1. row ***************************

Table: sakila.sales_view3

Op: check

Msg_type: error

Msg_text: View 'sakila.sales_view3' references invalid table(s) or column(s) or function(s)

or definer/invoker of view lack rights to use them

1 row in set (0.00 sec)

 

 

2 定期優化表

優化表的語法如下:

1

OPTIMIZE [LOCAL | NO_WRITE_TO_BINLOG] TABLE tbl_name [, tbl_name] ...

 

  如果已經刪除了表的一大部分,或者如果已經對含有可變長度行的表(含有 VARCHAR、BLOB 或 TEXT 列的表)進行了很多更改,則應使用 OPTIMIZE TABLE 命令來進行表優化。這個命令可以將表中的空間碎片進行合併,並且可以消除由於刪除或者更新造成的空間浪費,但OPTIMIZE TABLE 命令只對 MyISAM、BDB 和 InnoDB 表起作用。

 

1

2

3

4

5

6

7

8

9

10

11

mysql> optimize table sales;

+--------------+----------+----------+----------+

Table

| Op

| Msg_type | Msg_text |

+--------------+----------+----------+----------+

| sakila.sales | optimize | status

| OK

|

+--------------+----------+----------+----------+

1 row in set (0.00 sec)

 

 

 

注意:ANALYZE、CHECK、OPTIMIZE 執行期間將對錶進行鎖定,因此一定注意要在數據庫不繁忙的時候執行相關的操作。

 

四、常用 SQL 的優化

1 大批量插入數據

當用 load 命令導入數據的時候,適當的設置可以提高導入的速度。
對於 MyISAM 存儲引擎的表,可以通過以下方式快速的導入大量的數據。

ALTER TABLE tbl_name DISABLE KEYS;
loading the data
ALTER TABLE tbl_name ENABLE KEYS;

  DISABLE KEYS 和 ENABLE KEYS 用來打開或者關閉 MyISAM 表非唯一索引的更新。在導入大量的數據到一個非空的 MyISAM 表時,通過設置這兩個命令,可以提高導入的效率。對於導入大量數據到一個空的 MyISAM 表,默認就是先導入數據然後才創建索引的,所以不用進行設置。

 

複製代碼

mysql> alter table film_test2 disable keys;
Query OK, 0 rows affected (0.00 sec)

mysql> load data infile '/home/mysql/film_test.txt' into table film_test2;
Query OK, 529056 rows affected (6.34 sec)
Records: 529056 Deleted: 0 Skipped: 0 Warnings: 0

mysql> alter table film_test2 enable keys;
Query OK, 0 rows affected (12.25 sec)

複製代碼

 

 

 

  上面是對MyISAM表進行數據導入時的優化措施,對於InnoDB類型的表,這種方式並不能提高導入數據的效率,可以有以下幾種方式提高InnoDB表的導入效率。

(1)因爲 InnoDB 類型的表是按照主鍵的順序保存的,所以將導入的數據按照主鍵的順序排列,可以有效地提高導入數據的效率。

(2)在導入數據前執行 SET UNIQUE_CHECKS=0,關閉唯一性校驗,在導入結束後執行SET UNIQUE_CHECKS=1,恢復唯一性校驗,可以提高導入的效率。

 

SET UNIQUE_CHECKS=0;
#loading the data
load data infile '/home/mysql/film_test.txt' into table film_test2;
SET UNIQUE_CHECKS=1;

 

 

 

 

 

 

 

 

 

(3)如果應用使用自動提交的方式,建議在導入前執行 SET AUTOCOMMIT=0,關閉自動提交,導入結束後再執行 SET AUTOCOMMIT=1,打開自動提交,也可以提高導入的效率。

 

SET AUTOCOMMIT=0;
#loading the data
load data infile '/home/mysql/film_test.txt' into table film_test2;
SET AUTOCOMMIT=1;

 

 

 

2 優化 INSERT 語句

當進行數據 INSERT 的時候,可以考慮採用以下幾種優化方式。

  •  如果同時從同一客戶插入很多行,儘量使用多個值表的 INSERT 語句,這種方式將大大縮減客戶端與數據庫之間的連接、關閉等消耗,使得效率比分開執行的單個 INSERT 語句快(在一些情況中幾倍)。下面是一次插入多值的一個例子:
    insert into test values(1,2),(1,3),(1,4)...
  •  如果從不同客戶插入很多行,能通過使用 INSERT DELAYED 語句得到更高的速度。DELAYED 的含義是讓 INSERT 語句馬上執行,其實數據都被放在內存的隊列中,並沒有真正寫入磁盤,這比每條語句分別插入要快的多;LOW_PRIORITY 剛好相反,在所有其他用戶對錶的讀寫完後才進行插入;
  •  將索引文件和數據文件分在不同的磁盤上存放(利用建表中的選項);
  •  如果進行批量插入,可以增加 bulk_insert_buffer_size 變量值的方法來提高速度,但是,這隻能對 MyISAM 表使用;
  •  當從一個文本文件裝載一個表時,使用 LOAD DATA INFILE。這通常比使用很多 INSERT 語句快 20 倍。

 

3 優化 GROUP BY 語句

默認情況下,MySQL 對所有 GROUP BY col1,col2....的字段進行排序。這與在查詢中指定ORDER BY col1,col2...類似。因此,如果顯式包括一個包含相同的列的 ORDER BY 子句,則對 MySQL 的實際執行性能沒有什麼影響。

如果查詢包括 GROUP BY 但用戶想要避免排序結果的消耗,則可以指定 ORDER BY NULL禁止排序。

 

4 優化 ORDER BY 語句

在某些情況中, MySQL 可以使用一個索引來滿足 ORDER BY 子句,而不需要額外的排序。WHERE 條件和 ORDER BY 使用相同的索引,並且 ORDER BY 的順序和索引順序相同,並且ORDER BY 的字段都是升序或者都是降序。

例如,下列 SQL 可以使用索引。

SELECT * FROM t1 ORDER BY key_part1,key_part2,... ;
SELECT * FROM t1 WHERE key_part1=1 ORDER BY key_part1 DESC, key_part2 DESC;
SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 DESC;

 
但是在以下幾種情況下則不使用索引:

複製代碼

SELECT * FROM t1 ORDER BY key_part1 DESC, key_part2 ASC;
--order by 的字段混合 ASC 和 DESC
SELECT * FROM t1 WHERE key2=constant ORDER BY key1;
--用於查詢行的關鍵字與 ORDER BY 中所使用的不相同
SELECT * FROM t1 ORDER BY key1, key2;
--對不同的關鍵字使用 ORDER BY:

複製代碼

 

5 優化嵌套查詢

  MySQL 4.1 開始支持 SQL 的子查詢。這個技術可以使用 SELECT 語句來創建一個單列的查詢結果,然後把這個結果作爲過濾條件用在另一個查詢中。使用子查詢可以一次性地完成很多邏輯上需要多個步驟才能完成的 SQL 操作,同時也可以避免事務或者表鎖死,並且寫起來也很容易。但是,有些情況下,子查詢可以被更有效率的連接(JOIN)替代。

  連接(JOIN)之所以更有效率一些,是因爲 MySQL 不需要在內存中創建臨時表來完成這個邏輯上的需要兩個步驟的查詢工作。

 

6 MySQL 如何優化 OR 條件

對於含有 OR 的查詢子句,如果要利用索引,則 OR 之間的每個條件列都必須用到索引;如果沒有索引,則應該考慮增加索引。

MySQL 在處理含有 OR字句的查詢時,實際是對 OR 的各個字段分別查詢後的結果進行了 UNION。

 

7 使用 SQL 提示

SQL 提示(SQL HINT)是優化數據庫的一個重要手段,簡單來說就是在 SQL 語句中加入一些人爲的提示來達到優化操作的目的。

 

1.USE INDEX

在查詢語句中表名的後面,添加 USE INDEX 來提供希望 MySQL 去參考的索引列表,就可以讓 MySQL 不再考慮其他可用的索引。

1

2

3

4

5

6

7

8

9

10

11

12

13

mysql> explain select from sales2 use index (ind_sales2_id) where id = 3\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: sales2

type: ref

possible_keys: ind_sales2_id

key: ind_sales2_id

key_len: 5

ref: const

rows: 1

Extra: Using where

1 row in set (0.00 sec).

 

2.IGNORE INDEX

如果用戶只是單純地想讓 MySQL 忽略一個或者多個索引,則可以使用 IGNORE INDEX 作爲 HINT。

1

2

3

4

5

6

7

8

9

10

11

12

13

mysql> explain select from sales2 ignore index (ind_sales2_id) where id = 3\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: sales2

type: ALL

possible_keys: NULL

keyNULL

key_len: NULL

ref: NULL

rows: 1000

Extra: Using where

1 row in set (0.00 sec).

 

3.FORCE INDEX

爲強制 MySQL 使用一個特定的索引,可在查詢中使用 FORCE INDEX 作爲 HINT。

例如,
當不強制使用索引的時候,因爲 id 的值都是大於 0 的,因此 MySQL 會默認進行全表掃描,而不使用索引,如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

mysql> explain select from sales2 where id > 0 \G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: sales2

type: ALL

possible_keys: ind_sales2_id

keyNULL

key_len: NULL

ref: NULL

rows: 1000

Extra: Using where

1 row in set (0.00 sec)

 
但是,當使用 FORCE INDEX 進行提示時,即便使用索引的效率不是最高,MySQL 還是選擇使用了索引,這是 MySQL 留給用戶的一個自行選擇執行計劃的權力。加入 FORCE INDEX 提示後再次執行上面的 SQL:

1

2

3

4

5

6

7

8

9

10

11

12

13

mysql> explain select from sales2 force index (ind_sales2_id) where id > 0\G;

*************************** 1. row ***************************

id: 1

select_type: SIMPLE

table: sales2

type: range

possible_keys: ind_sales2_id

key: ind_sales2_id

key_len: 5

ref: NULL

rows: 1000

223Extra: Using where

1 row in set (0.00 sec).

 

 執行計劃中使用了 FORCE INDEX 後的索引。

2.IGNORE INDEX
如果用戶只是單純地想讓 MySQL 忽略一個或者多個索引,則可以使用 IGNORE INDEX 作
爲 HINT。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章