Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/63.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
MySQL:大数据慢读_Mysql - Fatal编程技术网

MySQL:大数据慢读

MySQL:大数据慢读,mysql,Mysql,我有一个非常大的表,有17044833行,大小为6.4GB。我正在运行下面的简单查询,大约需要5秒钟。我可以做些什么优化来提高这个查询的速度 SELECT `stat_date`, SUM(`adserver_impr`), SUM(`adserver_clicks`) FROM `dfp_stats` WHERE `stat_date` >= '2014-02-01' AND `stat_date` <= '2014-02-28' 服务器: Memory: 48GB

我有一个非常大的表,有17044833行,大小为6.4GB。我正在运行下面的简单查询,大约需要5秒钟。我可以做些什么优化来提高这个查询的速度

SELECT 
`stat_date`,
SUM(`adserver_impr`),
SUM(`adserver_clicks`)
FROM `dfp_stats` WHERE 
`stat_date` >= '2014-02-01' 
AND 
`stat_date` <= '2014-02-28'
服务器:

Memory: 48GB     
Disk: 480GB
更新

原始查询:

EXPLAIN
SELECT
DS.`stat_date` 'DATE',
DC.`name` COUNTRY,
DA.`name` ADVERTISER,
DOX.`id` ORDID,
DOX.`name` ORDNAME,
DLI.`id` LIID,
DLI.`name` LINAME,
DLI.`is_ron` ISRON,
DOX.`is_direct` ISDIRECT,
DSZ.`size` LISIZE,
PUBSITE.`id` SITEID,

SUM(DS.`adserver_impr`) 'DFPIMPR',
SUM(DS.`adserver_clicks`) 'DFPCLCKS',
SUM(DS.`adserver_rev`) 'DFPREV'

FROM `dfp_stats` DS
LEFT JOIN `dfp_adunit1` AD1 ON AD1.`id` = DS.`dfp_adunit1_id`
LEFT JOIN `dfp_adunit2` AD2 ON AD2.`id` = DS.`dfp_adunit2_id`
LEFT JOIN `dfp_adunit3` AD3 ON AD3.`id` = DS.`dfp_adunit3_id`
LEFT JOIN `dfp_orders` DOX  ON DOX.`id` = DS.`dfp_order_id`
LEFT JOIN `dfp_advertisers` DA  ON DA.`id` = DOX.`dfp_advertiser_id`
LEFT JOIN `dfp_lineitems` DLI  ON DLI.`id` = DS.`dfp_lineitem_id`
LEFT JOIN `dfp_countries` DC  ON DC.`id` = DS.`dfp_country_id`
LEFT JOIN `dfp_creativesize` DSZ ON DSZ.`id` =  DS.`dfp_creativesize_id`
LEFT JOIN `pubsites` PUBSITE
ON AD1.`pubsite_id` = PUBSITE.`id`
OR AD2.`pubsite_id` = PUBSITE.`id`

WHERE
DS.`stat_date` >= '2014-02-01'
AND DS.`stat_date` <= '2014-02-28'
AND PUBSITE.`id` = 6
GROUP BY  DLI.`id`,DS.`stat_date`;
解释
挑选
DS.‘统计日期’‘日期’,
DC.`name`国家,
DA.`name`广告商,
DOX.`id`ORDID,
DOX.`name`或name,
DLI.`id`LIID,
DLI.`name`LINAME,
德莉·伊斯隆,
DOX.‘is_direct’,
DSZ.`size`LISIZE,
publisite.`id`SITEID,
总和(DS.`adserver\u impr`)`DFPIMPR',
总和(DS.`adserver_clicks`)`DFPCLCKS',
总和(DS.`adserver\u rev`)`DFPREV'
来自'dfp_stats`DS
左连接AD1上的'dfp\u adunit1'AD1。'id`=DS.'dfp\u adunit1\u id`
左连接AD2上的'dfp_adunit2'AD2。'id`=DS。'dfp_adunit2_id`
左连接AD3上的'dfp_adunit3'AD3。'id`=DS.'dfp_adunit3\u id`
左JOIN`dfp\u orders`DOX ON DOX.`id`=DS.`dfp\u order\u id`
左键连接'dfp\u广告商'DA ON DA.'id`=DOX.'dfp\u广告商'u id`
在DLI上左连接`dfp\U行项目`DLI。`id`=DS.`dfp\U行项目`id`
左连接'dfp\u countries`DC ON DC.'id`=DS.'dfp\u country\u id`
在DSZ上左键联接'dfp\u creativesize`DSZ。'id`=DS。'dfp\u creativesize\u id`
左连接'PUBSITE'PUBSITE
在AD1上,`pubsite_id`=pubsite.`id`
或AD2.`publisite\u id`=publisite.`id`
哪里
DS.‘统计日期’>=‘2014-02-01’

和DS.`stat\u date`如果您没有,您可能希望为
stat\u date
字段编制索引,以便更快地查找。以下是语法:

ALTER TABLE TABLE_NAME ADD INDEX (COLUMN_NAME);

请在此处阅读有关索引和优化的详细信息:

要获得此查询的最佳性能,请创建覆盖索引:

... ON `dfp_stats` (`stat_date`,`adserver_impr`,`adserver_clicks`) 
EXPLAIN的输出应显示“使用索引”。这意味着可以完全通过索引满足查询,而无需访问基础表中的任何页面。(术语“覆盖索引”是指包含查询引用的所有列的索引。)

至少,您需要一个前导列为
stat\u date
的索引,以便查询可以使用索引范围扫描操作。索引范围扫描基本上可以跳过大量行,并更快地找到实际需要检查的行

至于MySQL实例配置的更改,这实际上取决于表是InnoDB还是MyISAM


跟进

对于InnoDB来说,内存仍然是王者。如果服务器上有可用内存,则可以增加innodb_buffer_pool

还考虑启用MySQL查询缓存。(我们只为那些专门启用了SQL\u cache关键字的查询启用了查询缓存,即

选择SQL\u cache t.foo,
,这样我们就不会因为没有给我们带来好处的查询而使缓存混乱。对于其他查询,我们避免运行额外的代码(否则将需要额外的代码)搜索缓存并维护缓存内容

我们从查询缓存中获益的地方是针对相对静态且返回小结果集的表的“昂贵”查询(它们查看大量行并执行大量联接)。(我认为,如果一个查询的表很少更新,或者如果表上的一个DML操作使缓存失效,那么如果从一个整个装入的行中得到一个单行的查询将是查询缓存的一个很好的候选者,或者如果相同的查询将被运行好几次。)
您的查询返回的非聚合不在GROUPBY子句中,这有点奇怪

如果您的查询在stat_date上使用索引,则该查询很可能返回谓词指定范围内的
stat_date
的最低值;因此,使用
SELECT MIN(stat_date)作为stat_date
很可能会得到相同的结果


更复杂的方法是设置一个“摘要”表,并使用查询结果定期刷新该表,然后让应用程序查询摘要表。(数据仓库类型的方法。)如果需要“最新信息”,则此方法不起作用要做到这一点,您可能需要在目标表上引入触发器,以维护插入、更新和删除操作的汇总表

如果我这样做的话,我可能会选择为每个
stat\u date
存储一个摘要行,这样它就可以容纳对任何范围或日期集的查询

CREATE TABLE dfp_stats_summary 
( stat_date       DATE NOT NULL PRIMARY KEY
, adserver_impr   BIGINT
, adserver_clicks BIGINT
) ENGINE=InnoDB ;

-- refresh
INSERT INTO dfp_stats_summary (stat_date, adserver_impr, adserver_clicks)
SELECT t.stat_date
     , SUM(t.adserver_impr) AS adserver_impr
     , SUM(t.adserver_clicks) AS adserver_clicks
  FROM dfp_stats
 GROUP BY t.stat_date
    ON DUPLICATE KEY
       UPDATE adserver_impr = VALUES(adserver_impr)
           , adserver_clicks = VALUES(adserver_clicks)
 ;
刷新查询将启动;您可能希望在WHERE子句中指定一个日期范围,以便一次执行一到两个月,并遍历所有可能的月份

填充汇总表后,只需将原始查询更改为引用新的汇总表,而不是详细信息表。与数十万个详细信息行相比,添加28个汇总行要快得多



我将尝试创建一个覆盖索引。我已经在stat\u date上有了一个索引。这些表都是InnoDB。@iser2884319:注意:如果您创建一个以
stat\u date
作为前导列的多列索引,那么就不需要在单个
stat\u date
列上创建另一个索引(除非用于强制唯一约束。)所以基本上你说的是尝试解释说使用索引?所以如果我在查询中添加更多的列,我必须将其包括在覆盖索引中,对吗?@user2884319:是的,基本上。显然会有一个转折点。通过存储子集,我们可以将更多的行打包到一个页面中(更少的页面访问)此外,我们不必按照索引指针访问基础表中的页面。直接从索引获取所需的值是一个较短的代码路径。是的,当MySQL使用“覆盖索引”时,解释输出将显示“使用索引”在额外的一列中。而且,如果您的查询是按统计日期进行分组,MySQL可以使用相同的索引来避免“使用文件排序”操作。感谢更新,我添加了覆盖索引。我知道这让人感到困惑,因为我没有做任何分组依据之类的事情。我只是添加了
CREATE TABLE dfp_stats_summary 
( stat_date       DATE NOT NULL PRIMARY KEY
, adserver_impr   BIGINT
, adserver_clicks BIGINT
) ENGINE=InnoDB ;

-- refresh
INSERT INTO dfp_stats_summary (stat_date, adserver_impr, adserver_clicks)
SELECT t.stat_date
     , SUM(t.adserver_impr) AS adserver_impr
     , SUM(t.adserver_clicks) AS adserver_clicks
  FROM dfp_stats
 GROUP BY t.stat_date
    ON DUPLICATE KEY
       UPDATE adserver_impr = VALUES(adserver_impr)
           , adserver_clicks = VALUES(adserver_clicks)
 ;