Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/67.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_Sql_Performance_Optimization_Mariadb - Fatal编程技术网

为什么我的MySQL组速度这么慢?

为什么我的MySQL组速度这么慢?,mysql,sql,performance,optimization,mariadb,Mysql,Sql,Performance,Optimization,Mariadb,我试图查询一个分区表(按月计算),该表的行数接近2000万行。我需要按日期(事务\ utc)和国家/地区\ id进行分组。如果我关闭group by和aggregates,返回的行数刚好超过40k,这并不太多,但是添加group by会使查询速度大大降低,除非所述group by位于事务\ utc列上,在这种情况下,查询速度会变快 我一直在尝试通过调整查询和/或索引来优化下面的第一个查询,并且达到了下面的点(大约是最初的2倍),但是仍然坚持使用5s查询来汇总45k行,这似乎太多了 作为参考,此框

我试图查询一个分区表(按月计算),该表的行数接近2000万行。我需要按日期(事务\ utc)和国家/地区\ id进行分组。如果我关闭group by和aggregates,返回的行数刚好超过40k,这并不太多,但是添加group by会使查询速度大大降低,除非所述group by位于事务\ utc列上,在这种情况下,查询速度会变快

我一直在尝试通过调整查询和/或索引来优化下面的第一个查询,并且达到了下面的点(大约是最初的2倍),但是仍然坚持使用5s查询来汇总45k行,这似乎太多了

作为参考,此框是一个全新的24逻辑核心、64GB RAM、Mariadb-5.5.x服务器,其可用INNODB缓冲池远多于服务器上的索引空间,因此不应存在任何RAM或CPU压力

所以,我想知道是什么原因导致了这种减速,并提出加速的建议。任何反馈都将不胜感激!:)

好的,关于细节

下面的查询(我实际需要的查询)大约需要5秒(+/-),返回的行数少于100行

SELECT lss.`country_id` AS CountryId
, Date(lss.`transaction_utc`) AS TransactionDate
, c.`name` AS CountryName,  lss.`country_id` AS CountryId
, COALESCE(SUM(lss.`sale_usd`),0) AS SaleUSD
, COALESCE(SUM(lss.`commission_usd`),0) AS CommissionUSD  
FROM `sales` lss  
JOIN `countries` c ON lss.`country_id` = c.`country_id`  
WHERE ( lss.`transaction_utc` BETWEEN '2012-09-26' AND '2012-10-26' AND lss.`username` = 'someuser' )  GROUP BY lss.`country_id`, DATE(lss.`transaction_utc`)
同一查询的解释选择如下。请注意,它没有使用transaction_utc键。它不应该使用我的覆盖指数吗

id  select_type table   type    possible_keys   key key_len ref rows    Extra
1   SIMPLE  lss ref idx_unique,transaction_utc,country_id   idx_unique  50  const   1208802 Using where; Using temporary; Using filesort
1   SIMPLE  c   eq_ref  PRIMARY PRIMARY 4   georiot.lss.country_id  1   
现在来看看我试图确定发生了什么的几个其他选项

以下查询(更改的分组方式)大约需要5秒(+/-),并且只返回3行:

SELECT lss.`country_id` AS CountryId
, DATE(lss.`transaction_utc`) AS TransactionDate
, c.`name` AS CountryName,  lss.`country_id` AS CountryId
, COALESCE(SUM(lss.`sale_usd`),0) AS SaleUSD
, COALESCE(SUM(lss.`commission_usd`),0) AS CommissionUSD  
FROM `sales` lss  
JOIN `countries` c ON lss.`country_id` = c.`country_id`  
WHERE ( lss.`transaction_utc` BETWEEN '2012-09-26' AND '2012-10-26' AND lss.`username` = 'someuser' )  GROUP BY lss.`country_id`
以下查询(删除分组依据)需要4-5秒(+/-)并返回1行:

SELECT lss.`country_id` AS CountryId
    , DATE(lss.`transaction_utc`) AS TransactionDate
    , c.`name` AS CountryName,  lss.`country_id` AS CountryId
    , COALESCE(SUM(lss.`sale_usd`),0) AS SaleUSD
    , COALESCE(SUM(lss.`commission_usd`),0) AS CommissionUSD  
    FROM `sales` lss  
    JOIN `countries` c ON lss.`country_id` = c.`country_id`  
    WHERE ( lss.`transaction_utc` BETWEEN '2012-09-26' AND '2012-10-26' AND lss.`username` = 'someuser' )
以下查询耗时.00X秒(+/-),返回约45k行。这向我表明,在最大情况下,我们只尝试将45K行分组到少于100个组中(如我的初始查询中所示):

表架构:

CREATE TABLE IF NOT EXISTS `sales` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `user_linkshare_account_id` int(11) unsigned NOT NULL,
  `username` varchar(16) NOT NULL,
  `country_id` int(4) unsigned NOT NULL,
  `order` varchar(16) NOT NULL,
  `raw_tracking_code` varchar(255) DEFAULT NULL,
  `transaction_utc` datetime NOT NULL,
  `processed_utc` datetime NOT NULL ,
  `sku` varchar(16) NOT NULL,
  `sale_original` decimal(10,4) NOT NULL,
  `sale_usd` decimal(10,4) NOT NULL,
  `quantity` int(11) NOT NULL,
  `commission_original` decimal(10,4) NOT NULL,
  `commission_usd` decimal(10,4) NOT NULL,
  `original_currency` char(3) NOT NULL,
  PRIMARY KEY (`id`,`transaction_utc`),
  UNIQUE KEY `idx_unique` (`username`,`order`,`processed_utc`,`sku`,`transaction_utc`),
  KEY `raw_tracking_code` (`raw_tracking_code`),
  KEY `idx_usd_amounts` (`sale_usd`,`commission_usd`),
  KEY `idx_countries` (`country_id`),
  KEY `transaction_utc` (`transaction_utc`,`username`,`country_id`,`sale_usd`,`commission_usd`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8
/*!50100 PARTITION BY RANGE ( TO_DAYS(`transaction_utc`))
(PARTITION pOLD VALUES LESS THAN (735112) ENGINE = InnoDB,
 PARTITION p201209 VALUES LESS THAN (735142) ENGINE = InnoDB,
 PARTITION p201210 VALUES LESS THAN (735173) ENGINE = InnoDB,
 PARTITION p201211 VALUES LESS THAN (735203) ENGINE = InnoDB,
 PARTITION p201212 VALUES LESS THAN (735234) ENGINE = InnoDB,
 PARTITION pMAX VALUES LESS THAN MAXVALUE ENGINE = InnoDB) */ AUTO_INCREMENT=19696320 ;

有问题的部分可能是
按日期分组(交易协调时间)
。您还声称有此查询的覆盖索引,但我看不到。您的5列索引包含查询中使用的所有列,但顺序不是最佳的(即:
WHERE
-
groupby
-
SELECT

因此,引擎如果没有找到有用的索引,就必须为所有2000万行计算此函数。实际上,它会找到一个以
用户名
(idx_unique)开头的索引,并使用该索引,因此它必须对(仅)120万行的函数进行评估。如果您有一个
(transaction\u utc)
(username,transaction\u utc)
,它将选择三个中最有用的一个

您能通过将列拆分为日期和时间部分来更改表结构吗? 如果可以,那么在
(用户名、国家/地区id、交易日期)
(更改用于分组的两列的顺序)
上的索引(用户名、交易日期、国家/地区id)将非常有效

(用户名、国家id、交易日期、销售额、佣金)的覆盖指数更好


如果要保留当前结构,请尝试将5列索引内的顺序更改为:

(username, country_id, transaction_utc, sale_usd, commission_usd)
或:

(username, transaction_utc, country_id, sale_usd, commission_usd)

由于您使用的是MariaDB,因此可以使用功能,而无需更改现有列:

添加虚拟(持久)列和适当的索引:

ALTER TABLE sales 
    ADD COLUMN transaction_date DATE NOT NULL
               AS DATE(transaction_utc) 
               PERSISTENT 
    ADD INDEX special_IDX 
        (username, country_id, transaction_date, sale_usd, commission_usd) ;

是的,我想把日期/时间分开,但我觉得还有其他问题。我在这些字段上有一个不同顺序的索引,你是说我应该修改索引中字段的顺序?是的,索引中列的顺序非常重要。另一个可能好(甚至更好)的索引是,如果您将这两列转置:
(用户名、事务协调时间、国家/地区id)
更改查询顺序不会有同样的效果吗?(我看到这在性能上没有明显的差异)。其中(lss.
username
='someuser'和lss.
transaction_utc
,介于'2012-09-26'和'2012-10-26'之间),或者让查询保持原样,并按照您的说明更改索引会更好吗?我想我不清楚查询的每个部分的执行顺序,因为我认为应该是(“WHERE子句项”、“groupby项”、“JOIN项”、“RETURNED项”)。WHERE
中的顺序并不重要
其中a_条件和b_条件
其中b_条件和a_条件
是等价的,优化器知道这一点。我认为在选择索引时顺序很重要,但我想我需要更详细地阅读文档。虚拟是我不知道的东西,但我肯定可以在其他地方(可能在这里也一样)利用它。谢谢你!我要试试你的索引重新排序建议,看看效果如何。你是否检查了
EXPLAIN PARTITIONS
以确保只扫描有效的分区?
ALTER TABLE sales 
    ADD COLUMN transaction_date DATE NOT NULL
               AS DATE(transaction_utc) 
               PERSISTENT 
    ADD INDEX special_IDX 
        (username, country_id, transaction_date, sale_usd, commission_usd) ;