Mysql第七天查詢優化2
接上文,其他的一些Mysql對於查詢的優化,或者Mysql的不足,我們應該注意怎樣優化。
關聯子查詢
Mysql的子查詢實現比較有問題, 特別是對於IN(子查詢),這樣的方式。
比如:查詢一個訂單表中,所有支付方式為4也就是京東網銀錢包的訂單:
EXPLAIN SELECT * FROM virtual_order vo WHERE vo.jd_order_id IN (SELECT jd_order_id FROM biz_pay_task p WHERE p.pay_type=4);
// 執行結果:
+—-+——————–+——-+—————–+—————+————-+———+——+——+————-+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+—-+——————–+——-+—————–+—————+————-+———+——+——+————-+
| 1 | PRIMARY | vo | ALL | NULL | NULL | NULL | NULL | 50 | Using where |
| 2 | DEPENDENT SUBQUERY | p | unique_subquery | jd_order_id | jd_order_id | 8 | func | 1 | Using where |
+—-+——————–+——-+—————–+—————+————-+———+——+——+————-+
實際上,上面的sql會被轉化為:
SELECT * FROM virtual_order vo WHERE EXISTS (SELECT jd_order_id FROM biz_pay_task p WHERE p.pay_type=4 AND vo.jd_order_id=p.jd_order_id);
這意味著,先對vo進行了全表掃描,然後再對每一個jd_order_id去執行子查詢,看是否返回true
EXISTS關鍵字決定了 其子查詢是否能夠有返回結果。
這種情況下,我們期望怎樣執行呢?當然是先執行子查詢,有結果了之後利用IN的二分查詢來加快檢索速度。
這樣的話,我們就可以使用GROUP_CONCAT()來手工拼一個IN裡面的內容,
或者改寫為如下的內連線:
SELECT * FROM virtual_order vo INNER JOIN biz_pay_task p USING(jd_order_id) WHERE p.pay_type=4 ;
但是關聯子查詢並不是一定比左連,內聯等慢的。
比如NOT EXISTS的情況下,會有終止返回的優化,還是有可能變得比較快。 針對於這種,最好的方式就是進行測試,得到實際的執行結果,來進行判斷。
UNION ALL的限制
(SQL1) UNION ALL (SQL2) LIMIT 20;
這種情況會把 SQL1和SQL2中的所有記錄放到臨時表中然後去LIMIT20.比較好的做法是。分別LIMIT 如下:
(SQL1 LIMIT 20) UNION ALL (SQL2 LIMIT 20) LIMIT 20;
等值傳遞
當遇到ON, USING的時候會誘發等值傳遞的優化,但是如果要傳遞的是IN(),或者其他比較複雜的等值時,有可能傳遞給內層之後影響內層的效能。
不能並行執行
沒有雜湊關聯
可以使用自建雜湊欄位加索引
鬆散索引掃描
也就是跳著掃描。比如有一個聚合索引(a, b)如果WHERE中只有b的條件,那麼因為用不到這個索引,就會變成全表掃描。 我們期望的是先從a的索引開始掃描,然後對於a的沒一個索引鍵中因為b是有序的,因此這個時候又可以對於每個鍵中的b的區間範圍進行索引掃描。比如如下的結構:
|a|b|
|1|1|
|1|2|
|1|3|
|1|4|
|2|1|
|2|2|
|2|3|
|2|4|
WHERE b between 2 and 3 掃描方式變為a1-b2-b3-a2-b2-b3,這樣跳著掃描可以減少掃描行數,大幅度增加效能。
Mysql的主要應用掉在於GROUP BY 的語句中。Extra欄位顯示”Using index for group-by”
這裡有一個更好的說明: 請點選
最大值和最小值優化
SELECT MIN(id) from user WHERE name=”wzj”;
這裡會做全表掃描。 但是如果是從主鍵開始掃描的話,可以快很多。Mysql自己不能完成這種行為,但是我們可以手工的完成它,比如使用如下的語句:
SELECT id FROM user USE INDEX(PRIMARY) WHERE name=”wzj” LIMIT 1;
查詢優化器的提示 hint
主要是認為的控制一下Mysql的優化過程,列舉一些,更具體的需要看Mysql的官方文件了。
- DELAYED 插入資料放入緩衝區,提高對客戶端的響應
- STRAIGHT_JOIN 這個之前使用過,可以強制指定驅動表
- SQL_SMALL_RESULT 和SQL_BIG_RESULT 告訴優化器對於SELECT GROUPBY這種的返回集的大概大小,如果小則用記憶體而不用臨時表
- SQL_CACHE 告訴優化器這個結果存在快取中
- USE INDEX 這個告訴優化器使用或者不適用哪些索引
- 還有一些配置項,之前也已經講過了,有深度太多跳過選擇執行計劃的等等。
另外隨著版本的更新可能會引起不一樣的行為,因此最好每次升級都進行測試。可以使用Percona Tookit 的pt-upgrade工具來檢查結果。
優化特定型別的查詢
優化COUNT()
- COUNT(fieldname) 統計欄位中不為NULL的值
- COUNT(*) 統計所有行,這個的效能要好一些
- MYISAM之所以COUNT(*)快,是因為實時更新了總行數,但是對於加了WHERE的就不快了。
- 利用MYISAM很快的特性,對於WHERE b>5,可以使用COUNT(*) – WHERE b < 5的特性,這樣可以加快一些效能。
- 使用估算值,在特定場景下,可以使用去除DISTINCT,或者是WHERE後面對於結果集影響很小的條件,只求出估算值
- 更復雜的方式可以使用匯總表,索引覆蓋,Memcached這樣的外部快取。快速,精確和簡單,最多隻能滿足其二,所以優化肯定會有一個限度
優化關聯查詢
- 卻表ON和USING子句的列上有索引, 建立索引時考慮好關聯的順序,只需要在驅動表上建立索引
- 確保任何GROUP BY和ORDER BY 表示式只涉及一個表的列,這樣才能使用索引
- 注意不要使用全連,用內聯和外聯
優化子查詢
儘量用關聯代替
Mysql5.6中已經自帶了這種優化,因此可以不是很關注了
優化LIMIT分頁
- 利用覆蓋索引,先查出有索引的列,然後使用連線操作
- 再有是先記錄下當前查詢到的最後的位置,然後再下一次查詢是上WHERE子句過濾掉這個位置之前的值。比如SELECT * FROM user ORDER BY id DESC LIMIT 20;
這個時候得到最小的id是10000,那麼下面就可以使用SELECT * FROM user WHERE id < 10000 ORDER BY id DESC LIMIT 20;來得到下20行,這樣子來提升效率。 - 其他的方法,還可以使用冗餘表,只包含主鍵和需要排序的資料列。
分頁優化
- SQL_CALC_FOUND_ROWS 這個hint並不能真正的提高效率,其還是要統計所有的行的數量
- 使用EXPLAIN語句中的rows結果來做估算
- 讀取21條,看是否有第21條來決定是否有下一頁
- 讀取1000條,其他的資料使用更多按鈕來展示。
UNION優化
- 把優化的語句放到各個子查詢中
-
確定只有必須要去除重複行的時候採用UNION,否則使用UNION ALL,免得對所有結果進行DISTINCT
靜態查詢分析
Percona 的pt-query-advisor
例子
構建佇列表
mysql並不是最好的選擇,一般會有如下兩個問題要解決:
- 增長。 這種可以使用歸檔的方式
- 併發。 即有可能多個使用者操作同一個任務,為了避免種種情況,可以加鎖,但是加鎖效率比較低。
對於增長可以歸檔,可以分片。
對於併發,我們可以使用樂觀鎖
或者使用如下的優化,我們一般基本的做法可以是這個樣子的:給任務表增加一個woner欄位。 然後執行任務的時候:
BEGIN;
SELECT id FROM task WHERE owner = 0 AND status=0 LIMIT 10 FOR UPDATE;
-- result 1,2
// 程式碼中dosomething.
UPDATE task SET status=1 , owner = CONNECTON_ID() WHERE id IN (--result);
COMMIT;
這樣會造成使用相同索引的相同查詢有鎖定, 可以使用如下的方式代替:
BEGTIN;
UPDATE task set status=1 , owner=CONNECTION_ID() WHERE owner = 0 AND status=0 LIMIT 10;
COMMIT;
SET AUTOCOMMIT = 0;
SELECT id FROM task where owner = CONNECTION_ID() AND status = 1;
// do something
這種可以大幅的減少鎖的時間,但是後續的處理可能會比較多,比如如果執行具體的事情失敗,則還需要回寫內容。
上面的這種也稍顯複雜,最好還是使用悲觀鎖。
或者是直接使用redis,是一個更好的佇列容器。
查詢兩點之間的距離
這個主要是需要注意,什麼情況下使用索引。顯然轉化為矩形的情況下會使用索引。 使用矩形得出曉得資料集之後再進行精確計算。
相關文章
- MySQL查詢優化MySql優化
- MySQL優化COUNT()查詢MySql優化
- MySQL 的查詢優化MySql優化
- MySQL 慢查詢優化MySql優化
- mysql查詢優化檢查 explainMySql優化AI
- MySQL調優之查詢優化MySql優化
- MySQL索引與查詢優化MySql索引優化
- MySQL查詢優化利刃-EXPLAINMySql優化AI
- MySQL分頁查詢優化MySql優化
- MySQL-效能優化-索引和查詢優化MySql優化索引
- MySQL——優化巢狀查詢和分頁查詢MySql優化巢狀
- 十七、Mysql之SQL優化查詢MySql優化
- 【資料庫】MySQL查詢優化資料庫MySql優化
- Mysql 慢查詢優化實踐MySql優化
- MySQL: 使用explain 優化查詢效能MySqlAI優化
- mysql查詢效能優化總結MySql優化
- MySQL分優化之超大頁查詢MySql優化
- MySQL 索引及查詢優化總結MySql索引優化
- [玩轉MySQL之六]MySQL查詢優化器MySql優化
- 查詢優化優化
- 《MySQL慢查詢優化》之SQL語句及索引優化MySql優化索引
- MySQL查詢中分頁思路的優化BFMySql優化
- Mysql優化系列之——優化器對子查詢的處理MySql優化
- pgsql查詢優化之模糊查詢SQL優化
- MySQL基本知識點梳理和查詢優化MySql優化
- HBase查詢優化優化
- Oracle in 查詢優化Oracle優化
- join 查詢優化優化
- PostgreSQL 原始碼解讀(17)- 查詢語句#2(查詢優化基礎)SQL原始碼優化
- 從理論到實踐,Mysql查詢優化剖析MySql優化
- 優化-mysql子查詢索引失效問題解決優化MySql索引
- MySql常用30種SQL查詢語句優化方法MySql優化
- mysql 表資料量大量查詢慢如何優化MySql優化
- mysql查詢太慢,我們如何進行效能優化?MySql優化
- EntityFramework優化:查詢WITH(NOLOCK)Framework優化
- EntityFramework優化:查詢效能Framework優化
- 優化sql查詢速度優化SQL
- 分頁查詢優化優化
- KunlunDB 查詢優化(一)優化