同様の質問があった場合は、事前に謝罪します。私はGoogleとStackExchangeを調べて答えを見つけようとしてきましたが、今では行き詰まって困っています。私はまだDB管理の世界では比較的新しいです。
Backgorund
データベースに接続してインターネットAPIから取得した情報を保存するローカルプログラムを作成しました。通常、それは約9-10分(私のプログラムの計算時間を含む)で8,000以上のレコードを更新します。ただし、MySqlサーバーをバージョン8.0.20に更新した後、実行時間が29〜30分に急増していることに気付きました。
私のプログラムは各反復で2つのテーブルを更新し、コードの実行ブロックをストップウォッチタイマークラスにラップして実行速度をデバッグしました。私のデータベースは次のように設定されています。
+------------------------+
| diagnostic |
| fundamentals |
| historicalData |
| messages |
| info |
+------------------------+
5 rows in set (0.00 sec)
最初は、最初の数百のレコードはすべて比較的同じ速度で迅速に更新されます。
Sample output from software:
API CONNECTION: 0.760000 seconds (<- Response time from the API)
-DB Fetch time: 0.177000 seconds (<- Query that selects data from the info table)
-EXECUTION SPEED: 0.186000 seconds (<- Query to update the info table query)
-EXECUTION SPEED: 0.843000 seconds (<- Query to updates the historicalData table)
Updated batch in: 3.716000 seconds (<- Total time)
約800レコード後、問題は悪化し始めます。
Sample output from software:
API CONNECTION: 0.747000 seconds (<- Response time from the API)
-DB Fetch time: 0.153000 seconds (<- Query that selects data from the info table)
-EXECUTION SPEED: 0.310000 seconds (<- Query to update the info table query)
-EXECUTION SPEED: 7.858000 seconds (<- Query to updates the historicalData table)
Updated batch in: 10.612000 seconds (<- Total time)
最も遅い速度は、historicalDataテーブルの更新中、クエリごとに15〜20秒でした。
(クエリ内のデータはMySQL Workbenchの速度を低下させ、非常に再現が困難になるため、この方法で速度を診断しました。)
上記を前提として、historicalDataテーブルに注目しました。
mysql> show columns in historicalData;
+-----------------+------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+------------+------+-----+---------+-------+
| symbol | char(10) | NO | PRI | NULL | |
| 12_month | json | YES | | NULL | |
| 6_month | json | YES | | NULL | |
| 3_month | json | YES | | NULL | |
| 5_day | json | YES | | NULL | |
| my_last_updated | mediumtext | YES | | NULL | |
+-----------------+------------+------+-----+---------+-------+
6 rows in set (0.00 sec)
使用されるクエリ構文は、プログラムで生成されるinfoテーブルとhistoricalDataテーブルの両方で同じです。たとえば、これは、historicalDataを更新するために使用されるクエリです。
INSERT INTO `historicalData` (`symbol`, `5_day`, `12_month`, `6_month`, `3_month`, `my_last_updated`)
VALUES ( ## Appropriate values, including a very long JSON string as `12_month` that would spam out the question on StackExchange ## )
AS new(`symbol`, `5_day`, `12_month`, `6_month`, `3_month`, `my_last_updated`)
ON DUPLICATE KEY UPDATE `symbol`=new.symbol,`5_day`=new.5_day,`12_month`=new.12_month,`6_month`=new.6_month,`3_month`=new.3_month,`my_last_updated`=new.my_last_updated;
メモリ:
私のサーバーは3.4プロセッサで8GのRAMを実行しており、MySql以外のサービスは実行していません。
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 5497159680
Dictionary memory allocated 672373
Buffer pool size 327680
Free buffers 199182
Database pages 128470
Old database pages 47382
Modified db pages 2099
Pending reads 0
Pending writes: LRU 0, flush list 6, single page 0
Pages made young 767195, not young 205843
0.00 youngs/s, 0.00 non-youngs/s
Pages read 183858, created 554254, written 1094269
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
Buffer pool hit rate 1000 / 1000, young-making rate 46 / 1000 not 0 / 1000
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 128470, unzip_LRU len: 0
I/O sum[6180]:cur[96], unzip sum[0]:cur[0]
sys.innodb_buffer_stats_by_table:
+-----------------------------------------------+------------+------------+-------+--------------+-----------+-------------+
| object_name | allocated | data | pages | pages_hashed | pages_old | rows_cached |
+-----------------------------------------------+------------+------------+-------+--------------+-----------+-------------+
| diagnostic | 416.00 KiB | 326.05 KiB | 26 | 0 | 22 | 74 |
| fts_0000000000000af9_0000000000000a25_index_1 | 16.00 KiB | 5.32 KiB | 1 | 0 | 1 | 85 |
| fts_0000000000000af9_0000000000000a25_index_2 | 544.00 KiB | 251.52 KiB | 34 | 0 | 34 | 4580 |
| fts_0000000000000af9_0000000000000a25_index_3 | 176.00 KiB | 136.38 KiB | 11 | 0 | 11 | 2499 |
| fts_0000000000000af9_0000000000000a25_index_4 | 160.00 KiB | 118.54 KiB | 10 | 0 | 10 | 2257 |
| fts_0000000000000af9_0000000000000a25_index_5 | 224.00 KiB | 176.48 KiB | 14 | 0 | 14 | 3282 |
| fts_0000000000000af9_0000000000000a25_index_6 | 96.00 KiB | 60.51 KiB | 6 | 0 | 6 | 1168 |
| fts_0000000000000af9_0000000000000a2c_index_1 | 16.00 KiB | 5.07 KiB | 1 | 0 | 1 | 79 |
| fts_0000000000000af9_0000000000000a2c_index_2 | 288.00 KiB | 238.78 KiB | 18 | 0 | 18 | 4336 |
| fts_0000000000000af9_0000000000000a2c_index_3 | 176.00 KiB | 136.38 KiB | 11 | 0 | 11 | 2499 |
| fts_0000000000000af9_0000000000000a2c_index_4 | 160.00 KiB | 118.54 KiB | 10 | 0 | 10 | 2257 |
| fts_0000000000000af9_0000000000000a2c_index_5 | 224.00 KiB | 176.48 KiB | 14 | 0 | 14 | 3282 |
| fts_0000000000000af9_0000000000000a2c_index_6 | 96.00 KiB | 60.51 KiB | 6 | 0 | 6 | 1168 |
| fts_0000000000000af9_being_deleted | 16.00 KiB | 0 bytes | 1 | 0 | 1 | 0 |
| fts_0000000000000af9_being_deleted_cache | 16.00 KiB | 0 bytes | 1 | 0 | 1 | 0 |
| fts_0000000000000af9_config | 16.00 KiB | 233 bytes | 1 | 0 | 1 | 6 |
| fts_0000000000000af9_deleted | 16.00 KiB | 0 bytes | 1 | 0 | 1 | 0 |
| fts_0000000000000af9_deleted_cache | 16.00 KiB | 0 bytes | 1 | 0 | 1 | 0 |
| fundamentals | 2.31 MiB | 2.06 MiB | 148 | 0 | 148 | 6301 |
| historicalData | 3.30 MiB | 2.73 MiB | 211 | 198 | 83 | 8502 |
| messages | 64.00 KiB | 31.69 KiB | 4 | 0 | 2 | 87 |
| info | 3.53 MiB | 3.17 MiB | 226 | 205 | 212 | 8459 |
+-----------------------------------------------+------------+------------+-------+--------------+-----------+-------------+
Mysql-slow.logのサンプル
# Time: 2020-05-06T00:31:52.558595Z
# User@Host: me[me] @ Host [IP Address] Id: 383
# Query_time: 13.655712 Lock_time: 0.039945 Rows_sent: 0 Rows_examined: 0
SET timestamp=1588725098;
INSERT INTO `historicalData` (`symbol`, `5_day`, `12_month`, `6_month`, `3_month`, `my_last_updated`) VALUES ... etc etc
私が試したこと:
8.0.19にロールバックする前に、私は試しました:
12_month
、my_last_updated
) (変化なし)他に何を試したらよいかわからないので、8.0.20に更新するときにこの問題が発生したことに少し不満を感じています。私が8.0.20に更新した日まで、すべてがスムーズに実行されました(更新後にプログラムが実行された最初のインスタンスは、これらの症状が現れました)。ここにいる誰かが同じまたは類似の問題を抱えていたか、私のパフォーマンスを8.0.19の周りに戻す方法を知っていることを願っています。
[〜#〜] edit [〜#〜]要求どおり、以下は説明データです
EXPLAIN INSERT... ON DUPLICATE KEY...
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | INSERT | historicalData | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
+----+-------------+----------------+------------+------+---------------+------+---------+------+------+----------+-------+
SHOW CREATE TABLE historicalData;
+----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| historicalData | CREATE TABLE `historicalData` (
`symbol` char(10) NOT NULL,
`12_month` json DEFAULT NULL,
`6_month` json DEFAULT NULL,
`3_month` json DEFAULT NULL,
`5_day` json DEFAULT NULL,
`my_last_updated` mediumtext,
PRIMARY KEY (`symbol`),
UNIQUE KEY `symbol` (`symbol`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 |
+----------------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
mysql> explain select count(*) from historicalData;
+----+-------------+----------------+------------+-------+---------------+--------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------------+------------+-------+---------------+--------+---------+------+------+----------+-------------+
| 1 | SIMPLE | historicalData | NULL | index | NULL | symbol | 10 | NULL | 1 | 100.00 | Using index |
+----+-------------+----------------+------------+-------+---------------+--------+---------+------+------+----------+-------------+
申し訳ありませんが、私は率直に言っています。
増え続ける文字列(共産主義者、JSON、TEXTなど)へのデータ収集の使用を放棄します。これは特に株価の履歴データに当てはまります。見積もりごとに1行を使用する方がはるかに効果的です。
(いいえ、なぜ遅くなったのかは説明できません。ただし、再現可能なテストケースを提供できる場合は、バグレポートをbugs.mysql.comに提出してください)
もっと...
FULLTEXT
ファイルがいくつかあります。しかし、このテーブルにはFTインデックスはありませんね?
PRIMARY KEY
はUNIQUE KEY
、これは冗長であり、削除する必要があります:
UNIQUE KEY `symbol` (`symbol`)