mysql檢視產生派生表無法優化案例
阿新 • • 發佈:2021-11-04
環境: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 等,就無法進行優化。
目前解決這種問題,應該只有一個辦法 ,就是別用檢視。