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';
SHOW GLOBAL VARIABLES LIKE 'tmp_table_size' ;
このクエリの所要時間は約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 @@version
:5.6.22-1+deb.sury.org~precise+1
UPDATE:EXPLAIN
ステートメントのSELECT
:
上記のすべてのインデックスはSHOW CREATE TABLE
で確認できます
UPDATE:実際のところ、測定の誤りでした。クエリのデバッグにSQLyogを使用していますが、デフォルトで1000レコードに制限されています。私はそれを認識しており、行制限オプションをオフにしてテストしました。問題は次のとおりです。最初の試行で、オプションをオフにしても、約2秒かかる(実際には40秒ほどかかる)と表示されます。
ただし、オプションをオフにしてクエリを再起動すると、正しい時刻が報告されます。私の間違いは、オプションのチェックを外す時間は正しいと思い、残りの約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
構造は上にあります。
ありがとう
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
に変更することを検討してください。