1.union執行過程mysql
首先咱們建立一個表t1算法
create table t1(id int primary key, a int, b int, index(a)); delimiter ;; create procedure idata() begin declare i int; set i=1; while(i<=1000)do insert into t1 values(i, i, i); set i=i+1; end while; end;; delimiter ; call idata();
而後咱們執行一下這條語句sql
explain select 1000 as f union (select id from t1 order by id desc limit 2)
首先說下union的語義,union的語義是取兩個結果的並集,重複的保留一行,而後咱們來看下explain的結果,第二行的key=PRIMARY,說明用到了主鍵索引。mysql優化
第三行的Extra的Using temporary說明用到了臨時表優化
下面咱們看下這條語句的執行流程:spa
1.建立一個臨時表,只有f一個字段,且爲主鍵code
2.將1000這個數據插入臨時表blog
3.子查詢中步驟:排序
1.插入1000進入臨時表,由於主鍵衝突,插入失敗索引
2.插入第二行900,插入成功
4.將臨時表數據做爲結果返回,並刪除臨時表
這個過程的流程圖以下:
若是咱們把union改爲union all,就不須要使用臨時表了,由於union all是重複的也保留,
你們能夠看到extra這一列已經沒有了Using temporary
explain select 1000 as f union all (select id from t1 order by id desc limit 2)
2.group by執行過程
咱們來看下面這條語句:
explain select id%10 as m, count(*) as c from t1 group by m;
能夠看到explain結果
Using index(使用到了覆蓋索引a,不須要回表); Using temporary(用到了臨時表); Using filesort(對數據進行了排序)
這條語句的意思是將id%10進行分組統計,並按照m進行排序
執行流程以下:
1.建立臨時表,增長m,c字段,m是主鍵
2.計算id%10的結果記爲x
3.若是臨時表裏面沒有主鍵爲x的行,則插入(x,1),若是有的話,就將該行的c值加1
4.遍歷完成後,按照m字段排序返回結果給客戶端
流程圖以下
接下來咱們看下這條語句的執行結果
explain select id%10 as m, count(*) as c from t1 group by m
其實,若是咱們不須要對查詢結果進行排序,咱們能夠加一個order by null
咱們執行一下這條語句
explain select id%10 as m, count(*) as c from t1 group by m order by null
能夠看到這裏沒有進行排序,因爲掃描是從表t的id是從1開始的,因此第一行是1
若是咱們執行下列語句,會發生什麼呢?
咱們上面說的臨時表,實際上是內存臨時表,若是咱們把內存臨時表的容量改的比咱們要查詢的數據的容量小,那麼就會使用到磁盤臨時表,磁盤臨時表的默認引擎是innodb
et tmp_table_size=1024; select id%100 as m, count(*) as c from t1 group by m order by null limit 10
group by 優化方法--直接排序
其實在上面的關於從內存臨時錶轉化成磁盤臨時表是很浪費時間的,也就是說mysql,在執行過程當中發現空間不夠了,在轉成磁盤臨時表,可是若是咱們直接告訴mysql,我要查詢的數據很大,那麼mysql優化器就會想到,既然你告訴我數據很大,那麼我就直接用sort_buffer進行排序,若是sort_buffer內存不夠大,會用到磁盤臨時表輔助排序。
select SQL_BIG_RESULT id%100 as m, count(*) as c from t1 group by m;
小結一下:
1.若是咱們不須要對統計結果進行排序,能夠加上order by null省去排序流程。
2.儘可能讓排序過程用上內存臨時表,能夠經過適當調大tmp_table_size的值來避免用到磁盤臨時表。
3.若是數據量實在太大,使用SQL_BIG_RESULT告訴優化器,直接使用排序算法。