AVt天堂网 手机版,亚洲va久久久噜噜噜久久4399,天天综合亚洲色在线精品,亚洲一级Av无码毛片久久精品

當前位置:首頁 > 科技  > 軟件

七個常見的SQL慢查詢問題,及其解決方法

來源: 責編: 時間:2024-05-24 17:20:31 171觀看
導讀得益于摩爾定律,計算機性能已大幅提升,加上數據庫的進步以及微服務所倡導的各種反模式設計。因此,我們現在編寫復雜SQL查詢的機會越來越少。業界(是的,甚至包括谷歌)已經開始提倡不要進行專門的SQL優化,因為節省下來的資源

h0V28資訊網——每日最新資訊28at.com

得益于摩爾定律,計算機性能已大幅提升,加上數據庫的進步以及微服務所倡導的各種反模式設計。因此,我們現在編寫復雜SQL查詢的機會越來越少。業界(是的,甚至包括谷歌)已經開始提倡不要進行專門的SQL優化,因為節省下來的資源并不足以抵消員工的工資成本。但是,作為工程師,我們應該在技術上努力追求卓越,成為本領域的頂尖科學家。h0V28資訊網——每日最新資訊28at.com

在這里,將介紹7個常見的SQL慢查詢語句,并解釋如何優化它們的性能。希望這對你有所幫助。h0V28資訊網——每日最新資訊28at.com

由DALLE-3生成由DALLE-3生成h0V28資訊網——每日最新資訊28at.com

1. LIMIT語句

分頁是最常用的方案之一,但也容易出現問題。例如,對于以下簡單的語句,DBA通常建議的解決方案是添加一個包含type、name和create_time字段的復合索引。這樣,條件和排序就可以有效利用索引,從而顯著提高性能。h0V28資訊網——每日最新資訊28at.com

SELECT *FROM   operationWHERE  type = 'SQLStats'       AND name = 'SlowLog'ORDER  BY create_timeLIMIT  1000, 10;

這可能會解決90%以上DBA的問題。但是,當LIMIT子句變成“LIMIT 1000000, 10”時,程序員仍會抱怨“為什么在只查詢10條記錄的時候,速度還這么慢?” 要知道,數據庫不知道第1000000條記錄從何處開始,所以即使有索引,它仍需要從頭開始計算。在大多數情況下,這個性能問題是由于懶惰編程造成的。h0V28資訊網——每日最新資訊28at.com

在前端數據瀏覽或批量導出大量數據的場景中,可以使用上一頁的最大值作為查詢參數。SQL可以重新設計如下:h0V28資訊網——每日最新資訊28at.com

SELECT   *FROM     operationWHERE    type = 'SQLStats'AND      name = 'SlowLog'AND      create_time > '2017-03-16 14:00:00'ORDER BY create_timeLIMIT    10;

采用這種新設計后,查詢時間保持不變,不會隨著數據量的增加而變化。h0V28資訊網——每日最新資訊28at.com

2. 隱式轉換

SQL語句中另一個常見的錯誤是查詢變量和字段定義的類型不匹配。以下面的語句為例:h0V28資訊網——每日最新資訊28at.com

mysql> explain extended SELECT *     > FROM   my_balance b     > WHERE  b.bpn = 14000000123     >       AND b.isverified IS NULL ;mysql> show warnings;| Warning | 1739 | Cannot use ref access on index 'bpn' due to type or collation conversion on field 'bpn'

在這種情況下,字段bpn被定義為varchar(20),而MySQL的策略是在比較之前將字符串轉換為數字。這會導致函數被應用到表字段上,從而使索引失效。h0V28資訊網——每日最新資訊28at.com

這種情況可能是由應用程序框架自動填充參數造成的,而不是程序員的本意。如今,應用程序框架通常都很復雜,雖然它們提供了便利,但也可能帶來隱患。h0V28資訊網——每日最新資訊28at.com

3. 連接更新和刪除

盡管MySQL 5.6引入了物化,但它只優化了SELECT語句。對于UPDATE或DELETE語句,需要使用JOIN手動重寫。h0V28資訊網——每日最新資訊28at.com

例如,請看下面的UPDATE語句。MySQL實際上執行了一個循環/嵌套子查詢(DEPENDENT SUBQUERY),執行時間可想而知。h0V28資訊網——每日最新資訊28at.com

UPDATE operation oSET    status = 'applying'WHERE  o.id IN (SELECT id                FROM   (SELECT o.id,                               o.status                        FROM   operation o                        WHERE  o.group = 123                               AND o.status NOT IN ( 'done' )                        ORDER  BY o.parent,                                  o.id                        LIMIT  1) t);

執行計劃如下:h0V28資訊網——每日最新資訊28at.com

+----+--------------------+-------+-------+---------------+---------+---------+-------+------+-----------------------------------------------------+| id | select_type        | table | type  | possible_keys | key     | key_len | ref   | rows | Extra                                               |+----+--------------------+-------+-------+---------------+---------+---------+-------+------+-----------------------------------------------------+| 1  | PRIMARY            | o     | index |               | PRIMARY | 8       |       | 24   | Using where; Using temporary                        || 2  | DEPENDENT SUBQUERY |       |       |               |         |         |       |      | Impossible WHERE noticed after reading const tables || 3  | DERIVED            | o     | ref   | idx_2,idx_5   | idx_5   | 8       | const | 1    | Using where; Using filesort                         |+----+--------------------+-------+-------+---------------+---------+---------+-------+------+-----------------------------------------------------+

將其重寫為JOIN后,子查詢的選擇類型從DEPENDENT SUBQUERY變為DERIVED,執行時間顯著得從7秒縮短到2毫秒。h0V28資訊網——每日最新資訊28at.com

UPDATE operation o       JOIN  (SELECT o.id,                            o.status                     FROM   operation o                     WHERE  o.group = 123                            AND o.status NOT IN ( 'done' )                     ORDER  BY o.parent,                               o.id                     LIMIT  1) t         ON o.id = t.idSET    status = 'applying';

簡化后的執行計劃如下:h0V28資訊網——每日最新資訊28at.com

+----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------------------------------------+| id | select_type | table | type | possible_keys | key   | key_len | ref   | rows | Extra                                               |+----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------------------------------------+| 1  | PRIMARY     |       |      |               |       |         |       |      | Impossible WHERE noticed after reading const tables || 2  | DERIVED     | o     | ref  | idx_2,idx_5   | idx_5 | 8       | const | 1    | Using where; Using filesort                         |+----+-------------+-------+------+---------------+-------+---------+-------+------+-----------------------------------------------------+

4. 混合排序

MySQL無法利用索引進行混合排序。但是,在某些場景下,仍然可以使用特殊方法來提高性能。h0V28資訊網——每日最新資訊28at.com

SELECT *FROM   my_order o       INNER JOIN my_appraise a ON a.orderid = o.idORDER  BY a.is_reply ASC,          a.appraise_time DESCLIMIT  0, 20;

執行計劃顯示的是全表掃描:h0V28資訊網——每日最新資訊28at.com

+----+-------------+-------+--------+-------------+---------+---------+---------------+---------+-+| id | select_type | table | type   | possible_keys     | key     | key_len | ref      | rows    | Extra+----+-------------+-------+--------+-------------+---------+---------+---------------+---------+-+|  1 | SIMPLE      | a     | ALL    | idx_orderid | NULL    | NULL    | NULL    | 1967647 | Using filesort ||  1 | SIMPLE      | o     | eq_ref | PRIMARY     | PRIMARY | 122     | a.orderid |       1 | NULL           |+----+-------------+-------+--------+---------+---------+---------+-----------------+---------+-+

由于is_reply只有0和1兩種狀態,我們可以將其重寫如下,從而將執行時間從1.58秒縮短到2毫秒:h0V28資訊網——每日最新資訊28at.com

SELECT *FROM   ((SELECT *         FROM   my_order o                INNER JOIN my_appraise a                        ON a.orderid = o.id                           AND is_reply = 0         ORDER  BY appraise_time DESC         LIMIT  0, 20)        UNION ALL        (SELECT *         FROM   my_order o                INNER JOIN my_appraise a                        ON a.orderid = o.id                           AND is_reply = 1         ORDER  BY appraise_time DESC         LIMIT  0, 20)) tORDER  BY  is_reply ASC,          appraisetime DESCLIMIT  20;

5. EXISTS語句

在處理EXISTS子句時,MySQL仍然使用嵌套子查詢進行執行。以下面的SQL語句為例:h0V28資訊網——每日最新資訊28at.com

SELECT *FROM   my_neighbor n       LEFT JOIN my_neighbor_apply sra              ON n.id = sra.neighbor_id                 AND sra.user_id = 'xxx'WHERE  n.topic_status < 4       AND EXISTS(SELECT 1                  FROM   message_info m                  WHERE  n.id = m.neighbor_id                         AND m.inuser = 'xxx')       AND n.topic_type <> 5;
+----+--------------------+-------+------+-----+------------------------------------------+---------+-------+---------+ -----+| id | select_type        | table | type | possible_keys | key     | key_len | ref      | rows    | Extra+----+--------------------+-------+------+ -----+------------------------------------------+---------+-------+---------+ -----+|  1 | PRIMARY            | n     | ALL  |  | NULL     | NULL    | NULL    | 1086041 | Using where                   ||  1 | PRIMARY            | sra   | ref  |  | idx_user_id | 123     | const |       1 | Using where          ||  2 | DEPENDENT SUBQUERY | m     | ref  |  | idx_message_info   | 122     | const |       1 | Using index condition; Using where |+----+--------------------+-------+------+ -----+------------------------------------------+---------+-------+---------+ -----+

通過刪除EXISTS子句并將其更改為JOIN, 我們可以避免嵌套子查詢,并將執行時間從1.93秒減少到1毫秒。h0V28資訊網——每日最新資訊28at.com

SELECT *FROM   my_neighbor n       INNER JOIN message_info m               ON n.id = m.neighbor_id                  AND m.inuser = 'xxx'       LEFT JOIN my_neighbor_apply sra              ON n.id = sra.neighbor_id                 AND sra.user_id = 'xxx'WHERE  n.topic_status < 4       AND n.topic_type <> 5;

新的執行計劃如下:h0V28資訊網——每日最新資訊28at.com

+----+-------------+-------+--------+ -----+------------------------------------------+---------+ -----+------+ -----+| id | select_type | table | type   | possible_keys | key   | key_len | ref   | rows | Extra |+----+-------------+-------+--------+ -----+------------------------------------------+---------+ -----+------+ -----+|  1 | SIMPLE      | m     | ref    | | idx_message_info   | 122     | const |    1 | Using index condition ||  1 | SIMPLE      | n     | eq_ref | | PRIMARY   | 122     | ighbor_id |    1 | Using where      ||  1 | SIMPLE      | sra   | ref    | | idx_user_id | 123     | const |    1 | Using where           |+----+-------------+-------+--------+ -----+------------------------------------------+---------+ -----+------+ -----+

6. 條件下推

在某些情況下,外部查詢條件無法下推到復雜的視圖或子查詢中:h0V28資訊網——每日最新資訊28at.com

  1. 聚合子查詢。
  2. 帶有LIMIT的子查詢。
  3. UNION或UNION ALL子查詢。
  4. 輸出字段中的子查詢。

請看下面的語句,其中的條件會影響聚合子查詢:h0V28資訊網——每日最新資訊28at.com

SELECT *FROM   (SELECT target,               Count(*)        FROM   operation        GROUP  BY target) tWHERE  target = 'rm-xxxx';
+----+-------------+------------+-------+---------------+-------------+---------+-------+------+-------------+| id | select_type | table      | type  | possible_keys | key         | key_len | ref   | rows | Extra       |+----+-------------+------------+-------+---------------+-------------+---------+-------+------+-------------+|  1 | PRIMARY     | n          | ALL   | NULL          | NULL        | NULL    | NULL  | 1086041 | Using where ||  1 | PRIMARY     | sra        | ref   | NULL          | idx_user_id | 123     | const |    1 | Using where ||  2 | DEPENDENT SUBQUERY | m | ref   | NULL          | idx_message_info   | 122     | const |    1 | Using index condition; Using where |+----+-------------+------------+-------+---------------+-------------+---------+-------+------+-------------+

通過刪除EXISTS子句并將其更改為JOIN,我們可以避免嵌套子查詢并將執行時間從1.93秒減少到1毫秒。h0V28資訊網——每日最新資訊28at.com

SELECT *FROM   my_neighbor n       INNER JOIN message_info m               ON n.id = m.neighbor_id                  AND m.inuser = 'xxx'       LEFT JOIN my_neighbor_apply sra              ON n.id = sra.neighbor_id                 AND sra.user_id = 'xxx'WHERE  n.topic_status < 4       AND n.topic_type <> 5;

新的執行計劃如下:h0V28資訊網——每日最新資訊28at.com

+----+-------------+-------+--------+ -----+------------------------------------------+---------+ -----+------+ -----+| id | select_type | table | type   | possible_keys | key   | key_len | ref   | rows | Extra |+----+-------------+-------+--------+ -----+------------------------------------------+---------+ -----+------+ -----+|  1 | SIMPLE      | m     | ref    | | idx_message_info   | 122     | const |    1 | Using index condition ||  1 | SIMPLE      | n     | eq_ref | | PRIMARY   | 122     | ighbor_id |    1 | Using where      ||  1 | SIMPLE      | sra   | ref    | | idx_user_id | 123     | const |    1 | Using where           |+----+-------------+-------+--------+ -----+------------------------------------------+---------+ -----+------+ -----+

7. 提前縮小范圍

讓我們看看以下經過部分優化的示例(左連接中的主表作為主查詢條件):h0V28資訊網——每日最新資訊28at.com

SELECT    a.*,          c.allocatedFROM      (              SELECT   resourceid              FROM     my_distribute d                   WHERE    isdelete = 0                   AND      cusmanagercode = '1234567'                   ORDER BY salecode limit 20) aLEFT JOIN          (              SELECT   resourcesid, sum(ifnull(allocation, 0) * 12345) allocated              FROM     my_resources                   GROUP BY resourcesid) cON        a.resourceid = c.resourcesid;

這條語句是否還存在其他問題?很明顯,子查詢c是對整個表進行聚合查詢,在處理大量表時可能會導致性能下降。h0V28資訊網——每日最新資訊28at.com

事實上,對于子查詢c,左連接的結果集只關心可以與主表的resourceid匹配的數據。因此,我們可以將語句重寫如下,將執行時間從2秒減少到2毫秒:h0V28資訊網——每日最新資訊28at.com

SELECT    a.*,          c.allocatedFROM      (                   SELECT   resourceid                   FROM     my_distribute d                   WHERE    isdelete = 0                   AND      cusmanagercode = '1234567'                   ORDER BY salecode limit 20) aLEFT JOIN          (                   SELECT   resourcesid, sum(ifnull(allocation, 0) * 12345) allocated                   FROM     my_resources r,                            (                                     SELECT   resourceid                                     FROM     my_distribute d                                     WHERE    isdelete = 0                                     AND      cusmanagercode = '1234567'                                     ORDER BY salecode limit 20) a                   WHERE    r.resourcesid = a.resourcesid                   GROUP BY resourcesid) cON        a.resourceid = c.resourcesid;

然而,子查詢a在我們的SQL語句中出現了多次。這種方法不僅會產生額外的成本,而且也會使語句變得更加復雜。我們可以使用WITH語句來簡化它:h0V28資訊網——每日最新資訊28at.com

WITH a AS(         SELECT   resourceid         FROM     my_distribute d         WHERE    isdelete = 0         AND      cusmanagercode = '1234567'         ORDER BY salecode limit 20)SELECT    a.*,          c.allocatedFROM      aLEFT JOIN          (                   SELECT   resourcesid, sum(ifnull(allocation, 0) * 12345) allocated                   FROM     my_resources r,                            a                   WHERE    r.resourcesid = a.resourcesid                   GROUP BY resourcesid) cON        a.resourceid = c.resourcesid;

結論

數據庫編譯器生成的執行計劃決定了SQL語句的實際執行方式。但是,編譯器只能盡力提供服務,沒有一個數據庫編譯器是完美的。上述情況在其他數據庫中也同樣存在。了解了數據庫編譯器的特性,我們就能繞過它的限制,編寫出高性能的SQL語句。h0V28資訊網——每日最新資訊28at.com

在設計數據模型和編寫SQL語句時,將算法思維或算法意識引入到這個過程非常重要。在編寫復雜的SQL語句時,養成使用WITH語句的習慣可以簡化語句,減輕數據庫的負擔。h0V28資訊網——每日最新資訊28at.com

最后,下面是SQL語句的執行順序:h0V28資訊網——每日最新資訊28at.com

FROMON JOINWHEREGROUP BYHAVINGSELECTDISTINCTORDER BYLIMIT


h0V28資訊網——每日最新資訊28at.com

本文鏈接:http://www.tebozhan.com/showinfo-26-90655-0.html七個常見的SQL慢查詢問題,及其解決方法

聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 20 種不同并發模型示例,帶你深入理解并發模型

下一篇: 水下數據中心的殺手:聲波攻擊

標簽:
  • 熱門焦點
Top