(轉)僅供自己學習,特此轉發
普遍遇到的慢SQL有以下三種:
1.未走索引
2.where條件里包含子查詢,多表聯查
3.查詢大量數據
解決
一.索引:SQL中的高速公路
但凡優化SQL,首先要看的就是這條查詢是否走了索引。走索引的查詢和沒走索引的差距可謂雲泥之別。
可以看下面這個例子:
在一張大約3W數據量的用戶表中,兩種查詢方式在速度上的差距:
不走索引:
select * from kw_user_copy where new_id=1 時間: 0.321 s
走主鍵索引:
select * from kw_user_copy where id=1 時間: 0.002 s
執行時間上有着數百倍的差距。
這種差距如果放在一些大的嵌套中,譬如循環查詢500次,將成為非常致命的問題,甚至可能讓程序執行超時。
PS. 很多查詢條件也會導致SQL放棄索引而執行全表遍歷,譬如:
select id from item where num is null
這些細節也要引起注意。
實際項目中的例子:
select id as user_id, name, nickname, photo, status, sdk_key, sdk_status from kw_user where name = 'wallkop' AND password = '44209a6a592dea91bcf7d4dd53e47a5a' 時間: 0.247 s
這是一條非常常見的用戶登錄查詢。
直觀看起來,這條SQL似乎寫的非常完善了,根據name和password去查詢相關用戶的信息,怎么看都沒有優化的余地了。
我們也知道:name和password作為兩個string字段,通常是不會建立索引的,也就是說,這是一條必然不走索引的查詢。
這種查詢就沒有優化余地了嗎?
非也。
下面就是一個簡單的優化:
select id from kw_user where name = 'wallkop' AND password = '44209a6a592dea91bcf7d4dd53e47a5a' 時間: 0.060 s select id as user_id, name, nickname, photo, status, sdk_key, sdk_status from kw_user where id=37215 時間: 0.001 s 總耗時:0.061 s
將一條查詢語句拆成兩條,第一條不走索引的查詢,我們盡量去簡化它,只查一個id字段,你會驚奇的發現:速度居然提升了4倍。
而第二條查詢用戶詳細信息的SQL,我們走了主鍵索引,僅僅用了0.001s。
如此一來,兩條查詢加起來總耗時才0.061s,比之前快了4倍。
這就是索引的靈活運用之道。
二.子查詢(多表聯查)優化
子查詢速度慢的原因非常簡單:
主查詢遍歷多少條數據,就要執行多少遍子查詢。
簡單來說,一張只有50條數據的表,普通查詢和復雜子查詢是沒什么速度差異的,但當數據量級達到幾萬甚至幾十萬的時候,這個差距就會非常明顯。
分析一條SQL語句來說明:
select id from kw_question where game_id=2 AND status=2 AND id in (select question_id from kw_answer where question_id = kw_question.id AND answer like "%瑞文%"); 時間: 0.063 s
在這個語句中,where末端是一個非常坑爹的模糊文字子查詢
但乍一看這條SQL速度似乎也不算太慢,其原因就在於:game_id=2和status=2這兩個查詢條件大幅度縮小了需要查詢的數據集范圍。
這就給我們了一個優化思路:
如果我能在執行子查詢前,盡可能的縮減它的“數據范圍”,不就可以提高查詢效率了么?
分析一個實際項目里的案例,根據數據特點突破了SQL速度的極限:
select id as question_id, question, game_id, modify_time, has_attachment, status from kw_question a where game_id=2 AND status in (0,1,2) AND not exists( select id from kw_answer where question_id=a.id and status != -1) order by a.create_time desc limit 0,20 時間: 3.160 s
上述SQL是一個問題列表頁的查詢語句,業務要求是把符合where條件的零回答數據篩選出來。
其實對於這種查詢,最簡單高效的方式是在字段里加一個answer_num,手動記錄每個問題下的答案數量。
但是由於相關業務比較復雜,涉及審核、關閉、二次編輯等流程,最終項目組放棄了這個字段的使用。
所依在kw_question表缺乏相關answer_num字段的情況下,查詢一個“零回答”的問題,就得去查其相關聯的表:kw_answer。
這就由一個簡單的單表查詢變成了多表聯查,大幅度增加了時間性能損耗。
平心而論,單純從SQL的角度來講,這條SQL已經沒啥能優化的了,在無法使用answer_num這個字段的情況下,它已經寫的蠻不錯了。
然而3.16秒的速度真的讓人無法接受。
於是開始思考:
首先EXPLAIN分析,發現主查詢中rows多達157269條,難怪這么慢。
結合“零回答”這個特點仔細想了想,發現某個問題一旦有了至少一個答案,就徹底擺脫了0回答,通常來講這個過程是不可逆的。
這就導致了這15W次查詢中,大部分查詢是廢查詢,因為你知道那些數據根本不會變動,但SQL還是把它們全部遍歷了一次。
三.大量數據查詢優化
1、首先分析有沒有用到索引
2、