web-dev-qa-db-ja.com

MySQLでSELECTからのCREATE TEMPORARY TABLEが遅い

StackOverflowでこれを尋ねましたが、ヒントはあまりありません。そう:

SHOW CREATE TABLE cashadv_txn

CREATE TABLE `cashadv_txn` (
  `txn_id` int(11) NOT NULL AUTO_INCREMENT,
  `cashadv_id` int(11) NOT NULL,
  `txn_type` enum('LI','MI','CI','LD','MD','LDA','SRDA','SRD','NRF') DEFAULT NULL,
  `datetime` datetime NOT NULL,
  `amount` float(20,2) NOT NULL,
  `status` varchar(16) DEFAULT NULL,
  `usaepay_invoice_id` int(11) DEFAULT NULL,
  `link_uid` int(11) DEFAULT NULL,
  `link_date` date DEFAULT NULL,
  `link_txn_id` int(11) DEFAULT NULL,
  PRIMARY KEY (`txn_id`),
  KEY `link_txn_id` (`link_txn_id`),
  KEY `type_date_uid` (`cashadv_id`,`txn_type`,`link_date`,`link_uid`),
  KEY `usaepay_invoice_id` (`usaepay_invoice_id`),
  KEY `idx1` (`cashadv_id`,`link_uid`)
) ENGINE=InnoDB AUTO_INCREMENT=4586015 DEFAULT CHARSET=utf8

関連する一時テーブル変数。これはやり過ぎですが、後で減らすことができます。これは、それらが十分に高いことを示すためだけです。

SHOW GLOBAL VARIABLES LIKE 'max_heap_table_size';

enter image description here

SHOW GLOBAL VARIABLES LIKE 'tmp_table_size' ;

enter image description here

このクエリの所要時間は約0.8〜1秒です。

SELECT SQL_NO_CACHE
            link_uid AS lender_uid,
            cashadv_id,
            SUM(amount) AS lenderTotalCredit      
         FROM
            cashadv_txn      
         WHERE
            cashadv_txn.txn_type='LDA'      
         GROUP BY
            cashadv_id,
            link_uid;

ただし、このクエリ(実際には、前のクエリが結果を一時テーブルに保存するクエリ)には40秒かかります。

CREATE TEMPORARY TABLE IF NOT EXISTS tLenderTotalCredits ENGINE=MEMORY  AS (SELECT
            link_uid AS lender_uid,
            cashadv_id,
            SUM(amount) AS lenderTotalCredit      
         FROM
            cashadv_txn      
         WHERE
            cashadv_txn.txn_type='LDA'      
         GROUP BY
            cashadv_id,
            link_uid
            );

SELECT @@version5.6.22-1+deb.sury.org~precise+1

UPDATE:EXPLAINステートメントのSELECT

enter image description here

上記のすべてのインデックスはSHOW CREATE TABLEで確認できます

UPDATE:実際のところ、測定の誤りでした。クエリのデバッグにSQLyogを使用していますが、デフォルトで1000レコードに制限されています。私はそれを認識しており、行制限オプションをオフにしてテストしました。問題は次のとおりです。最初の試行で、オプションをオフにしても、約2秒かかる(実際には40秒ほどかかる)と表示されます。

enter image description here

ただし、オプションをオフにしてクエリを再起動すると、正しい時刻が報告されます。私の間違いは、オプションのチェックを外す時間は正しいと思い、残りの約30秒はグリッド内のすべての行をレンダリングすることでした。

別の質問として、私はこのクエリを最適化しようとしています(これはクレイジーです、私は知っています)。これはより大きなクエリのサブクエリです。しかし、これを最適化すると、大きなクエリ全体が非常に速く実行されると思います。現時点では、このクエリの実行には1.5分かかります。

SELECT
         cashadv_id,
         link_date AS DATE,
         link_uid AS lender_uid,
         amount,
         'Approved' AS STATUS   
      FROM
         `cashadv_txn`   
      WHERE
         txn_type='LD'     
         AND STATUS='Approved'   
      UNION
      ALL SELECT
         tLenderTotalCredits.cashadv_id,
         DATE(NOW()) AS DATE,
         tLenderTotalCredits.lender_uid,
         IFNULL(tLenderTotalCredits.lenderTotalCredit,0) - IFNULL(tLenderTotalPay.lenderTotalPay,0) AS amount,
         'Ready' AS STATUS   
      FROM
         ( SELECT
            link_uid AS lender_uid,
            cashadv_id,
            SUM(amount) AS lenderTotalCredit      
         FROM
            cashadv_txn      
         WHERE
            cashadv_txn.txn_type='LDA'      
         GROUP BY
            link_uid,
            cashadv_id ) tLenderTotalCredits   
      LEFT JOIN
         (
            SELECT
               cashadv_id,
               link_uid AS lender_uid,
               SUM(amount) AS lenderTotalPay      
            FROM
               cashadv_txn      
            WHERE
               txn_type='LD'        
               AND STATUS='Approved'      
            GROUP BY
               cashadv_id,
               link_uid 
         ) tLenderTotalPay 
            ON tLenderTotalPay.cashadv_id=tLenderTotalCredits.cashadv_id   
            AND tLenderTotalPay.lender_uid=tLenderTotalCredits.lender_uid -- check if there is current day payment     
      LEFT JOIN
         cashadv_txn cashadv_txn3 
            ON cashadv_txn3.txn_type='LD'   
            AND cashadv_txn3.cashadv_id=tLenderTotalCredits.cashadv_id   
            AND cashadv_txn3.link_uid=tLenderTotalCredits.lender_uid   
            AND cashadv_txn3.link_date = DATE(NOW())   
            AND cashadv_txn3.status='Approved'   
      WHERE
         cashadv_txn3.cashadv_id IS NULL

ご覧のとおり、2つのサブクエリ結合があり、最初に結果セットをメモリ内の一時テーブルに入れてから結合することで、高速化を望んでいました。

私はまだアプリのロジックに100%似ているわけではないので、何かを壊すのではないかと心配しています。これらの2つのサブ結合に23k行すべてが本当に必要か、制限することができるかわかりません。 。しかし、それとは別に、これを最適化する方法について何かアイデアはありますか? cashadv_txn構造は上にあります。

ありがとう

3
Alexey

RAMはどれくらいありますか?予備の2GBはありますか?それがそのMEMORYテーブルに許可しているものです。

括弧CREATE ... AS ( SELECT ... )を削除します。

追加

_INDEX(txn_type, cashadv_id, link_uid) -- in that order
INDEX(txn_type, status, cashadv_id, link_uid) -- in that order
_

Tmp_table_size = 2Gにするのは危険です。複数の接続ごとにtmpテーブルが必要な場合、RAMがすぐに不足する可能性があります。その時点で、mysqlの速度は非常に遅くなります。

この構成は最適化が不十分であるため、実行が遅くなります。

_FROM ( SELECT ... )
JOIN ( SELECT ... ) ON ...
_

DATE(NOW())-> CURRENT_DATE()

float(20,2)-> FLOATまたはDECIMAL(20,2)(おそらく20は過剰です)

status varchar(16) DEFAULT NULL,ENUMに変更することを検討してください。

3
Rick James