1. 程式人生 > 其它 >直播軟體原始碼,選項提供多選專案,彈出多選框

直播軟體原始碼,選項提供多選專案,彈出多選框

sql優化一般步驟概要:

通過 show status 命令瞭解各種sql的執行頻率
定位執行效率較低的sql語句
通過explain分析低效sql的執行計劃
通過 show profile 分析sql
通過trace分析 優化器 如何選擇執行計劃
確定問題並採取相應的優化措施
以下闡述每步具體操作,請詳細跟進操作,會有不錯的收貨!

1 通過 show status 命令瞭解各種sql的執行頻率
mysql客戶端連線成功後,通過show[session|global] status命令,可以檢視伺服器的狀態,如果不加預設為session(session級為當前連線的統計結果,global級為自資料庫上次啟動到現在的統計結果)。
Eg: show status like 'Com_%';

…圖示內容還有好多,不一一展示。
com_xx表示每個xx語句的執行次數,通常著重看curd操作次數(這些引數適用於所有儲存引擎);

Com_insert:執行select操作的次數,一次查詢只累加1;
Com_select:執行insert操作的次數,批量插入的insert操作只累加一次;
Com_delete:執行update操作次數,提交和回滾均會累加;
Com_update:執行delete操作次數。
針對innodb儲存引擎的表操作,累加的演算法比如:show status like 'Innodb_rows_%';

Innodb_rows_deleted :執行delete操作刪除的行數;
Innodb_rows_inserted:執行insert操作插入的行數;
Innodb_rows_read :select查詢返回的函式;
Innodb_rows_updated: update操作更新的函式。
通過截圖,可以瞭解到當前資料庫是以插入和查詢為主,以及各種型別操作的執行比例。
對於事務型的應用,通過com_commit 和com_rollback可以瞭解事務提交和回滾的情況,對於回滾特別頻繁的操作,可能意味著程式碼編寫有問題。
以下操作使用者瞭解資料庫的基本情況:
show status like ‘connections’;-- 試圖連線mysql資料庫的次數

show status like ‘uptime’;-- 伺服器工作時間

show status like ‘slow_queries’; – 慢查詢的次數

2 定位執行效率較低的sql語句
通過慢查詢日誌定位哪些sql執行效率低下,用–log-slow-queries[=file_name]選項啟動時,mysqld寫一個包含所有執行時間超過long_query_time 秒的sql語句的日誌檔案,具體可參看後面的日誌管理部分。
慢查詢日誌在查詢結束以後才記錄,所以在應用反正執行效率出現問題的時候查詢慢查詢日誌並不能定位問題,可以使用show processlist命令檢視當前mysql在進行的執行緒,包括執行緒的狀態,是否鎖表等,可以實時檢視sql的執行情況,同時對一些鎖表操作進行優化。
mysql> show processlist;
+------+------+-----------+------+---------+------+-------+------------------+
| Id | User | Host | db | Command | Time | State | Info |
+------+------+-----------+------+---------+------+-------+------------------+
| 1619 | root | localhost | wq | Query | 0 | init | show processlist |
+------+------+-----------+------+---------+------+-------+------------------+
1 row in set (0.00 sec)
1
2
3
4
5
6
7
3 通過explain分析低效sql的執行計劃
追查到效率低的sql後,可以通過explain或desc命令獲取mysql如何執行select語句的資訊,包括在select語句執行過程中表如何連線和連線的順序,eg:統計某個email為租賃電影拷貝所支付的總金額,需要關聯客戶表customer和付款表payment,並且對付款金額amount欄位做求和操作,相應sql的執行計劃如下:
explain select sum(amount) from customer a ,payment b where 1=1 and a.customer_id=b.customer_id and email ='[email protected]'\G

mysql> explain select sum(amount) from customer a ,payment b where 1=1 and a.customer_id=b.customer_id and email ='[email protected]'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: a
type: ALL
possible_keys: PRIMARY
key: NULL
key_len: NULL
ref: NULL
rows: 599
Extra: Using where
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: b
type: ref
possible_keys: idx_fk_customer_id
key: idx_fk_customer_id
key_len: 2
ref: wq.a.customer_id
rows: 13
Extra: NULL
2 rows in set (0.00 sec)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
下面對每個列進行簡單說明:
select_type:表示select的型別,常見取值 simple(簡單表,即不適用表連線或者子查詢),primary(主查詢,即外層的查詢),union(union中的第二個或者後面的查詢語句),subquery(子查詢中的第一個select)等。
table:輸出結果集的表。
possible_keys:表示查詢時可能使用的索引,不表示出現在這裡的就是表的全部索引。
key:表示實際使用的索引。
key_len:使用到索引欄位的長度。
rows:掃描行的數量。
extra:執行情況的說明和描述,包含不適合在其他列中顯示但是對執行計劃非常重要的額外資訊。

type:表示mysql在表中找到所需行的方式,或者叫訪問型別,常見如下(效能由左至右由最差變道最好):all 、 index 、 range 、 ref、 eq_ref 、 const,system 、 null

補充:型別type還有其他的值:
ref_or_null:和ref類似,區別在於條件中包含對null的查詢
index_merge:索引合併優化
unique_subquery:in的後面是一個查詢主鍵欄位的子查詢
index_subquery:與unique_subquery類似,區別在於in的後面是查詢非唯一索引欄位的子查詢。

我們可以試下大概什麼情況會取type型別的值:
(1)type=all,表示全表掃描,mysql遍歷全表來找到匹配的行:

mysql> explain select * from film where rating >9\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: film
type: ALL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1000
Extra: Using where
1 row in set (0.00 sec)
1
2
3
4
5
6
7
8
9
10
11
12
13
(2)type=index ,索引全表掃描,mysql遍歷整個索引來查詢匹配的行:

mysql> explain select title from film\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: film
type: index
possible_keys: NULL
key: idx_title
key_len: 767
ref: NULL
rows: 1000
Extra: Using index
1 row in set (0.00 sec)
1
2
3
4
5
6
7
8
9
10
11
12
13
(3)type=range,索引範圍掃描,常見於 < 、 <=、 >、 >= 、 between 等操作符:

mysql> explain select * from payment where customer_id>=300 and customer_id<=350\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: payment
type: range
possible_keys: idx_fk_customer_id
key: idx_fk_customer_id
key_len: 2
ref: NULL
rows: 1349
Extra: Using index condition
1 row in set (0.00 sec)
1
2
3
4
5
6
7
8
9
10
11
12
13
(4) type=ref ,使用非唯一索引掃描或唯一索引的字首掃描,返回匹配某個單獨值的記錄行,eg:

mysql> EXPLAIN select * from payment where customer_id=350\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: payment
type: ref
possible_keys: idx_fk_customer_id
key: idx_fk_customer_id
key_len: 2
ref: const
rows: 23
Extra: NULL
1 row in set (0.00 sec)
1
2
3
4
5
6
7
8
9
10
11
12
13
'idx_fk_customer_id’索引是非唯一索引,查詢條件為等值查詢條件 customer_id=35,所以掃描索引的型別為ref.ref還經常出現在join操作中:
如:

mysql> explain select a.*,b.* from payment a,customer b where a.customer_id=b.customer_id\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: b
type: ALL
possible_keys: PRIMARY
key: NULL
key_len: NULL
ref: NULL
rows: 599
Extra: NULL
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: a
type: ref
possible_keys: idx_fk_customer_id
key: idx_fk_customer_id
key_len: 2
ref: wq.b.customer_id
rows: 13
Extra: NULL
2 rows in set (0.00 sec)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(5)type=eq_ref ,類似ref ,區別就在使用的索引是唯一索引,對於每個索引鍵值,表中只有一條記錄匹配;簡單說即多表連線中使用primary key或者unique index作為關聯條件。

mysql> explain select * from film a,film_text b where a.film_id=b.film_id\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: b
type: ALL
possible_keys: PRIMARY
key: NULL
key_len: NULL
ref: NULL
rows: 1000
Extra: NULL
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: a
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 2
ref: wq.b.film_id
rows: 1
Extra: Using where
2 rows in set (0.00 sec)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
(6)TYPE=const/system ,單表中最多有一個匹配行,查詢起來非常迅速,所以這個匹配行中的其他列的值可以被優化器在當前查詢中當做常量來處理,例如,根據主鍵primary key 或者唯一索引unique index進行的查詢。下面構造一個查詢:

mysql> alter table customer add unique index uk_email(email);
Query OK, 0 rows affected (0.10 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> explain select * from (select * from customer where email='[email protected]')a\G
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: <derived2>
type: system
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: 1
Extra: NULL
*************************** 2. row ***************************
id: 2
select_type: DERIVED
table: customer
type: const
possible_keys: uk_email
key: uk_email
key_len: 153
ref: const
rows: 1
Extra: NULL
2 rows in set (0.00 sec)
mysql> explain select * from film_text b where b.film_id=22\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: b
type: const
possible_keys: PRIMARY
key: PRIMARY
key_len: 2
ref: const
rows: 1
Extra: NULL
1 row in set (0.01 sec)
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
35
36
37
38
39
40
以上可以看出,通過唯一索引 uk_email訪問的時候,型別為type=const;而從只有一條記錄的a表中查詢資料時,型別type就為system。
(7)type=null ,mysql 不用訪問表或者索引,直接能得到結果,eg:

mysql> explain select 1 from dual where 1\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: NULL
type: NULL
possible_keys: NULL
key: NULL
key_len: NULL
ref: NULL
rows: NULL
Extra: No tables used
1 row in set (0.00 sec)
1
2
3
4
5
6
7
8
9
10
11
12
13
mysql4.1開始引入了explain extended命令,通過explain extended加上show warnings,可以看到sql真正被執行之後,優化器做了哪些sql改寫:

mysql> explain extended select sum(amount) from customer a,payment b where 1=1 and a.customer_id=b.customer_id and email='[email protected]'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: a
type: const
possible_keys: PRIMARY,uk_email
key: uk_email
key_len: 153
ref: const
rows: 1
filtered: 100.00
Extra: Using index
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: b
type: ref
possible_keys: idx_fk_customer_id
key: idx_fk_customer_id
key_len: 2
ref: const
rows: 28
filtered: 100.00
Extra: NULL
2 rows in set, 1 warning (0.00 sec)

mysql> show warnings\G
*************************** 1. row ***************************
Level: Note
Code: 1003
Message: /* select#1 */ select sum(`wq`.`b`.`amount`) AS `sum(amount)` from `wq`.`customer` `a` join `wq`.`payment` `b` where ((`wq`.`b`.`customer_id` = '77') and ('[email protected]' = '[email protected]'))
1 row in set (0.00 sec)
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
以上結果可以看到:

explain extended輸出結果中多了filtered欄位,
同時從warning的message欄位能夠看到優化器自動去除了1=1 恆成立的條件,也就是說優化器在改寫sql的時候會自動去掉恆成立的條件。
在遇到複雜sql的時候,可以利用explain extended的結果獲取一個更清晰易讀的sql.
mysql 5.1開始支援分割槽功能,同時explain命令也增加了分割槽的功能, 可以通過explain partitions的命令檢視sql所訪問的分割槽。eg:建立一個hash分割槽的customer_part表,根據分割槽鍵查詢的時候,能夠看到explain partitions的輸出結果中有一列partitions,其中顯示了sql所需要訪問的分割槽名字 p2:

mysql> create table customer_part(customer_id smallint(5) unsigned not null auto_increment,primary key(customer_id))partition by hash(customer_id) partitions 8;
Query OK, 0 rows affected (0.26 sec)


mysql> insert into customer_part select customer_id from customer;
Query OK, 599 rows affected (0.01 sec)
Records: 599 Duplicates: 0 Warnings: 0

mysql> explain partitions select * from customer_part where customer_id=130\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: customer_part
partitions: p2
type: const
possible_keys: PRIMARY
key: PRIMARY
key_len: 2
ref: const
rows: 1
Extra: Using index
1 row in set (0.00 sec)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
但是有時只通過explain分析執行計劃並不能很快定位sql問題,那麼如果有profile聯合分析就更好了。

4 通過 show profile 分析sql
從5.0.37版本開始增加了對show profiles 和show profile的語句支援。通過having_profiling引數,可以看到sql是否支援profile:

mysql> select @@have_profiling;
+------------------+
| @@have_profiling |
+------------------+
| YES |
+------------------+
1 row in set, 1 warning (0.00 sec)
1
2
3
4
5
6
7
預設profiling是關閉的,可以通過set語句在session級別開啟profiling:

mysql> select @@profiling;
+-------------+
| @@profiling |
+-------------+
| 0 |
+-------------+
1 row in set, 1 warning (0.00 sec)
1
2
3
4
5
6
7
可以直接set其值為1

mysql> set profiling=1;
Query OK, 0 rows affected, 1 warning (0.00 sec)
1
2
通過profile我們可以大致瞭解sql執行的過程。例如myisam表有表元資料的快取(比如行數,即count(*)值),那麼對一個myisam表的count(*)是不需要消耗太多資源的,而對於innodb來說,就沒有這種元資料快取,count(*)執行的較慢。下面來做個試驗驗證下。
首先,在一個innodb引擎的付款表payment上,執行一個count(*)查詢:

mysql> select count(*) from payment;
+----------+
| count(*) |
+----------+
| 16049 |
+----------+
1 row in set (0.00 sec)
1
2
3
4
5
6
7
執行完畢後,通過show profiles語句,看到當前sql的query id為1:

mysql> show profiles;
+----------+------------+------------------------------+
| Query_ID | Duration | Query |
+----------+------------+------------------------------+
| 1 | 0.00372550 | select count(*) from payment |
+----------+------------+------------------------------+
1 row in set, 1 warning (0.00 sec)
1
2
3
4
5
6
7
通過show profile for query 1;語句能夠看到執行過程中執行緒的每個狀態和消耗的時間:

mysql> show profile for query 1;
+----------------------+----------+
| Status | Duration |
+----------------------+----------+
| starting | 0.000082 |
| checking permissions | 0.000008 |
| Opening tables | 0.000022 |
| init | 0.000017 |
| System lock | 0.000008 |
| optimizing | 0.000006 |
| statistics | 0.000014 |
| preparing | 0.000012 |
| executing | 0.000003 |
| Sending data | 0.003496 |
| end | 0.000007 |
| query end | 0.000006 |
| closing tables | 0.000010 |
| freeing items | 0.000011 |
| cleaning up | 0.000025 |
+----------------------+----------+
15 rows in set, 1 warning (0.00 sec)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
狀態值為sending data表示mysql執行緒開始訪問資料行並把結果返回給客戶端之前,mysql執行緒要做大良的磁碟讀取操作,所以會導致在整個查詢中耗時最長的狀態。
通過執行show profile for query,可以看到在執行count(*)操作的過程中,時間耗費在狀態值為sending data 上,通過查詢 information_schema.profiling表並按照時間做個desc排序:

mysql> select state,sum(duration) as total_r,round(100*sum(duration)/(select sum(duration) from information_schema.profiling where query_id=@query_id),2) as pct_r,count(*) as calls,sum(duration)/count(*) as "R/Call" from information_schema.profiling where query_id=@query_id group by state order by total_r desc;
+----------------------+----------+-------+-------+--------------+
| state | total_r | pct_r | calls | R/Call |
+----------------------+----------+-------+-------+--------------+
| Sending data | 0.003496 | 93.80 | 1 | 0.0034960000 |
| starting | 0.000082 | 2.20 | 1 | 0.0000820000 |
| cleaning up | 0.000025 | 0.67 | 1 | 0.0000250000 |
| Opening tables | 0.000022 | 0.59 | 1 | 0.0000220000 |
| init | 0.000017 | 0.46 | 1 | 0.0000170000 |
| statistics | 0.000014 | 0.38 | 1 | 0.0000140000 |
| preparing | 0.000012 | 0.32 | 1 | 0.0000120000 |
| freeing items | 0.000011 | 0.30 | 1 | 0.0000110000 |
| closing tables | 0.000010 | 0.27 | 1 | 0.0000100000 |
| System lock | 0.000008 | 0.21 | 1 | 0.0000080000 |
| checking permissions | 0.000008 | 0.21 | 1 | 0.0000080000 |
| end | 0.000007 | 0.19 | 1 | 0.0000070000 |
| query end | 0.000006 | 0.16 | 1 | 0.0000060000 |
| optimizing | 0.000006 | 0.16 | 1 | 0.0000060000 |
| executing | 0.000003 | 0.08 | 1 | 0.0000030000 |
+----------------------+----------+-------+-------+--------------+
15 rows in set (0.00 sec)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
獲取到最消耗時間的執行緒狀態後,mysql進一步選擇all,cpu,block io,context switch,page faults等明細型別來檢視mysql在使用什麼資源上耗費了過高的時間,例如,選擇檢視cpu的耗費時間:

mysql> show profile cpu for query 1;
+----------------------+----------+----------+------------+
| Status | Duration | CPU_user | CPU_system |
+----------------------+----------+----------+------------+
| starting | 0.000065 | 0.000027 | 0.000028 |
| checking permissions | 0.000007 | 0.000003 | 0.000004 |
| Opening tables | 0.000021 | 0.000011 | 0.000011 |
| init | 0.000014 | 0.000007 | 0.000007 |
| System lock | 0.000009 | 0.000004 | 0.000005 |
| optimizing | 0.000007 | 0.000003 | 0.000003 |
| statistics | 0.000013 | 0.000007 | 0.000007 |
| preparing | 0.000012 | 0.000005 | 0.000006 |
| executing | 0.000003 | 0.000002 | 0.000002 |
| Sending data | 0.003683 | 0.003684 | 0.000000 |
| end | 0.000007 | 0.000006 | 0.000000 |
| query end | 0.000006 | 0.000006 | 0.000000 |
| closing tables | 0.000009 | 0.000009 | 0.000000 |
| freeing items | 0.000010 | 0.000010 | 0.000000 |
| cleaning up | 0.000014 | 0.000014 | 0.000000 |
+----------------------+----------+----------+------------+
15 rows in set, 1 warning (0.00 sec)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
可想而知,sending data狀態值大的原因為時間主要耗費在cpu的操作上了,對比myisam表的count(*)操作,也建立一個同樣表結構的myisam表,資料量也完全一致。

mysql> create table payment_myisam like payment;
Query OK, 0 rows affected (0.04 sec)

mysql> alter table payment_myisam engine=myisam;
Query OK, 0 rows affected (0.04 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> insert into payment_myisam select * from payment;
Query OK, 16049 rows affected (0.08 sec)
Records: 16049 Duplicates: 0 Warnings: 0
1
2
3
4
5
6
7
8
9
10
執行count(*),顯示profile:

mysql> select count(*) from payment_myisam;
+----------+
| count(*) |
+----------+
| 16049 |
+----------+
1 row in set (0.00 sec)

mysql> show profiles;
+----------+------------+--------------------------------------------------------------------------------------------------------------------------------------+
| Query_ID | Duration | Query |
+----------+------------+--------------------------------------------------------------------------------------------------------------------------------------+
| 1 | 0.00016550 | select @@have_profiling |
| 2 | 0.00387925 | select count(*) from payment |
| 3 | 0.00005900 | show profile context switch for query 2 |
| 4 | 0.00007175 | create table payment_myisam lkike payment |
| 5 | 0.04542150 | create table payment_myisam like payment |
| 6 | 0.03682200 | alter table payment_myisam engine=myisam |
| 7 | 0.08007525 | insert into payment_myisam select * from payment |
| 8 | 0.00005425 | mysql> create table payment_myisam like payment |
| 9 | 0.00005275 | Query OK, 0 rows affected (0.04 sec)

mysql> alter table payment_myisam engine=myisam |
| 10 | 0.00005300 | Query OK, 0 rows affected (0.04 sec)
Records: 0 Duplicates: 0 Warnings: 0

mysql> insert into payment_myisam select * from payment |
| 11 | 0.00006350 | Query OK, 16049 rows affected (0.08 sec)
Records: 16049 Duplicates: 0 Warnings: 0 |
| 12 | 0.00016450 | select count(*) from payment_myisam |
+----------+------------+--------------------------------------------------------------------------------------------------------------------------------------+
12 rows in set, 1 warning (0.00 sec)

mysql> show profile for query 12;
+----------------------+----------+
| Status | Duration |
+----------------------+----------+
| starting | 0.000063 |
| checking permissions | 0.000007 |
| Opening tables | 0.000020 |
| init | 0.000014 |
| System lock | 0.000009 |
| optimizing | 0.000008 |
| executing | 0.000010 |
| end | 0.000004 |
| query end | 0.000004 |
| closing tables | 0.000009 |
| freeing items | 0.000008 |
| cleaning up | 0.000012 |
+----------------------+----------+
12 rows in set, 1 warning (0.00 sec)
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
由上面兩種引擎所建表的count(*)操作後,執行profile可看出,innodb引擎的表由sending data狀態,存在訪問資料即磁碟讀取的過程,時間主要耗費在cpu上,而myisam引擎的表在executing之後就結束查詢,意味著不需要去訪問資料。
如果對mysql原始碼感興趣,可以通過show profile source for query檢視sql解析執行過程中的每個步驟對應的原始碼檔案、函式名稱、具體原始檔行數:

mysql> show profile source for query 1;
+----------------------+----------+-----------------------+------------------+-------------+
| Status | Duration | Source_function | Source_file | Source_line |
+----------------------+----------+-----------------------+------------------+-------------+
| starting | 0.000065 | NULL | NULL | NULL |
| checking permissions | 0.000007 | check_access | sql_parse.cc | 5350 |
| Opening tables | 0.000021 | open_tables | sql_base.cc | 5095 |
| init | 0.000014 | mysql_prepare_select | sql_select.cc | 1051 |
| System lock | 0.000009 | mysql_lock_tables | lock.cc | 304 |
| optimizing | 0.000007 | optimize | sql_optimizer.cc | 139 |
| statistics | 0.000013 | optimize | sql_optimizer.cc | 365 |
| preparing | 0.000012 | optimize | sql_optimizer.cc | 488 |
| executing | 0.000003 | exec | sql_executor.cc | 110 |
| Sending data | 0.003683 | exec | sql_executor.cc | 190 |
| end | 0.000007 | mysql_execute_select | sql_select.cc | 1106 |
| query end | 0.000006 | mysql_execute_command | sql_parse.cc | 5049 |
| closing tables | 0.000009 | mysql_execute_command | sql_parse.cc | 5097 |
| freeing items | 0.000010 | mysql_parse | sql_parse.cc | 6486 |
| cleaning up | 0.000014 | dispatch_command | sql_parse.cc | 1815 |
+----------------------+----------+-----------------------+------------------+-------------+
15 rows in set, 1 warning (0.00 sec)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
show profile可以在sql優化時告訴我們時間主要浪費在哪了。

5 通過trace分析 優化器 如何選擇執行計劃
mysql5.6是通過trace檔案進一步告訴我們優化器是如何選擇執行計劃的。
(即mysql5.6提供了對sql的跟蹤trace檔案,從而得知優化器為何選擇a執行計劃而不選擇b執行計劃,有助於我們理解優化器的行為。)
首先開啟trace,設定格式為json,設定trace最大能夠使用記憶體的大小,避免解析過程中因為預設記憶體過小不能完全顯示:

mysql> set optimizer_trace="enabled=on" ,end_markers_in_json=on;
Query OK, 0 rows affected (0.00 sec)

mysql> set optimizer_trace_max_mem_size=1000000;
Query OK, 0 rows affected (0.00 sec)
1
2
3
4
5
然後執行下想做追蹤的sql語句,eg:租賃表中rental的庫存編號inventory_id為4466的電影拷貝 ,出租日期rental_date 在2005-05-25 4:00:00~5:00:00範圍內的出租記錄,最後通過檢查語句select * from information_schema.optimizer_trace就可以知道mysql如何執行sql的

mysql> select rental_id from rental where 1=1 and rental_date>='2005-05-25 04:00:00' and rental_date <='2005-05-25 05:00:00' and inventory_id=4466;
+-----------+
| rental_id |
+-----------+
| 39 |
+-----------+
1 row in set (0.00 sec)

mysql> select * from information_schema.optimizer_trace\G
*************************** 1. row ***************************
QUERY: select rental_id from rental where 1=1 and rental_date>='2005-05-25 04:00:00' and rental_date <='2005-05-25 05:00:00' and inventory_id=4466
TRACE: {
"steps": [
{
"join_preparation": {
"select#": 1,
"steps": [
{
"expanded_query": "/* select#1 */ select `rental`.`rental_id` AS `rental_id` from `rental` where ((1 = 1) and (`rental`.`rental_date` >= '2005-05-25 04:00:00') and (`rental`.`rental_date` <= '2005-05-25 05:00:00') and (`rental`.`inventory_id` = 4466))"
}
] /* steps */
} /* join_preparation */
},
{
"join_optimization": {
"select#": 1,
"steps": [
{
"condition_processing": {
"condition": "WHERE",
"original_condition": "((1 = 1) and (`rental`.`rental_date` >= '2005-05-25 04:00:00') and (`rental`.`rental_date` <= '2005-05-25 05:00:00') and (`rental`.`inventory_id` = 4466))",
"steps": [
{
"transformation": "equality_propagation",
"resulting_condition": "((1 = 1) and (`rental`.`rental_date` >= '2005-05-25 04:00:00') and (`rental`.`rental_date` <= '2005-05-25 05:00:00') and multiple equal(4466, `rental`.`inventory_id`))"
},
{
"transformation": "constant_propagation",
"resulting_condition": "((1 = 1) and (`rental`.`rental_date` >= '2005-05-25 04:00:00') and (`rental`.`rental_date` <= '2005-05-25 05:00:00') and multiple equal(4466, `rental`.`inventory_id`))"
},
{
"transformation": "trivial_condition_removal",
"resulting_condition": "((`rental`.`rental_date` >= '2005-05-25 04:00:00') and (`rental`.`rental_date` <= '2005-05-25 05:00:00') and multiple equal(4466, `rental`.`inventory_id`))"
}
] /* steps */
} /* condition_processing */
},
{
"table_dependencies": [
{
"table": "`rental`",
"row_may_be_null": false,
"map_bit": 0,
"depends_on_map_bits": [
] /* depends_on_map_bits */
}
] /* table_dependencies */
},
{
"ref_optimizer_key_uses": [
{
"table": "`rental`",
"field": "inventory_id",
"equals": "4466",
"null_rejecting": false
}
] /* ref_optimizer_key_uses */
},
{
"rows_estimation": [
{
"table": "`rental`",
"range_analysis": {
"table_scan": {
"rows": 16008,
"cost": 3300.7
} /* table_scan */,
"potential_range_indices": [
{
"index": "PRIMARY",
"usable": false,
"cause": "not_applicable"
},
{
"index": "rental_date",
"usable": true,
"key_parts": [
"rental_date",
"inventory_id",
"customer_id"
] /* key_parts */
},
{
"index": "idx_fk_inventory_id",
"usable": true,
"key_parts": [
"inventory_id",
"rental_id"
] /* key_parts */
},
{
"index": "idx_fk_customer_id",
"usable": false,
"cause": "not_applicable"
},
{
"index": "idx_fk_staff_id",
"usable": false,
"cause": "not_applicable"
}
] /* potential_range_indices */,
"best_covering_index_scan": {
"index": "rental_date",
"cost": 3229.9,
"chosen": true
} /* best_covering_index_scan */,
"setup_range_conditions": [
] /* setup_range_conditions */,
"group_index_range": {
"chosen": false,
"cause": "not_group_by_or_distinct"
} /* group_index_range */,
"analyzing_range_alternatives": {
"range_scan_alternatives": [
{
"index": "rental_date",
"ranges": [
"2005-05-25 04:00:00 <= rental_date <= 2005-05-25 05:00:00"
] /* ranges */,
"index_dives_for_eq_ranges": true,
"rowid_ordered": false,
"using_mrr": false,
"index_only": true,
"rows": 10,
"cost": 3.0254,
"chosen": true
},
{
"index": "idx_fk_inventory_id",
"ranges": [
"4466 <= inventory_id <= 4466"
] /* ranges */,
"index_dives_for_eq_ranges": true,
"rowid_ordered": true,
"using_mrr": false,
"index_only": false,
"rows": 5,
"cost": 7.01,
"chosen": false,
"cause": "cost"
}
] /* range_scan_alternatives */,
"analyzing_roworder_intersect": {
"usable": false,
"cause": "too_few_roworder_scans"
} /* analyzing_roworder_intersect */
} /* analyzing_range_alternatives */,
"chosen_range_access_summary": {
"range_access_plan": {
"type": "range_scan",
"index": "rental_date",
"rows": 10,
"ranges": [
"2005-05-25 04:00:00 <= rental_date <= 2005-05-25 05:00:00"
] /* ranges */
} /* range_access_plan */,
"rows_for_plan": 10,
"cost_for_plan": 3.0254,
"chosen": true
} /* chosen_range_access_summary */
} /* range_analysis */
}
] /* rows_estimation */
},
{
"considered_execution_plans": [
{
"plan_prefix": [
] /* plan_prefix */,
"table": "`rental`",
"best_access_path": {
"considered_access_paths": [
{
"access_type": "ref",
"index": "idx_fk_inventory_id",
"rows": 5,
"cost": 6,
"chosen": true
},
{
"access_type": "range",
"rows": 5,
"cost": 5.0254,
"chosen": true
}
] /* considered_access_paths */
} /* best_access_path */,
"cost_for_plan": 5.0254,
"rows_for_plan": 5,
"chosen": true
}
] /* considered_execution_plans */
},
{
"attaching_conditions_to_tables": {
"original_condition": "((`rental`.`inventory_id` = 4466) and (`rental`.`rental_date` >= '2005-05-25 04:00:00') and (`rental`.`rental_date` <= '2005-05-25 05:00:00'))",
"attached_conditions_computation": [
] /* attached_conditions_computation */,
"attached_conditions_summary": [
{
"table": "`rental`",
"attached": "((`rental`.`inventory_id` = 4466) and (`rental`.`rental_date` >= '2005-05-25 04:00:00') and (`rental`.`rental_date` <= '2005-05-25 05:00:00'))"
}
] /* attached_conditions_summary */
} /* attaching_conditions_to_tables */
},
{
"refine_plan": [
{
"table": "`rental`",
"access_type": "range"
}
] /* refine_plan */
}
] /* steps */
} /* join_optimization */
},
{
"join_execution": {
"select#": 1,
"steps": [
] /* steps */
} /* join_execution */
}
] /* steps */
}
MISSING_BYTES_BEYOND_MAX_MEM_SIZE: 0
INSUFFICIENT_PRIVILEGES: 0
1 row in set (0.00 sec)
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
這裡缺一段講解,稍後補上

6 確定問題並採取相應的優化措施
經過以上操作,基本可以定位問題出現的原因。此時可以根據實際情況採取相應措施,通過優化來提高執行的效率。比如在第三點提到的低效sql的執行計劃,已經確認是對客戶表customer的權標掃描導致效率不理想,我們可以通過對email建立索引來提高效率。

mysql> create index idx_email on customer(email);
Query OK, 0 rows affected, 1 warning (0.05 sec)
Records: 0 Duplicates: 0 Warnings: 1

mysql> explain select sum(amount) from customer a ,payment b where 1=1 and a.customer_id=b.customer_id and email ='[email protected]'\G
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: a
type: const
possible_keys: PRIMARY,uk_email,idex_email,idx_email
key: uk_email
key_len: 153
ref: const
rows: 1
Extra: Using index
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: b
type: ref
possible_keys: idx_fk_customer_id
key: idx_fk_customer_id
key_len: 2
ref: const
rows: 28
Extra: NULL
2 rows in set (0.00 sec)
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
對比第3點,可以看出建立索引對customer表需要掃描的行數rows變少,效能有所提升(根據型別判斷效能:由all變為const),可見索引的使用可以大大提高資料庫的訪問速度,尤其表越大效率越明顯。
————————————————
版權宣告:本文為CSDN博主「甜可兒」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連結及本宣告。
原文連結:https://blog.csdn.net/qq_17033579/article/details/82950096