簡體   English   中英

優化 MySQL 查詢,使用子查詢; MariaDB 快得多

[英]Optimise MySQL query, which uses sub-query; MariaDB much faster

我有以下查詢(帶子查詢):

SELECT  ROUND(SUM(cb.points)) AS points
    FROM  clients_bonuses cb
    WHERE  cb.cb_year = 2016
      AND  cb.account_id IN (
        SELECT  CONCAT('85500/',uc.contract)
            FROM  users_contracts uc , users_details ud
            WHERE  ud.id=uc.users_details_id
              AND  uc.platform_id = 1
              AND  ud.id=6  ) 

它在我的本地 MySQL 服務器 ( 10.1.9-MariaDB ) 上運行得非常好 ( 0.2 秒),但是,在我的生產 MySQL 服務器 ( 5.5.46-0ubuntu0.14.04.2 ) 上它需要35 秒 去完成。

我的本地數據庫是生產數據庫的精確副本,硬件配置僅因視頻適配器而異(本地具有內置英特爾顯卡)。

我的問題是 - 問題的原因可能是什么? 我可以(以及如何)優化此查詢嗎?

解釋上述查詢的結果(注意FirstMatch(cb); Using join buffer (flat, BNL join)的差異)

(生產服務器):

"id"    "select_type"   "table" "type"  "possible_keys" "key"   "key_len"   "ref"   "rows"  "Extra"
"1" "PRIMARY"   "cb"    "ALL"   \N  \N  \N  \N  "394886"    "Using where"
"2" "DEPENDENT SUBQUERY"    "ud"    "const" "PRIMARY"   "PRIMARY"   "4" "const" "1" "Using index"
"2" "DEPENDENT SUBQUERY"    "uc"    "ALL"   \N  \N  \N  \N  "12243" "Using where"

(本地機器):

"id"    "select_type"   "table" "type"  "possible_keys" "key"   "key_len"   "ref"   "rows"  "Extra"
"1" "PRIMARY"   "ud"    "const" "PRIMARY"   "PRIMARY"   "4" "const" "1" "Using index"
"1" "PRIMARY"   "cb"    "ALL"   \N  \N  \N  \N  "394537"    "Using where"
"1" "PRIMARY"   "uc"    "ALL"   \N  \N  \N  \N  "12238" "Using where; FirstMatch(cb); Using join buffer (flat, BNL join)"

表(由於此編輯器,從表列名稱中刪除了引號):

  1. users_contracts

    CREATE TABLE users_contracts ( id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, users_details_id INT(11) NOT NULL DEFAULT '0', platform_id INT(11) NOT NULL DEFAULT '0', contract VARCHAR(20) NOT NULL DEFAULT '', createdon TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', createdby INT(11) NOT NULL DEFAULT '0', updatedon TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', updatedby INT(11) NOT NULL DEFAULT '0', portfolio_id INT(11) NULL DEFAULT NULL, PRIMARY KEY (id) ) COLLATE='cp1251_general_ci' ENGINE=MyISAM AUTO_INCREMENT=13617;

  2. 客戶獎金

    CREATE TABLE clients_bonuses ( id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, account_id VARCHAR(255) NOT NULL DEFAULT '', bi_id INT(11) NOT NULL DEFAULT '0', cb_year YEAR NOT NULL DEFAULT '0000', cb_month TINYINT(1) NOT NULL DEFAULT '0', cb_day TINYINT(1) NOT NULL DEFAULT '0', bonus DECIMAL(10,4) NOT NULL DEFAULT '0.0000', amount DECIMAL(20,4) NOT NULL DEFAULT '0.0000', points DECIMAL(20,4) NOT NULL DEFAULT '0.0000', month_lots DECIMAL(10,4) NOT NULL DEFAULT '0.0000', PRIMARY KEY (id) ) COLLATE='cp1251_general_ci' ENGINE=MyISAM AUTO_INCREMENT=395015;

  3. 用戶詳細信息

    CREATE TABLE users_details ( id INT(11) UNSIGNED NOT NULL AUTO_INCREMENT, email VARCHAR(100) NOT NULL DEFAULT '', phone VARCHAR(32) NULL DEFAULT NULL, interest TINYINT(1) NOT NULL DEFAULT '0', contact TINYINT(1) NOT NULL DEFAULT '0', instrument TINYINT(1) NOT NULL DEFAULT '0', instrument1 TINYINT(1) NOT NULL DEFAULT '0', comments TEXT NOT NULL, reminder TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', reminder1 TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', last_accessed TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', dealer INT(11) NOT NULL DEFAULT '0', dealer1 INT(11) NOT NULL DEFAULT '0', real_client TINYINT(1) NOT NULL DEFAULT '0', real_client_meta TINYINT(1) NOT NULL DEFAULT '0', real_client_bgtrader TINYINT(1) NOT NULL DEFAULT '0', real_client_bmpro TINYINT(1) NOT NULL DEFAULT '0', advmails TINYINT(1) NOT NULL DEFAULT '0', analysis_status TINYINT(4) NULL DEFAULT '0', real_client_date TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', real_client_meta_date TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', real_client_bgtrader_date TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', real_client_bmpro_date TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', reg_status TINYINT(1) NOT NULL DEFAULT '0', password VARCHAR(100) NOT NULL DEFAULT '', real_name VARCHAR(100) NOT NULL DEFAULT '', date_of_first_points TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', trader_points INT(11) NOT NULL DEFAULT '0', metatrader_points INT(11) NOT NULL DEFAULT '0', bgtrader_points INT(11) NOT NULL DEFAULT '0', bmpro_points INT(11) NOT NULL DEFAULT '0', vps_status TINYINT(1) NOT NULL DEFAULT '0', vps_username VARCHAR(50) NOT NULL DEFAULT '', vps_password VARCHAR(50) NOT NULL DEFAULT '', vps_start_date TIMESTAMP NOT NULL DEFAULT '0000-00-00 00:00:00', metatrader5_points INT(11) NOT NULL DEFAULT '0', mt_points INT(11) NOT NULL DEFAULT '0', reminder_from INT(11) NOT NULL DEFAULT '0', reminder_mt4 DATE NULL DEFAULT NULL, taken_bonus DATE NOT NULL DEFAULT '0000-00-00', taken_bonus_from DATE NOT NULL DEFAULT '0000-00-00', experience INT(11) NOT NULL DEFAULT '0', invalid_phone TINYINT(4) NOT NULL DEFAULT '0', invalid_email TINYINT(4) NOT NULL DEFAULT '0', number_of_calls INT(11) NOT NULL DEFAULT '0', last_call DATETIME NULL DEFAULT NULL, PRIMARY KEY (id), UNIQUE INDEX email (email), INDEX users_details_email (email(30)) ) COLLATE='cp1251_general_ci' ENGINE=InnoDB AUTO_INCREMENT=63820;

您應該將ud.id=uc.users_details_id AND uc.platform_id = 1 AND ud.id= 6 從WHEREINNER JOIN

為了uc.users_details_id正常工作,為uc.users_details_id添加 INDEX ud.id 是主鍵,所以它已經被索引了。

IN ( SELECT ... )優化不佳。

將其轉換為JOIN可能會使聚合 ( SUM ) 膨脹。

FROM ( SELECT ... )消除了IN問題,但存在“膨脹”問題的風險。

所以, EXISTS ( SELECT * ... )可能是最好的答案:

SELECT  ROUND(SUM(cb.points)) AS points
    FROM  clients_bonuses cb
    WHERE  cb.cb_year = 2016
      AND  EXISTS 
      ( SELECT  *
            FROM  users_contracts uc
            JOIN  users_details ud  ON  ud.id = uc.users_details_id
            WHERE  uc.platform_id = 1
              AND  ud.id=6
              AND  CONCAT('85500/', uc.contract) = cb.account_id 
      ) 

您可以受益於:

users_contracts : INDEX(users_details_id, platform_id) -- in either order
clients_bonuses : INDEX(cb_year, account_id, points)  -- in that order

看起來您可以通過擺脫 users_details 進一步加快速度:

SELECT  ROUND(SUM(cb.points)) AS points
    FROM  clients_bonuses cb
    WHERE  cb.cb_year = 2016
      AND  EXISTS 
      ( SELECT  *
            FROM  users_contracts uc
            WHERE  uc.platform_id = 1
              AND  uc.users_details_id = 6
              AND  CONCAT('85500/', uc.contract) = cb.account_id 
      ) 

自 4.1 以來,這些公式可能適用於 MySQL/MariaDB 的所有變體。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM