MyISAMと比較してInnoDBでの選択が遅い-MyISAMでの実行の違いは約2分、InnoDBでは2時間です。
関係する行数は33 Mです。結果として設定される行は約23Mです。
サーバー:
場所:Google Cloud
ディスク:4T SSD
メモリ:
[root@gcpjobrunner01 ~]# free -g
total used free shared buff/cache available
Mem: 58 44 0 0 12 13
Swap: 0 0 0
MySQLの正確なバージョン:
mysql> show global variables like '%version%';
+-------------------------+----------------------------------------------------+
| Variable_name | Value |
+-------------------------+----------------------------------------------------+
| innodb_version | 5.7.24-27 |
| protocol_version | 10 |
| slave_type_conversions | |
| tls_version | TLSv1,TLSv1.1,TLSv1.2 |
| version | 5.7.24-27-log |
| version_comment | Percona Server (GPL), Release 27, Revision bd42700 |
| version_compile_machine | x86_64 |
| version_compile_os | Linux |
| version_suffix | -log |
+-------------------------+----------------------------------------------------+
関連するテーブルのDDL:
CREATE TABLE `account_balances` (
`abl_aad_id` double NOT NULL,
`abl_txn_type_cd` varchar(8) NOT NULL,
`abl_prm_code` varchar(30) NOT NULL,
`abl_bal_sort` double NOT NULL DEFAULT '1',
`abl_bal_abl` double NOT NULL,
`abl_bal` double NOT NULL,
`abl_bal_pd` double NOT NULL,
`abl_bal_waived` double NOT NULL,
`abl_bal_chgoff` double NOT NULL DEFAULT '0',
`abl_bal_recovered` double NOT NULL DEFAULT '0',
`abl_bal_adjusted_plus` double NOT NULL DEFAULT '0',
`abl_bal_adjusted_minus` double NOT NULL DEFAULT '0',
`abl_bal_open_ctd` double NOT NULL DEFAULT '0',
`abl_bal_ctd` double NOT NULL,
`abl_bal_pd_ctd` double NOT NULL,
`abl_bal_waived_ctd` double NOT NULL,
`abl_bal_chgoff_open_ctd` double NOT NULL,
`abl_bal_chgoff_ctd` double NOT NULL,
`abl_bal_recovered_ctd` double NOT NULL,
`abl_bal_adjusted_plus_ctd` double NOT NULL,
`abl_bal_adjusted_minus_ctd` double NOT NULL,
`abl_bal_open_ytd` double NOT NULL,
`abl_bal_ytd` double NOT NULL,
`abl_bal_pd_ytd` double NOT NULL,
`abl_bal_waived_ytd` double NOT NULL,
`abl_bal_chgoff_open_ytd` double NOT NULL,
`abl_bal_chgoff_ytd` double NOT NULL,
`abl_bal_recovered_ytd` double NOT NULL,
`abl_bal_adjusted_plus_ytd` double NOT NULL,
`abl_bal_adjusted_minus_ytd` double NOT NULL,
`abl_bal_billed_ind` varchar(30) NOT NULL,
`abl_interest_accrued_ind` varchar(30) NOT NULL DEFAULT 'y',
`abl_chargeoff_method_cd` varchar(30) NOT NULL,
`abl_reschedule_method_cd` varchar(30) NOT NULL,
`abl_writeoff_method_cd` varchar(30) NOT NULL,
`abl_bal_open_ctd1` double NOT NULL DEFAULT '0',
`abl_bal_ctd1` double NOT NULL,
`abl_bal_pd_ctd1` double NOT NULL,
`abl_bal_waived_ctd1` double NOT NULL,
`abl_bal_chgoff_open_ctd1` double NOT NULL,
`abl_bal_chgoff_ctd1` double NOT NULL,
`abl_bal_recovered_ctd1` double NOT NULL,
`abl_bal_adjusted_plus_ctd1` double NOT NULL,
`abl_bal_adjusted_minus_ctd1` double NOT NULL,
`abl_bal_open_ytd1` double NOT NULL,
`abl_bal_ytd1` double NOT NULL,
`abl_bal_pd_ytd1` double NOT NULL,
`abl_bal_waived_ytd1` double NOT NULL,
`abl_bal_chgoff_open_ytd1` double NOT NULL,
`abl_bal_chgoff_ytd1` double NOT NULL DEFAULT '0',
`abl_bal_recovered_ytd1` double NOT NULL DEFAULT '0',
`abl_bal_adjusted_plus_ytd1` double NOT NULL DEFAULT '0',
`abl_bal_adjusted_minus_ytd1` double NOT NULL DEFAULT '0',
`abl_bill_method_cd` varchar(30) NOT NULL DEFAULT 'lv',
`abl_pmt_amt` double NOT NULL,
`abl_pmt_per` double NOT NULL DEFAULT '0',
`abl_dt` datetime NOT NULL,
`abl_prom_type_cd` varchar(30) NOT NULL,
`abl_rate` double NOT NULL,
`abl_term` double NOT NULL,
`abl_prm_end_dt` datetime NOT NULL,
`abl_due_amt1` double NOT NULL,
`abl_due_amt2` double NOT NULL,
`abl_due_amt3` double NOT NULL,
`abl_due_amt4` double NOT NULL DEFAULT '0',
`abl_due_amt5` double NOT NULL,
`abl_due_amt_pd1` double NOT NULL,
`abl_due_amt_pd2` double NOT NULL,
`abl_due_amt_pd3` double NOT NULL,
`abl_due_amt_pd4` double NOT NULL,
`abl_due_amt_pd5` double NOT NULL,
`abl_pin_code` varchar(30) NOT NULL DEFAULT 'undefined',
`abl_ins_sub_type_cd` varchar(30) NOT NULL DEFAULT 'undefined',
`abl_ins_status_cd` varchar(30) NOT NULL,
`abl_acc_orig_sys_xref` varchar(30) NOT NULL,
`created_by` varchar(30) NOT NULL,
`creation_date` datetime NOT NULL,
`last_updated_by` varchar(30) NOT NULL,
`last_update_date` datetime NOT NULL,
`abl_bal_terminate` double NOT NULL,
`abl_bal_terminate_ctd` double NOT NULL,
`abl_bal_terminate_ytd` double NOT NULL,
`abl_bal_terminate_ctd1` double NOT NULL,
`abl_bal_terminate_ytd1` double NOT NULL,
`abl_terminate_ind` varchar(30) NOT NULL,
`abl_bal_abl_ctd1` double NOT NULL,
`abl_bal_abl_ctd2` double NOT NULL,
`abl_bal_abl_ctd3` double NOT NULL,
`abl_bal_abl_ctd4` double NOT NULL DEFAULT '0',
`abl_bal_abl_ctd5` double NOT NULL DEFAULT '0',
`abl_non_perform_roll_ind` varchar(30) NOT NULL,
`abl_non_perform_txn_type_cd` varchar(30) NOT NULL,
`abl_bal_xfer2non_perform` double NOT NULL,
`abl_bal_pd_xfer2non_perform` double NOT NULL,
`abl_non_perform_bal` double NOT NULL,
`abl_non_perform_bal_pd` double NOT NULL,
`abl_non_perform_bal_pd_excess` double NOT NULL,
`abl_non_perform_bal_waived` double NOT NULL,
`abl_non_perform_bal_adj_plus` double NOT NULL,
`abl_non_perform_bal_adj_minus` double NOT NULL,
`abl_bal_billed` double NOT NULL,
`abl_bal_billed_ctd` double NOT NULL,
`abl_bal_billed_ctd1` double NOT NULL,
`abl_bal_billed_ytd` double NOT NULL,
UNIQUE KEY `account_balances_abl_udx` (`abl_aad_id`,`abl_txn_type_cd`,`abl_prm_code`),
KEY `abl_txn_type_cd` (`abl_txn_type_cd`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
MyISAMで実行されるクエリのDDLは同じです。
これがクエリです:
SELECT
abl_aad_id,
abl_txn_type_cd,
abl_bal_chgoff,
abl_bal_recovered,
abl_bal,
abl_bal_pd,
abl_bal_waived,
abl_bal_adjusted_minus,
abl_bal_adjusted_plus
FROM
account_balances
WHERE abl_txn_type_cd IN ('ADV','INT','FLC','FNSF','FEXT','FOTH1','FPHP','EBKR','ERPO','ESVC','EOTH1','ADV');
SPからの実際のSQLは次のとおりです。
SELECT NOW(), 'Creating tmp_mv_account_balances';
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
SET sql_mode=(SELECT REPLACE(@@sql_mode, 'ONLY_FULL_GROUP_BY', ''));
DROP TABLE IF EXISTS tmp_mv_account_balances;
CREATE TABLE tmp_mv_account_balances AS
SELECT acc_nbr AS "acc_nbr_bal",
abl_aad_id AS "aad_id_bal",
ROUND(SUM( CASE WHEN abl_txn_type_cd = 'ADV' THEN CASE WHEN acc_status_cd = 'CHGOFF' THEN abl_bal_chgoff - abl_bal_recovered ELSE abl_bal - abl_bal_pd - abl_bal_waived - abl_bal_adjusted_minus + abl_bal_adjusted_plus END ELSE 0 END ) , 2) AS "advance_principal_bal",
ROUND(SUM( CASE WHEN abl_txn_type_cd = 'INT' THEN CASE WHEN acc_status_cd = 'CHGOFF' THEN abl_bal_chgoff - abl_bal_recovered ELSE abl_bal - abl_bal_pd - abl_bal_waived - abl_bal_adjusted_minus + abl_bal_adjusted_plus END ELSE 0 END ) , 2) AS "interest_bal",
ROUND(SUM( CASE WHEN abl_txn_type_cd = 'FLC' THEN CASE WHEN acc_status_cd = 'CHGOFF' THEN abl_bal_chgoff - abl_bal_recovered ELSE abl_bal - abl_bal_pd - abl_bal_waived - abl_bal_adjusted_minus + abl_bal_adjusted_plus END ELSE 0 END ) , 2) AS "fee_late_charge_bal",
ROUND(SUM( CASE WHEN abl_txn_type_cd = 'FNSF' THEN CASE WHEN acc_status_cd = 'CHGOFF' THEN abl_bal_chgoff - abl_bal_recovered ELSE abl_bal - abl_bal_pd - abl_bal_waived - abl_bal_adjusted_minus + abl_bal_adjusted_plus END ELSE 0 END ) , 2) AS "fee_nsf_bal",
ROUND(SUM( CASE WHEN abl_txn_type_cd = 'FEXT' THEN CASE WHEN acc_status_cd = 'CHGOFF' THEN abl_bal_chgoff - abl_bal_recovered ELSE abl_bal - abl_bal_pd - abl_bal_waived - abl_bal_adjusted_minus + abl_bal_adjusted_plus END ELSE 0 END ) , 2) AS "fee_extension_bal",
ROUND(SUM( CASE WHEN abl_txn_type_cd = 'FOTH1' THEN CASE WHEN acc_status_cd = 'CHGOFF' THEN abl_bal_chgoff - abl_bal_recovered ELSE abl_bal - abl_bal_pd - abl_bal_waived - abl_bal_adjusted_minus + abl_bal_adjusted_plus END ELSE 0 END ) , 2) AS "fee_convenience_bal",
ROUND(SUM( CASE WHEN abl_txn_type_cd = 'FPHP' THEN CASE WHEN acc_status_cd = 'CHGOFF' THEN abl_bal_chgoff - abl_bal_recovered ELSE abl_bal - abl_bal_pd - abl_bal_waived - abl_bal_adjusted_minus + abl_bal_adjusted_plus END ELSE 0 END ) , 2) AS "fee_phone_pay_bal",
ROUND(SUM( CASE WHEN abl_txn_type_cd = 'EBKR' THEN CASE WHEN acc_status_cd = 'CHGOFF' THEN abl_bal_chgoff - abl_bal_recovered ELSE abl_bal - abl_bal_pd - abl_bal_waived - abl_bal_adjusted_minus + abl_bal_adjusted_plus END ELSE 0 END ) , 2) AS "expense_legal_bal",
ROUND(SUM( CASE WHEN abl_txn_type_cd = 'ERPO' THEN CASE WHEN acc_status_cd = 'CHGOFF' THEN abl_bal_chgoff - abl_bal_recovered ELSE abl_bal - abl_bal_pd - abl_bal_waived - abl_bal_adjusted_minus + abl_bal_adjusted_plus END ELSE 0 END ) , 2) AS "expense_repo_bal",
ROUND(SUM( CASE WHEN abl_txn_type_cd = 'ESVC' THEN CASE WHEN acc_status_cd = 'CHGOFF' THEN abl_bal_chgoff - abl_bal_recovered ELSE abl_bal - abl_bal_pd - abl_bal_waived - abl_bal_adjusted_minus + abl_bal_adjusted_plus END ELSE 0 END ) , 2) AS "expense_dmv_bal",
ROUND(SUM( CASE WHEN abl_txn_type_cd = 'EOTH1' THEN CASE WHEN acc_status_cd = 'CHGOFF' THEN abl_bal_chgoff - abl_bal_recovered ELSE abl_bal - abl_bal_pd - abl_bal_waived - abl_bal_adjusted_minus + abl_bal_adjusted_plus END ELSE 0 END ) , 2) AS "expense_deferred_bal",
ROUND(SUM( CASE WHEN abl_txn_type_cd = 'ADV' THEN CASE WHEN acc_status_cd = 'CHGOFF' THEN abl_bal_chgoff - abl_bal_recovered ELSE abl_bal - abl_bal_pd - abl_bal_waived - abl_bal_adjusted_minus + abl_bal_adjusted_plus - acc_int_rebate_amt END ELSE 0 END ) , 2) AS "net_advance_principal_bal",
acc_int_rebate_amt AS "int_rebate_bal"
FROM
account_balances
JOIN accounts ON abl_aad_id = acc_aad_id
GROUP BY
abl_aad_id;
SELECT NOW(), 'Creating indexes';
ALTER TABLE tmp_mv_account_balances ADD INDEX idx01_bal ( acc_nbr_bal );
ALTER TABLE tmp_mv_account_balances ADD INDEX idx02_bal ( aad_id_bal );
MyISAMは2分で結果を返しますが、InnoDBでは「永久に」かかります。
InnoDBの計画を説明:
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
------ ----------- ---------------- ---------- ------ --------------- ------ ------- ------ -------- -------- -------------
1 SIMPLE account_balances (NULL) ALL abl_txn_type_cd (NULL) (NULL) (NULL) 28858313 100.00 Using where
MyISAMで計画を説明:
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
------ ----------- ---------------- ---------- ------ ------------- ------ ------- ------ -------- -------- -------------
1 SIMPLE account_balances (NULL) ALL (NULL) (NULL) (NULL) (NULL) 33628821 50.00 Using where
ここでは、フォースインデックス(abl_txn_type_cd)
InnoDBの場合、MyISAMにそのインデックスはありませんが、問題はありません。SELECTはInnoDBで永久に実行されます。
id select_type table partitions type possible_keys key key_len ref rows filtered Extra
------ ----------- ---------------- ---------- ------ --------------- --------------- ------- ------ -------- -------- -----------------------
1 SIMPLE account_balances (NULL) range abl_txn_type_cd abl_txn_type_cd 10 (NULL) 40672694 100.00 Using index condition
SHOW TABLE STATUSです InnoDB:
mysql> show table status like 'account_balances';
+------------------+--------+---------+------------+----------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-------------------+----------+-----------------------+---------+
| Name | Engine | Version | Row_format | Rows | Avg_row_length | Data_length | Max_data_length | Index_length | Data_free | Auto_increment | Create_time | Update_time | Check_time | Collation | Checksum | Create_options | Comment |
+------------------+--------+---------+------------+----------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-------------------+----------+-----------------------+---------+
| account_balances | InnoDB | 10 | Compressed | 31858884 | 521 | 16606822400 | 0 | 1128251392 | 4194304 | NULL | 2019-02-09 15:35:01 | NULL | NULL | latin1_swedish_ci | NULL | row_format=COMPRESSED | |
+------------------+--------+---------+------------+----------+----------------+-------------+-----------------+--------------+-----------+----------------+---------------------+-------------+------------+-------------------+----------+-----------------------+---------+
1 row in set (0.00 sec)
これはInnoDBバッファー情報です:
mysql> show global variables like '%innodb_buffer%';
+-------------------------------------+----------------+
| Variable_name | Value |
+-------------------------------------+----------------+
| innodb_buffer_pool_chunk_size | 134217728 |
| innodb_buffer_pool_dump_at_shutdown | ON |
| innodb_buffer_pool_dump_now | OFF |
| innodb_buffer_pool_dump_pct | 25 |
| innodb_buffer_pool_filename | ib_buffer_pool |
| innodb_buffer_pool_instances | 8 |
| innodb_buffer_pool_load_abort | OFF |
| innodb_buffer_pool_load_at_startup | ON |
| innodb_buffer_pool_load_now | OFF |
| innodb_buffer_pool_size | 42949672960 |
+-------------------------------------+----------------+
10 rows in set (0.00 sec)
誰かがいくつかのアドバイスを提供できますか?
お金のためにDOUBLE
(またはFLOAT
)を使用しないでください。2進数と10進数の間の前後が原因で丸めエラーが発生する可能性があります。代わりに、DECIMAL(m,n)
で_m,n
_の適切な値を選択してください。
33M行、各サイズは約1KB。これは、(割り当てられた他のものを許可した後で)buffer_poolを満たすのに十分な量になります。これは、テーブルスキャン(実行中)がほとんどのものをbuffer_poolから押し出し、I/Oバウンドになることを意味します。 pdate: _TABLE STATUS
_は、テーブルのInnoDBバージョンで17GBを示しています。これは、40G buffer_poolの一部では小さすぎて、次の設定を有効にできません。
クエリを再度実行すると、同じことが起こります。I/ Oがバインドされ、すべてがぶつかります。
MyISAMとInnoDBの両方のケースで_SHOW TABLE STATUS
_を提供してください。
OK、なぜMyISAMの方が優れていたのですか?...
行のサイズに関しては、MyISAMの方がきついです。半分くらいの大きさだと思います。だから...しかし、最初に、MyISAMのメモリ使用量について説明しましょう。 「key_buffer」はインデックスブロックのみを保持します。データブロックはOSによって管理されます。 MyISAMタイミングテストで過度のメモリ使用量がなかったと仮定すると、OSはおそらくすべてのデータブロックをRAMに保持する余裕がありました。つまり、同じクエリの2回目の実行は高速で、I/Oバウンドではありません。
しかし... MyISAMテーブルのサイズを2倍にすると、突然、数分ではなく数時間もかかります。
つまり、あなたは崖から落ちています。ただし、InnoDBは、その割り当てメカニズムにより、すぐに機能しなくなります。
じゃあ何をすればいいの??
DECIMAL(11, 2)
は、各DOUBLE
が8バイトであるのに対して、6バイトを取り、最大10億ドル/ユーロ/円などを処理します。EXPLAIN
が見積もるように、2800万行を返す場合、その大量のデータで何を実行しますか?クライアントがそれをさらに処理する場合、おそらくSQLはより多くの処理を実行できますか?