在MySQL中,咱們能夠將NOT EXISTS語句轉換爲LEFT JOIN語句來進行優化,哪爲何會有性能提高呢?性能
使用NOT EXISTS方式SQL爲:優化
SELECT count(1) FROM t_monitor m WHERE NOT exists (SELECT 1 FROM t_alarm_realtime AS a WHERE a.resource_id=m.resource_id AND a.resource_type=m.resource_type AND a.monitor_name=m.monitor_name)
而使用LEFT JOIN方式SQL爲:spa
SELECT count(1) FROM t_monitor m LEFT JOIN t_alarm_realtime AS a ON a.resource_id=m.resource_id AND a.resource_type=m.resource_type AND a.monitor_name=m.monitor_name WHERE a.resource_id is NULL
從查詢效果來看,NOT EXISTS 方式耗時29.38秒,而LEFT JOIN方式耗時1.20秒,性能提高25倍左右。code
查看NOT EXISTS方式的執行計劃:blog
*************************** 1. row *************************** id: 1 select_type: PRIMARY table: m partitions: NULL type: index possible_keys: NULL key: idx_id_name_type key_len: 119 ref: NULL rows: 578436 filtered: 100.00 Extra: Using where; Using index *************************** 2. row *************************** id: 2 select_type: DEPENDENT SUBQUERY table: a partitions: NULL type: eq_ref possible_keys: idx_id_name_type key: idx_id_name_type key_len: 119 ref: cmdb.m.resource_id,cmdb.m.monitor_name,cmdb.m.resource_type rows: 1 filtered: 100.00 Extra: Using index
查看LEFT JOIN方式的執行計劃:索引
*************************** 1. row *************************** id: 1 select_type: SIMPLE table: m partitions: NULL type: index possible_keys: NULL key: idx_id_name_type key_len: 119 ref: NULL rows: 578436 filtered: 100.00 Extra: Using index *************************** 2. row *************************** id: 1 select_type: SIMPLE table: a partitions: NULL type: eq_ref possible_keys: idx_id_name_type key: idx_id_name_type key_len: 119 ref: cmdb.m.resource_id,cmdb.m.monitor_name,cmdb.m.resource_type rows: 1 filtered: 100.00 Extra: Using where; Not exists; Using index
使用SQL PROFILE查看NOT EXISTS 執行過程:cmd
+--------------------+----------+----------+------------+-------------------+---------------------+--------------+---------------+ | Status | Duration | CPU_user | CPU_system | Context_voluntary | Context_involuntary | Block_ops_in | Block_ops_out | +--------------------+----------+----------+------------+-------------------+---------------------+--------------+---------------+ | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000022 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000028 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000022 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000029 | 0.001000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000022 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000027 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000028 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000026 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000025 | 0.000000 | 0.000999 | 0 | 0 | 0 | 0 | | Sending data | 0.000031 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000028 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000027 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000027 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000027 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000027 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000027 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000027 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000027 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000028 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000027 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000027 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000022 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000027 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000027 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000023 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 0.000033 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | end | 0.000024 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | query end | 0.000028 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | closing tables | 0.000027 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | freeing items | 0.000039 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | logging slow query | 0.000059 | 0.000000 | 0.000000 | 0 | 0 | 0 | 16 | | cleaning up | 0.000033 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | +--------------------+----------+----------+------------+-------------------+---------------------+--------------+---------------+
使用SQL PROFILE查看LEFT JOIN 執行過程:it
+----------------------+----------+----------+------------+-------------------+---------------------+--------------+---------------+ | Status | Duration | CPU_user | CPU_system | Context_voluntary | Context_involuntary | Block_ops_in | Block_ops_out | +----------------------+----------+----------+------------+-------------------+---------------------+--------------+---------------+ | starting | 0.000162 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | checking permissions | 0.000025 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | checking permissions | 0.000025 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Opening tables | 0.000033 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | init | 0.000049 | 0.001000 | 0.000000 | 0 | 0 | 0 | 0 | | System lock | 0.000030 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | optimizing | 0.000033 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | statistics | 0.000050 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | preparing | 0.000037 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | executing | 0.000025 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | Sending data | 1.200899 | 1.547764 | 0.124981 | 7460 | 116 | 0 | 8608 | | end | 0.000103 | 0.000000 | 0.000000 | 2 | 0 | 0 | 0 | | query end | 0.000028 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | closing tables | 0.000028 | 0.000000 | 0.000000 | 0 | 0 | 0 | 0 | | freeing items | 0.000039 | 0.000000 | 0.000000 | 2 | 0 | 0 | 8 | | logging slow query | 0.000052 | 0.000000 | 0.000000 | 0 | 0 | 0 | 24 | | cleaning up | 0.000030 | 0.000000 | 0.000000 | 1 | 0 | 0 | 0 | +----------------------+----------+----------+------------+-------------------+---------------------+--------------+---------------+
兩種執行方式對比:io
一、從執行計劃來看,兩個表都使用了索引,區別在於NOT EXISTS使用「DEPENDENT SUBQUERY」方式,而LEFT JOIN使用普通表關聯的方式table
二、從執行過程來看,LEFT JOIN方式主要消耗Sending data的上,在NOT EXISTS方式主要消耗在"executing"和「Sending data」兩項上,受限於PROFILE只能記錄100行結果,所以超過57萬個"executing"和「Sending data」的組合項沒有顯示,雖然每次"executing"和「Sending data」的組合項消耗時間較少(約50毫秒),但因爲執行次數較高,致使最終執行時間較長(50μs*578436=28921800us=28.92s)
如何在NOT EXISTS和LEFT JOIN中選擇:
一、當外層數據較少時,子查詢循環次數較少,使用NOT EXISTS並不會致使嚴重的性能問題,推薦使用NOT EXISTS方式。
二、當外層數據較大時,子查詢消耗隨外層數據量遞增,查詢性能較差,推薦使用LEFT JOIN方式
總結:
按照存在即合理是客觀惟心主義的理論,NOT EXISTS以更直觀地方式實現業務需求,在SQL複雜度上要遠低於LEFT JOIN,且在生產執行計劃時,NOT EXISTS方式相對更穩定些,LEFT JOIN可能會隨統計信息變化而生產不一樣的執行計劃。