简体   繁体   English

MySQL - 查询获取速度非常慢

[英]MySQL - query fetching very slow

CREATE TABLE `offers` (
  `id` mediumint(8) unsigned NOT NULL auto_increment,
  `cap_id` varchar(255),
  `deposit_value` varchar(255) default NULL,
  `term` varchar(255) default NULL,
  `annual_mileage` varchar(255) default NULL,
  `finance_type` varchar(255) default NULL,
  `monthly_payment` mediumint default NULL,
  `stock` varchar(255) default NULL,
  PRIMARY KEY (`id`)
) AUTO_INCREMENT=1;

INSERT INTO `offers` (`cap_id`,`deposit_value`,`term`,`annual_mileage`,`finance_type`,`monthly_payment`,`stock`) VALUES ("93897","6","24","12000","B",363,"0"),("90626","1","24","12000","P",810,"0"),("93607","6","36","25000","B",172,"1"),("35877","1","48","10000","B",678,"1"),("77134","3","24","10000","P",454,"0"),("47080","6","36","10000","B",737,"0"),("46392","3","24","30000","P",261,"0"),("21418","3","36","20000","B",390,"1"),("80175","9","48","10000","B",713,"1"),("91487","6","48","12000","P",778,"1");
INSERT INTO `offers` (`cap_id`,`deposit_value`,`term`,`annual_mileage`,`finance_type`,`monthly_payment`,`stock`) VALUES ("18311","9","24","20000","B",384,"0"),("41740","9","24","12000","P",674,"1"),("69030","9","24","10000","P",518,"0"),("53342","3","36","8000","B",746,"1"),("65566","6","36","30000","P",145,"1"),("63172","6","48","5000","P",698,"1"),("79712","1","48","30000","B",330,"1"),("90505","1","36","8000","B",458,"0"),("42393","1","36","20000","B",363,"1"),("70454","9","24","5000","B",673,"1");
INSERT INTO `offers` (`cap_id`,`deposit_value`,`term`,`annual_mileage`,`finance_type`,`monthly_payment`,`stock`) VALUES ("81215","1","48","8000","B",472,"0"),("76538","3","24","15000","B",226,"0"),("05094","9","36","12000","P",721,"1"),("57363","9","48","5000","B",777,"1"),("23233","1","48","12000","B",381,"0"),("40542","3","48","12000","P",610,"0"),("63824","3","24","12000","B",761,"1"),("17686","3","24","5000","P",893,"1"),("57669","9","48","30000","P",805,"0"),("21864","1","24","25000","P",530,"0");
INSERT INTO `offers` (`cap_id`,`deposit_value`,`term`,`annual_mileage`,`finance_type`,`monthly_payment`,`stock`) VALUES ("48360","3","48","12000","P",159,"0"),("88614","3","48","20000","B",730,"0"),("17693","1","24","10000","B",298,"0"),("34049","6","48","20000","B",728,"0"),("15038","9","24","10000","P",720,"1"),("31809","1","36","20000","P",237,"0"),("49277","9","48","25000","P",235,"1"),("54607","1","24","12000","P",661,"1"),("65098","1","48","20000","P",548,"1"),("76440","9","48","10000","P",495,"1");

CREATE TABLE `offers_lowest` (
  `id` mediumint(8) unsigned NOT NULL auto_increment,
  `date` DATE,
  `cap_id` varchar(255),
  `deposit` varchar(255) default NULL,
  `term` varchar(255) default NULL,
  `mileage` varchar(255) default NULL,
  `finance_type` varchar(255) default NULL,
  `lowest_price` mediumint default NULL,
  `stock` varchar(255) default NULL,
  PRIMARY KEY (`id`)
) AUTO_INCREMENT=1;

INSERT INTO `offers_lowest` (`date`,`cap_id`,`deposit`,`term`,`mileage`,`finance_type`,`lowest_price`,`stock`) VALUES ("2021-04-09","93897","6","24","12000","B",363,"0"),("2021-04-010","90626","1","24","12000","P",810,"0"),("2021-04-010","93607","6","36","25000","B",172,"1"),("2021-04-10","35877","1","48","10000","B",678,"1"),("2021-04-11","77134","3","24","10000","P",454,"0"),("2021-04-11","47080","6","36","10000","B",737,"0"),("2021-04-11","46392","3","24","30000","P",261,"0"),("2021-04-11","21418","3","36","20000","B",390,"1"),("2021-04-12","80175","9","48","10000","B",713,"1"),("2021-04-12","91487","6","48","12000","P",778,"1");
INSERT INTO `offers_lowest` (`date`,`cap_id`,`deposit`,`term`,`mileage`,`finance_type`,`lowest_price`,`stock`) VALUES ("2021-04-09","18311","9","24","20000","B",384,"0"),("2021-04-010","41740","9","24","12000","P",674,"1"),("2021-04-010","69030","9","24","10000","P",518,"0"),("2021-04-10","53342","3","36","8000","B",746,"1"),("2021-04-11","65566","6","36","30000","P",145,"1"),("2021-04-11","63172","6","48","5000","P",698,"1"),("2021-04-11","79712","1","48","30000","B",330,"1"),("2021-04-11","90505","1","36","8000","B",458,"0"),("2021-04-12","42393","1","36","20000","B",363,"1"),("2021-04-12","70454","9","24","5000","B",673,"1");
INSERT INTO `offers_lowest` (`date`,`cap_id`,`deposit`,`term`,`mileage`,`finance_type`,`lowest_price`,`stock`) VALUES ("2021-04-09","81215","1","48","8000","B",472,"0"),("2021-04-09","76538","3","24","15000","B",226,"0"),("2021-04-010","05094","9","36","12000","P",721,"1"),("2021-04-10","57363","9","48","5000","B",777,"1"),("2021-04-11","23233","1","48","12000","B",381,"0"),("2021-04-11","40542","3","48","12000","P",610,"0"),("2021-04-11","63824","3","24","12000","B",761,"1"),("2021-04-11","17686","3","24","5000","P",893,"1"),("2021-04-12","57669","9","48","30000","P",805,"0"),("2021-04-12","21864","1","24","25000","P",530,"0");
INSERT INTO `offers_lowest` (`date`,`cap_id`,`deposit`,`term`,`mileage`,`finance_type`,`lowest_price`,`stock`) VALUES ("2021-04-09","48360","3","48","12000","P",159,"0"),("2021-04-09","88614","3","48","20000","B",730,"0"),("2021-04-010","17693","1","24","10000","B",298,"0"),("2021-04-10","34049","6","48","20000","B",728,"0"),("2021-04-11","15038","9","24","10000","P",720,"1"),("2021-04-11","31809","1","36","20000","P",237,"0"),("2021-04-11","49277","9","48","25000","P",235,"1"),("2021-04-11","54607","1","24","12000","P",661,"1"),("2021-04-12","65098","1","48","20000","P",548,"1"),("2021-04-12","76440","9","48","10000","P",495,"1");

CREATE INDEX idx_profile_grouping ON offers (cap_id, deposit_value, term, annual_mileage);

CREATE INDEX idx_specials_query ON offers_lowest (cap_id, deposit, term, mileage);

SQLFiddle SQLFiddle

So, I'm trying to join two tables - one a list of active offers on our platform (we're a car leasing comparison site) and a custom table which records, daily, the best price for every vehicle (cap_id), and every possible finance profile ie, 9 month deposit value, 24 month contract and 8,000 miles per year, etc. etc.所以,我正在尝试加入两个表 - 一个是我们平台上的活动报价列表(我们是一个汽车租赁比较网站),另一个是自定义表,它每天记录每辆车的最佳价格(cap_id),以及每个可能的财务状况,即 9 个月的存款价值、24 个月的合同和每年 8,000 英里等。

This is with the end goal of identifying what a 'good' price is, with the output containing both MIN(offers.monthly_payment) and MIN(offers_lowest.lowest_price) , and a calculated percentage difference, with the historical price data taken from a 7-day date range not inclusive of the current day.这是为了确定什么是“好”价格的最终目标,output 包含MIN(offers.monthly_payment)MIN(offers_lowest.lowest_price)以及计算的百分比差异,历史价格数据取自 7 -day 日期范围不包括当天。

offers_lowest is aggregated from offers daily, finding the minimum monthly_payment grouped by: cap_id, deposit_value, term, annual_mileage, finance_type offers_lowest是从每天的offers中汇总的,找到按以下条件分组的最低monthly_payment付款: cap_id, deposit_value, term, annual_mileage, finance_type

This is what I've come up with so far:到目前为止,这是我想出的:

SELECT 
    s.cap_id,
    s.deposit,
    s.term,
    s.mileage,
    s.best_price,
    f.previous_best,
    (( s.best_price - f.previous_best ) / f.previous_best ) * 100 AS difference,
    s.stock
FROM
    (SELECT
        o.cap_id,
        o.deposit_value as deposit,
        o.term,
        o.annual_mileage as mileage,
        o.finance_type,
        MIN(o.monthly_payment) AS best_price,
        o.stock,
        o.brand_id
    FROM
        offers o USE INDEX(idx_profile_grouping)
    WHERE o.finance_type = 'P'
    GROUP BY o.cap_id, o.deposit_value, o.term, o.annual_mileage
    ) s 
    
INNER JOIN

    (SELECT
        ol.cap_id,
        ol.deposit,
        ol.term,
        ol.mileage,
        MIN(lowest_price) as previous_best
    FROM
        offers_lowest ol USE INDEX(idx_specials_query)
    WHERE finance_type = 'P'
        AND ol.date > CURDATE() - INTERVAL 7 DAY
        AND ol.date <= CURDATE() - INTERVAL 1 DAY
    GROUP BY ol.cap_id, ol.deposit, ol.term, ol.mileage
    ) f ON s.cap_id = f.cap_id AND s.deposit = f.deposit AND s.term = f.term AND s.mileage = f.mileage
GROUP BY s.cap_id, s.deposit, s.term, s.mileage

The issue is with speed.问题在于速度。 When I run either one of the subqueries individually, it takes <0.1s to run, then around 75 seconds to fetch.当我单独运行任一子查询时,运行时间 <0.1 秒,然后获取大约 75 秒。 When I run the whole thing, it runs until I stop it (>10 minutes).当我运行整个过程时,它会一直运行直到我停止它(>10 分钟)。

The indexes are ones I created over the cap_id, deposit, term and mileage fields (both tables) in an attempt to speed things up.这些索引是我在 cap_id、deposit、term 和 mileage 字段(两个表)上创建的,以试图加快速度。

EXPLAIN returns this:解释返回这个:

# id # ID select_type选择类型 table桌子 partitions分区 type类型 possible_keys可能的键 key钥匙 key_len key_len ref参考 rows filtered过滤 Extra额外的
1 1 PRIMARY基本的 ALL全部 965741 965741 100.00 100.00 Using where;使用哪里; Using temporary;使用临时的; Using filesort使用文件排序
1 1 PRIMARY基本的 ref参考 <auto_key0> <auto_key0> <auto_key0> <auto_key0> 10 10 f.cap_id f.cap_id f.deposit f.存款 f.term f.术语 f.mileage f.里程
3 3 DERIVED衍生的 ol奥尔 index指数 idx_specials_query idx_specials_query idx_specials_query idx_specials_query 13 13 17386818 17386818 5.55 5.55 Using where使用哪里
2 2 DERIVED衍生的 o index指数 idx_profile_grouping idx_profile_grouping idx_profile_grouping idx_profile_grouping 10 10 4800964 4800964 50.00 50.00 Using where使用哪里

And SHOW PROFILE returns this:并且 SHOW PROFILE 返回这个:

Status地位 Duration期间
starting开始 0.000017 0.000017
checking query cache for query检查查询缓存以进行查询 0.000171 0.000171
checking permissions检查权限 0.000006 0.000006
checking permissions检查权限 0.000005 0.000005
Opening tables打开表 0.000020 0.000020
init在里面 0.000109 0.000109
System lock系统锁 0.000009 0.000009
optimizing优化 0.000005 0.000005
optimizing优化 0.000011 0.000011
statistics统计数据 0.000033 0.000033
preparing准备 0.000037 0.000037
Sorting result排序结果 0.000007 0.000007
optimizing优化 0.000015 0.000015
statistics统计数据 0.000017 0.000017
preparing准备 0.000017 0.000017
Sorting result排序结果 0.000012 0.000012
statistics统计数据 0.000046 0.000046
preparing准备 0.000021 0.000021
Creating tmp table创建 tmp 表 0.000020 0.000020
Sorting result排序结果 0.000007 0.000007
executing执行 0.000013 0.000013
Sending data发送数据 0.000012 0.000012
executing执行 0.000004 0.000004
Sending data发送数据 68.205032 68.205032
converting HEAP to ondisk将 HEAP 转换为磁盘 0.542756 0.542756
Sending data发送数据 3.132642 3.132642
executing执行 0.000019 0.000019
Sending data发送数据 7.968966 7.968966
converting HEAP to ondisk将 HEAP 转换为磁盘 0.746200 0.746200
Sending data发送数据 22.506716 22.506716
converting HEAP to ondisk将 HEAP 转换为磁盘 0.567816 0.567816
Sending data发送数据 4.886870 4.886870
Creating sort index创建排序索引 2.151519 2.151519
end结尾 0.000019 0.000019
query end查询结束 0.000010 0.000010
removing tmp table删除 tmp 表 0.002051 0.002051
query end查询结束 0.000019 0.000019
closing tables收盘 0.000005 0.000005
removing tmp table删除 tmp 表 0.001385 0.001385
closing tables收盘 0.000009 0.000009
removing tmp table删除 tmp 表 0.002780 0.002780
closing tables收盘 0.000017 0.000017
freeing items释放物品 0.000069 0.000069
cleaned up清理干净 0.000007 0.000007
cleaning up打扫干净 0.000021 0.000021

It seems from this that I've not written the most efficient query..由此看来,我还没有写出最有效的查询..

We use an AWS RDS instance, running MySQL 5.7.12.我们使用 AWS RDS 实例,运行 MySQL 5.7.12。

If there's anything additional I can provide to add background, please let me know.如果我可以提供任何其他内容来添加背景,请告诉我。

  • FROM ( SELECT... ) JOIN ( SELECT... ) is inefficient. FROM ( SELECT... ) JOIN ( SELECT... )效率低下。 Try to move at least one of them into the outer SELECT .尝试将其中至少一个移到外部SELECT中。 (This may eliminate "Creating sort index" if you add a suitable index for the JOIN . It may also eliminate most of the cryptic "sending data".) (如果您为JOIN添加合适的索引,这可能会消除“创建排序索引”。它也可能会消除大多数神秘的“发送数据”。)
  • That date range includes only 6 DATEs.该日期范围仅包括 6 个 DATE。
  • What if one row has MIN(offers.monthly_payment) and a different row has MIN(offers_lowest.lowest_price)?如果一行有 MIN(offers.monthly_payment) 而另一行有 MIN(offers_lowest.lowest_price) 怎么办?
  • Don't use VARCHAR(255) for numeric values.不要将 VARCHAR(255) 用于数值。 Eg, MEDIUMINT for mileage.例如,MEDIUMINT 表示里程。 (This may eliminate "converting HEAP to ondisk".) (这可能会消除“将 HEAP 转换为磁盘”。)
  • Note how useless PROFILE is;注意 PROFILE 是多么无用; it likes to say "sending data" for most of the time (95% of total).大多数时候它喜欢说“发送数据”(占总数的 95%)。 I could identify 4% and recommend fixes, see above.我可以确定 4% 并建议修复,见上文。 The other 1% is unavoidable noise.另外 1% 是不可避免的噪音。
  • The first GROUP BY is inappropriate since it does not say which value to give for stock or brand_id.第一个GROUP BY是不合适的,因为它没有说明为 stock 或 brand_id 赋予哪个值。 If necessary, grab them with an extra JOIN after finding the rest of the info.如有必要,在找到信息的 rest 后,通过额外的JOIN获取它们。
  • offers_lowest needs INDEX(finance_type, date) offers_lowest需求INDEX(finance_type, date)
  • Get rid of "index hints" ( USE INDEX... )摆脱“索引提示”( USE INDEX...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM