1. 程式人生 > 其它 >mysql檢視產生派生表無法優化案例

mysql檢視產生派生表無法優化案例

環境:mysql 5.7/8.0

匯入測試資料:

git clone https://github.com/datacharmer/test_db
cd test_db
mysql -u root -p < employees.sql

employees : 300024 條記錄

salaries :2844047 條記錄

1、執行一個兩表關聯統計SQL,執行速度非常快,整個過程掃描了122行。

mysql> select VARIABLE_VALUE into @a from performance_schema.session_status where variable_name = 'Innodb_rows_read
'; Query OK, 1 row affected (0.00 sec) mysql> select e.emp_no,(select max(s.salary) from salaries s where s.emp_no=e.emp_no) from employees e limit 10; +--------+----------------------------------------------------------------+ | emp_no | (select max(s.salary) from salaries s where s.emp_no=e.emp_no) | +--------+----------------------------------------------------------------+ | 10001
| 88958 | | 10002 | 72527 | | 10003 | 43699 | | 10004 | 74057 | | 10005 | 94692
| | 10006 | 60098 | | 10007 | 88070 | | 10008 | 52668 | | 10009 | 94443 | | 10010 | 80324 | +--------+----------------------------------------------------------------+ 10 rows in set (0.00 sec) mysql> select VARIABLE_VALUE into @b from performance_schema.session_status where variable_name = 'Innodb_rows_read'; Query OK, 1 row affected (0.00 sec) mysql> select @b-@a; +-------+ | @b-@a | +-------+ | 122 | +-------+ 1 row in set (0.00 sec)

2、將這個關聯SQL,做成檢視,再次查詢會非常慢,實際掃描了 314W 行。

mysql> create view v_test as select e.emp_no,(select max(s.salary) from salaries s where s.emp_no=e.emp_no) from employees e;
Query OK, 0 rows affected (0.01 sec)

mysql> select VARIABLE_VALUE into @a from performance_schema.session_status where variable_name = 'Innodb_rows_read';
Query OK, 1 row affected (0.00 sec)

mysql> select * from v_test limit 10;
+--------+----------------------------------------------------------------+
| emp_no | (select max(s.salary) from salaries s where s.emp_no=e.emp_no) |
+--------+----------------------------------------------------------------+
|  10001 |                                                          88958 |
|  10002 |                                                          72527 |
|  10003 |                                                          43699 |
|  10004 |                                                          74057 |
|  10005 |                                                          94692 |
|  10006 |                                                          60098 |
|  10007 |                                                          88070 |
|  10008 |                                                          52668 |
|  10009 |                                                          94443 |
|  10010 |                                                          80324 |
+--------+----------------------------------------------------------------+
10 rows in set (1.34 sec)

mysql> select VARIABLE_VALUE into @b from performance_schema.session_status where variable_name = 'Innodb_rows_read';
Query OK, 1 row affected (0.00 sec)

mysql> select @b-@a;
+---------+
| @b-@a   |
+---------+
| 3144071 |
+---------+
1 row in set (0.00 sec)

3、分別檢視執行計劃

mysql> explain select e.emp_no,(select max(s.salary) from salaries s where s.emp_no=e.emp_no) from employees e limit 10;
+----+--------------------+-------+------------+-------+---------------+---------+---------+--------------------+--------+----------+-------------+
| id | select_type        | table | partitions | type  | possible_keys | key     | key_len | ref                | rows   | filtered | Extra       |
+----+--------------------+-------+------------+-------+---------------+---------+---------+--------------------+--------+----------+-------------+
|  1 | PRIMARY            | e     | NULL       | index | NULL          | PRIMARY | 4       | NULL               | 299556 |   100.00 | Using index |
|  2 | DEPENDENT SUBQUERY | s     | NULL       | ref   | PRIMARY       | PRIMARY | 4       | employees.e.emp_no |      9 |   100.00 | NULL        |
+----+--------------------+-------+------------+-------+---------------+---------+---------+--------------------+--------+----------+-------------+
2 rows in set, 2 warnings (0.00 sec)

mysql> explain select * from v_test limit 10;
+----+--------------------+------------+------------+-------+---------------+---------+---------+--------------------+--------+----------+-------------+
| id | select_type        | table      | partitions | type  | possible_keys | key     | key_len | ref                | rows   | filtered | Extra       |
+----+--------------------+------------+------------+-------+---------------+---------+---------+--------------------+--------+----------+-------------+
|  1 | PRIMARY            | <derived2> | NULL       | ALL   | NULL          | NULL    | NULL    | NULL               | 299556 |   100.00 | NULL        |
|  2 | DERIVED            | e          | NULL       | index | NULL          | PRIMARY | 4       | NULL               | 299556 |   100.00 | Using index |
|  3 | DEPENDENT SUBQUERY | s          | NULL       | ref   | PRIMARY       | PRIMARY | 4       | employees.e.emp_no |      9 |   100.00 | NULL        |
+----+--------------------+------------+------------+-------+---------------+---------+---------+--------------------+--------+----------+-------------+
3 rows in set, 2 warnings (0.00 sec)

4、分析執行計劃:

兩個執行計劃中,唯一不同的是使用檢視後,多了一個派生表。

關於派生表說明如下:

https://dev.mysql.com/doc/refman/5.7/en/derived-tables.html

關於派生表官方優化

https://dev.mysql.com/doc/refman/5.7/en/derived-table-optimization.html

由於加了limit 10;第一個 SQL 雖然顯示 e 表掃描行數很多,但實際並沒有進行全表掃描,只統計了前10條記錄便停止了。

第二個 SQL 雖然也加了 limit 10,但因為優化器產生了派生表,也就是將統計SQL結果都寫入到一個臨時表中,再到這個臨時表中去讀10條記錄。

官方雖然有派生表合併優化功能,但對於派生表中包含聚合函式,group by ,having , count ,limit 等,就無法進行優化。

目前解決這種問題,應該只有一個辦法 ,就是別用檢視。