約8.5m行のテーブルがあります。テーブルはtokudbで、以下に説明するインデックスがあります。次のような更新ステートメントを実行しようとすると、パフォーマンスが著しく低下します。
update retail.lw_item_discovery
set price = 'X',
prev_price = 'Y',
last_updated = '2016-04-13',
last_price_change = '2016-04-13'
where market = 'XX'
and sku = '123456'
この更新を実行するには、40秒以上かかります。このような他の更新が頻繁に行われていますが、このマシンのI/Oサブシステムにはストレスがほとんどなく(レイドSSD)、十分なRAMも利用可能です。
EXPLAIN
利回り:
+----+-------------+-------------------+------------+-------+------------------------------------------------------------+---------+---------+------+------+----------+------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------------------+------------+-------+------------------------------------------------------------+---------+---------+------+------+----------+------------------------------+
| 1 | UPDATE | lw_item_discovery | NULL | index | cl_unique_idx,cl_mkt_sku_upd_avail_idx,market_sku_item_idx | PRIMARY | 4 | NULL | 100 | 100.00 | Using where; Using temporary |
+----+-------------+-------------------+------------+-------+------------------------------------------------------------+---------+---------+------+------+----------+------------------------------+
1 row in set (0.00 sec)
これに基づいて-最初の2つの位置にwhereステートメントの両方の列があるcl_unique_idx
などの他のインデックスの代わりにPRIMARY
インデックスを選択しています。そのため、プランナが代わりにPRIMARY
を選択していて、パフォーマンスが非常に低下している理由に困惑しています。以下はインデックスのリストです。
+-------------------+------------+--------------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-------------------+------------+--------------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| lw_item_discovery | 0 | PRIMARY | 1 | itd_id | A | 82 | NULL | NULL | | BTREE | | |
| lw_item_discovery | 0 | cl_unique_idx | 1 | sku | A | 82 | NULL | NULL | | BTREE | | |
| lw_item_discovery | 0 | cl_unique_idx | 2 | market | A | 82 | NULL | NULL | | BTREE | | |
| lw_item_discovery | 0 | cl_unique_idx | 3 | upc | A | 82 | NULL | NULL | | BTREE | | |
| lw_item_discovery | 0 | cl_unique_idx | 4 | model_num | A | 82 | NULL | NULL | | BTREE | | |
| lw_item_discovery | 0 | cl_unique_idx | 5 | item_id | A | 82 | NULL | NULL | YES | BTREE | | |
| lw_item_discovery | 1 | update_idx | 1 | last_updated | A | 82 | NULL | NULL | | BTREE | | |
| lw_item_discovery | 1 | update_idx | 2 | market | A | 82 | NULL | NULL | | BTREE | | |
| lw_item_discovery | 1 | update_idx | 3 | sku | A | 82 | NULL | NULL | | BTREE | | |
| lw_item_discovery | 1 | description_idc | 1 | web_description | A | 82 | NULL | NULL | | BTREE | | |
| lw_item_discovery | 1 | category_idx | 1 | web_category | A | 82 | NULL | NULL | | BTREE | | |
| lw_item_discovery | 1 | category_idx | 2 | upc | A | 82 | NULL | NULL | | BTREE | | |
| lw_item_discovery | 1 | category_idx | 3 | sku | A | 82 | NULL | NULL | | BTREE | | |
| lw_item_discovery | 1 | upc_idx | 1 | upc | A | 82 | NULL | NULL | | BTREE | | |
| lw_item_discovery | 1 | item_id_idx | 1 | item_id | A | 82 | NULL | NULL | YES | BTREE | | |
| lw_item_discovery | 1 | item_id_idx | 2 | market | A | 82 | NULL | NULL | | BTREE | | |
| lw_item_discovery | 1 | item_id_idx | 3 | available | A | 82 | NULL | NULL | | BTREE | | |
| lw_item_discovery | 1 | cl_mkt_sku_upd_avail_idx | 1 | sku | A | 82 | NULL | NULL | | BTREE | | |
| lw_item_discovery | 1 | cl_mkt_sku_upd_avail_idx | 2 | market | A | 82 | NULL | NULL | | BTREE | | |
| lw_item_discovery | 1 | cl_mkt_sku_upd_avail_idx | 3 | last_updated | A | 82 | NULL | NULL | | BTREE | | |
| lw_item_discovery | 1 | cl_mkt_sku_upd_avail_idx | 4 | available | A | 82 | NULL | NULL | | BTREE | | |
| lw_item_discovery | 1 | market_sku_item_idx | 1 | market | A | 82 | NULL | NULL | | BTREE | | |
| lw_item_discovery | 1 | market_sku_item_idx | 2 | sku | A | 82 | NULL | NULL | | BTREE | | |
| lw_item_discovery | 1 | market_sku_item_idx | 3 | item_id | A | 82 | NULL | NULL | YES | BTREE | | |
+-------------------+------------+--------------------------+--------------+-----------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
24 rows in set (0.00 sec)
ロック待機の競合が大量に発生しないように、tokudb_lock_timeout
を4秒から40秒に増やす必要がありました。ここで何か不足していますか?
`lw_item_discovery` (
`item_id` bigint(20) unsigned DEFAULT '0',
`chain` varchar(12) NOT NULL DEFAULT 'lowes',
`market` varchar(4) NOT NULL DEFAULT '',
`available` varchar(1) NOT NULL DEFAULT 'y',
`last_updated` date NOT NULL DEFAULT '0000-00-00',
`itd_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`web_description` varchar(255) NOT NULL DEFAULT '',
`model_num` varchar(100) NOT NULL DEFAULT '' COMMENT 'its only 1char cause its not currently used. Its here for consistency',
`price` decimal(6,2) NOT NULL DEFAULT '0.00',
`item_link_url` text NOT NULL,
`item_img_url` text NOT NULL,
`store_shopped` smallint(5) unsigned NOT NULL DEFAULT '0',
`sku` varchar(32) NOT NULL DEFAULT '0',
`upc` varchar(12) NOT NULL DEFAULT '',
`web_category` varchar(255) NOT NULL DEFAULT '',
`mfr` varchar(100) NOT NULL DEFAULT '',
`class` tinyint(3) unsigned NOT NULL DEFAULT '0',
`subclass` tinyint(3) unsigned NOT NULL DEFAULT '0',
`first_found` date NOT NULL DEFAULT '0000-00-00' COMMENT 'first time it was seen in market',
`last_price_change` date NOT NULL DEFAULT '0000-00-00' COMMENT 'the date of the last price change observed',
`discontinued` varchar(1) NOT NULL DEFAULT 'n',
`discontinued_date` date NOT NULL DEFAULT '0000-00-00',
`prev_price` decimal(6,2) unsigned NOT NULL DEFAULT '0.00',
`rating` decimal(4,2) NOT NULL DEFAULT '-1.00',
`review_count` int(11) NOT NULL DEFAULT '-1',
PRIMARY KEY (`itd_id`),
UNIQUE KEY `cl_unique_idx` (`sku`,`market`,`upc`,`model_num`,`item_id`),
KEY `update_idx` (`last_updated`,`market`,`sku`),
KEY `description_idc` (`web_description`),
KEY `category_idx` (`web_category`,`upc`,`sku`),
KEY `upc_idx` (`upc`),
KEY `item_id_idx` (`item_id`,`market`,`available`) USING BTREE,
KEY `cl_mkt_sku_upd_avail_idx` (`sku`,`market`,`last_updated`,`available`),
CLUSTERING KEY `market_sku_item_idx` (`market`,`sku`,`item_id`)
) ENGINE=TokuDB AUTO_INCREMENT=8858224 DEFAULT CHARSET=latin1
更新される行数は、更新ごとに最大1〜3である必要があります。更新は、通常、1秒あたり1回から3〜4回、おそらく1秒あたり数十回の頻度で発生します。
これはPercona Server 5.7にあります。
最終的には、テーブルのダンプとリロードにより、奇妙なカーディナリティと行数の動作が解決されました。分析テーブルを使用してみましたが、問題は解決しませんでした。 ジョージの答え は非常によくできていますが、残念ながら問題は解決しません。
InnoDBとは異なり、TokuDBはこれまでカーディナリティ統計を自動的に計算しませんでした。ユーザーは、これらの値を計算するためにANALYZE TABLE
を手動で実行する必要がありました。
5.6.27-76.0より前に作成されたすべてのテーブルとインデックスも、正確な行数を維持しません。 5.6.27-76.0以降、新しいテーブルとインデックス、およびRECOUNT ROWS
分析が行われたテーブルはすべて、行数を正確に追跡していました。これは、カーディナリティメトリック、特にパーティション分割されたテーブルのカーディナリティにとって非常に重要です。
分析の変更について説明している次のドキュメントを参照してください。
5.7.11-4より前は、自動バックグラウンド分析はデフォルトで無効でした。 5.7.11-4以降、テーブルの約30%が変更(挿入/更新/削除)された場合、自動バックグラウンド分析がデフォルトで有効になります。上記のリンクに記載されているさまざまなシステム変数を操作することにより、このしきい値や分析の他のいくつかの側面を変更できます。
5.6.27-76.0より新しいサーバーにデータを再ロードすると、不正確な行数が修正され、5.7.11-4に移動すると、自動バックグラウンド分析が有効になります。
TokuDBを使用する場合は、理由を確認する必要があります。TokuDBは「すべてのロードでInnoDBよりも優れている」わけではありません。これには特定の利点とトレードオフがあり、InnoDBほどパフォーマンスがよくなく、一般にInnoDBほど成熟していないユースケースがあります。
圧縮が必要な場合、挿入の負荷が高い場合、ストレージが遅い場合、またはデータセットが使用可能なメモリを大幅に上回る場合は、TokuDBが適しています。生のランダムポイントクエリのパフォーマンスが必要な場合、大量の逐次削除に続いてクエリをカバーする場合、大きなchar/varchar/blobs(> 32K)の場合、十分な高速ストレージ(TokuDBはフラッシュの摩耗を減らすことができます)、または小さなデータセットそれは物理メモリサイズの小さな倍数ですが、TokuDBはおそらくあなたには向いていません。
また、100 GBのデータしかなく、500 GBのメモリ(100 GBのinnodbバッファープールを使用)があると言っていることにも気づきました。これは、ほとんど/すべてのデータがメモリに収まるケースです。 InnoDBは、ここで明らかにパフォーマンスの勝者になるはずです。 TokuDBは(まだ)インメモリワークロード用に最適化されていないため、この状況ではInnoDBがほぼ100%の時間で勝ちます。 100 GBのメモリとTB=のデータとインデックスがある場合は、TokuDBを検討する価値があります。
(私はPerconaのソフトウェアエンジニアです。)