【问题标题】:How to Optimize SELECT INTO OUTFILE query with LEFT JOIN in MySQL如何在 MySQL 中使用 LEFT JOIN 优化 SELECT INTO OUTFILE 查询
【发布时间】:2018-08-01 12:21:23
【问题描述】:

我在一个有 445 万行的表上运行下面的查询,并且查询需要 15-20 多分钟才能完成操作。我也尝试将引擎从 Innodb 更改为 MyISAM,但没有任何效果。我也尝试过添加多个类型为 normal 和 unique 的索引,但它仍然需要相同的时间。

这是我的查询:

SELECT 
a.source, a.destination, a.forward_to, a.start_epoch, a.end_epoch, a.duration, a.billsec, a.outbound_billsec, a.pool_id, a.group_id, a.cost, a.outbound_cost, a.net, a.keep, a.payin, a.payout, a.campaign_id, a.buyer, a.hangup_cause, a.endpoint_disposition, a.uuid, a.agreement, a.agreement_type, a.contract, a.contract_type, a.sip_received_ip,a.termination_ip, 
REPLACE(REPLACE(ifnull(b.line_type,''),'\n',' '),'\r',' ') AS line_type, 
REPLACE(REPLACE(ifnull(b.ocn,''),'\n',' '),'\r',' ') AS ocn, 
REPLACE(REPLACE(ifnull(b.spid_carrier_name,''),'\n',' '),'\r',' ') AS spid_carrier_name 
INTO OUTFILE '/tmp/test-husnain01' 
FIELDS TERMINATED BY ',' FROM inbound_022018 a 
LEFT JOIN wireless_checks b ON (a.uuid = b.uuid) 
WHERE date(a.start_epoch)='2018-02-19' AND 
a.endpoint_disposition='ANSWER' AND 
a.direction='inbound' AND 
a.billed=1;

下面是我的表结构(inbound_022018):

      CREATE TABLE `inbound_022018` (
        `id` int(11) NOT NULL AUTO_INCREMENT,
        `source` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
        `destination` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
        `prefix` int(22) NOT NULL,
        `forward_to` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
        `supplier` varchar(32) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
        `agreement` int(11) NOT NULL,
        `agreement_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
        `payout` float(11,4) NOT NULL,
        `pool_id` int(11) NOT NULL,
        `group_id` int(11) NOT NULL,
        `campaign_id` bigint(22) NOT NULL,
        `lead` int(1) NOT NULL,
        `cpl` float(11,4) NOT NULL,
        `buyer` varchar(32) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
        `contract` int(11) NOT NULL,
        `contract_type` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
        `payin` float(11,4) NOT NULL,
        `gross` float(11,4) NOT NULL,
        `cost` float(11,4) NOT NULL,
        `outbound_cost` float(11,4) NOT NULL,
        `net` float(11,4) NOT NULL,
        `keep` float(11,4) NOT NULL,
        `direction` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
        `session_id` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
        `uuid` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
        `sip_from_uri` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
        `sip_received_ip` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
        `domain_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
        `sip_req_uri` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
        `endpoint_disposition` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
        `hangup_cause` varchar(80) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
        `hangup_cause_q850` varchar(80) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
        `start_epoch` datetime DEFAULT NULL,
        `answer_epoch` datetime DEFAULT NULL,
        `bridge_epoch` datetime DEFAULT NULL,
        `progress_epoch` datetime DEFAULT NULL,
        `progress_media_epoch` datetime NOT NULL,
        `end_epoch` datetime NOT NULL,
        `digits_dialed` varchar(10) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
        `last_app` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
        `last_arg` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
        `duration` int(11) NOT NULL,
        `g30` int(1) DEFAULT NULL,
        `billsec` int(11) NOT NULL,
        `outbound_duration` int(11) NOT NULL,
        `outbound_billsec` int(11) NOT NULL,
        `progresssec` int(11) NOT NULL,
        `answersec` int(11) NOT NULL,
        `waitsec` int(11) NOT NULL,
        `progress_mediasec` int(11) NOT NULL,
        `flow_billsec` int(11) NOT NULL,
        `sip_hangup_disposition` int(11) NOT NULL,
        `callForwarded` varchar(10) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
        `forwardUuid` varchar(40) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
        `call_type` enum('s','v') CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT 's',
        `billed` int(1) NOT NULL,
        `uc` int(1) NOT NULL,
        `suc` int(1) NOT NULL,
        `callinfo` varchar(250) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
        `termination_ip` varchar(20) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
        `switchname` varchar(10) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL,
        `org_charges` float(11,4) NOT NULL,
        `call_summary` text,
        PRIMARY KEY (`id`),
        UNIQUE KEY `index_inbound_0717` (`id`) USING BTREE,
        UNIQUE KEY `index_uuid` (`uuid`) USING BTREE,
        UNIQUE KEY `index_all` (`id`,`campaign_id`,`session_id`,`uuid`) USING BTREE,
        KEY `index_source` (`source`) USING BTREE,
        KEY `index_destination` (`destination`) USING BTREE,
        KEY `index_endpoint` (`endpoint_disposition`) USING BTREE,
        KEY `index_build` (`billed`) USING BTREE,
        KEY `index_campainid` (`campaign_id`) USING BTREE
      ) ENGINE=MyISAM AUTO_INCREMENT=4457485 DEFAULT CHARSET=latin1

这是第二个表(wireless_checks):

         CREATE TABLE `wireless_checks` (
        `id` int(22) NOT NULL AUTO_INCREMENT,
        `date` varchar(10) NOT NULL,
        `uuid` varchar(100) NOT NULL,
        `tn` varchar(11) NOT NULL,
        `lrn` varchar(11) NOT NULL,
        `ported_status` varchar(2) NOT NULL,
        `ported_date` varchar(11) NOT NULL,
        `ocn` varchar(10) NOT NULL,
        `line_type` int(1) NOT NULL,
        `spid` varchar(10) NOT NULL,
        `spid_carrier_name` varchar(100) NOT NULL,
        `spid_carrier_type` varchar(10) NOT NULL,
        `altspid_carrier_name` varchar(10) NOT NULL,
        `altspid_carrier_type` varchar(10) NOT NULL,
        PRIMARY KEY (`id`),
        UNIQUE KEY `index_uuid` (`uuid`) USING BTREE
      ) ENGINE=MyISAM AUTO_INCREMENT=36175 DEFAULT CHARSET=latin1

请指导我如何优化此查询以减少执行时间。如果有任何其他方法可以完成这项工作,我也愿意接受解决方法。任何帮助将不胜感激。

谢谢

侯赛因

【问题讨论】:

  • 考虑了解和使用EXPLAIN

标签: mysql innodb myisam into-outfile


【解决方案1】:

应该有所作为的一个提示是,而不是这样做

WHERE date(a.start_epoch)='2018-02-19'

您应该考虑事先计算,然后使用实际值,即 1518998400

这是一个危险信号的原因是,通过在比较的左侧放置一个函数,您会强制数据库执行全表扫描,在所有 4.45m 行上运行该函数,只是为了处理WHERE 子句。相反,如果您将列本身与实际值进行比较,而不使用 DATE 函数,那么 MySQL 可以更有效地优化查询,如果有可用的索引,将使用 a.start_epoch 上的索引。

要创建该索引,只需这样做

CREATE INDEX epoch_idx on inbound_022018(start_epoch)

更广泛地说,您应该针对具有大量值(不仅仅是 1 或 2 个可能性)的列创建索引,而多列索引可以帮助优化复杂的查询。

EXPLAIN 放在查询前面,并查看特别大的行数的结果,这是确定查询中的成本位置的好方法。通常,有效的索引可以解决问题。

【讨论】:

    【解决方案2】:

    SELECT INTO OUTFILE 不是问题。许多其他事情正在减慢查询速度。

    以下是我需要讨论的 sn-ps:

        FROM  inbound_022018 a
        LEFT JOIN  wireless_checks b  ON (a.uuid = b.uuid)
        WHERE  date(a.start_epoch)='2018-02-19'
          AND  a.endpoint_disposition='ANSWER'
          AND  a.direction='inbound'
          AND  a.billed=1;
    
        `uuid` varchar(50) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
    
        `uuid` varchar(100) NOT NULL ... DEFAULT CHARSET=latin1
    
        float(11,4)
    
        `date` varchar(10) NOT NULL, ...
        `ported_date` varchar(11) NOT NULL,
    
        PRIMARY KEY (`id`),
        UNIQUE KEY `index_inbound_0717` (`id`) USING BTREE,
    
        PRIMARY KEY (`id`), ...
        UNIQUE KEY `index_all` (`id`,`campaign_id`,`session_id`,`uuid`) USING BTREE,
    

    很多问题:

    • UUID 是出了名的“随机”。你的桌子有多大?如果它们大于缓存在 RAM 中,那么查询注定会非常缓慢。
    • 比较两个字符串 (a.uuid = b.uuid) 时,如果字符集或排序规则不同,则无法使用索引。解决这个问题。
    • 更小的方法是将字符串转换为BINARY(16)。 (代码可在其他地方找到。)
    • UUID,除非你有特殊的东西,否则可以是 `CHAR(26) CHARSET ascii。这清理了几件事。
    • a 需要一个复合INDEX(billed, direction, endpoint_disposition, start_epoch) 以使WHERE 更有效。前 3 列可以按任意顺序排列。
    • 如下所述更改日期测试。
    • PRIMARY KEYUNIQUE 键;删除后者。
    • FLOAT(m,n) 是一个无用的构造,因为它涉及两个舍入。对于货币价值,使用DECIMAL(m,n);对于“科学”值,请使用 FLOAT 而不使用 (m,n)
    • 以 PK 的所有列开头的辅助键几乎是无效的。 (好吧,MyISAM 可能会受益,但 InnoDB 很少这样做。)
    • 如果您在任何地方都不需要b.id,请摆脱它并提升uuid 成为PK。这将加快 InnoDB 的 JOIN
    • 除非有充分的理由,否则不要在 VARCHAR 中输入日期。
    • 不要使用 MyISAM;解决我在这里讨论过的问题。如果需要,然后再回来进一步讨论。

    当列在函数中“隐藏”时(例如,DATE()),索引列将无济于事。改为

    WHERE  a.start_epoch >= '2018-02-19'
      AND  a.start_epoch  < '2018-02-19' + INTERVAL 1 DAY
    

    通过该更改,我建议的INDEX 中的第 4 列将可用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-08-01
      • 2015-02-22
      • 2012-09-05
      • 1970-01-01
      • 2011-08-01
      • 2011-02-21
      • 2018-10-06
      相关资源
      最近更新 更多