5

2,000万行に近づいているパーティションテーブル(月ごと)に対してクエリを実行しようとしています。DATE(transaction_utc)とcountry_idでグループ化する必要があります。group byをオフにして集計した場合に返される行は、40kをわずかに超えていますが、これはそれほど多くはありませんが、group BYがtransaction_utc列にある場合を除き、group byを追加すると、クエリが大幅に遅くなります。速くなります。

私はクエリやインデックスを微調整することでこの最初のクエリを最適化しようとしてきましたが、以下のポイントに到達しました(最初の約2倍の速さ)が、45k行を要約するための5sクエリに固執しました。過度に。

参考までに、このボックスは真新しい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`)

同じクエリのEXPLAINSELECTは次のとおりです。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   

今、私が何が起こっているのかを判断しようとした他のいくつかのオプションに移ります...

次のクエリ(group byで変更)は約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`

次のクエリ(group byで削除)は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未満のグループにグループ化しようとしていることを示しています(私の最初のクエリのように):

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.`transaction_utc`

テーブルスキーマ:

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 ;
4

1 に答える 1

11

問題のある部分はおそらくGROUP BY DATE(transaction_utc). また、このクエリのカバリング インデックスがあると主張していますが、何も表示されません。5 列のインデックスには、クエリで使用されるすべての列がありますが、最適な順序ではありません (つまり: WHERE- GROUP BY- SELECT)。

そのため、有用なインデックスが見つからないエンジンは、20M 行すべてに対してこの関数を評価する必要があります。username実際には、 (the )で始まるインデックスを見つけてidx_uniqueそれを使用するため、(のみ) 1.2M 行の関数を評価する必要があります。(transaction_utc)aまたは aがあれば(username, transaction_utc)、3 つのうち最も有用なものを選択します。

列を日付と時刻の部分に分割してテーブル構造を変更する余裕はありますか? 可能であれば、(username, country_id, transaction_date)または (グループ化に使用される 2 つの列の順序を変更する)のインデックス(username, transaction_date, country_id)は非常に効率的です。

(username, country_id, transaction_date, sale_usd, commission_usd)さらに優れたカバーインデックス。


現在の構造を維持したい場合は、5 列のインデックス内の順序を次のように変更してみてください。

(username, country_id, transaction_utc, sale_usd, commission_usd)

または:

(username, transaction_utc, country_id, sale_usd, commission_usd)

MariaDB を使用しているため、既存の列を変更せずにVIRTUAL列機能を使用できます。

仮想 (永続) 列と適切なインデックスを追加します。

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) ;
于 2012-10-27T20:35:16.340 に答える