web-dev-qa-db-ja.com

MyISAMと比較すると、SELECTの実行がInnoDBで遅い

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)

誰かがいくつかのアドバイスを提供できますか?

2
varnar

お金のために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億ドル/ユーロ/円などを処理します。
  • テーブルを分割します(垂直分割)。
  • サマリーテーブルを作成して維持します。
  • より多くのRAMを取得し、より多くのRAMを取得して...
  • (私のお気に入り)クエリの必要性を再考します。 EXPLAINが見積もるように、2800万行を返す場合、その大量のデータで何を実行しますか?クライアントがそれをさらに処理する場合、おそらくSQLはより多くの処理を実行できますか?
  • などアプリケーションの詳細を説明してください。おそらく役に立つ手がかりがあります。
2
Rick James