Percona 5.7 tokudb 查詢性能差 - 選擇了錯誤的(非聚集)索引
我有一張桌子,裡面有大約 850 萬行。該表是 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)
基於此 - 它選擇
PRIMARY
索引而不是其他索引之一,例如cl_unique_idx
在前兩個位置的 where 語句中具有兩列。所以我很困惑為什麼計劃者會選擇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 次到 3 到 4 次或高達每秒幾十次的速度發生。
這是在 Percona Server 5.7 上。
最終 - 轉儲表並重新載入解決了奇怪的基數和行計數行為。我們嘗試使用分析表,但這並沒有解決問題。 喬治的回答做得很好,但不幸的是它不能解決我的問題。
與 InnoDB 不同,TokuDB 歷來不會自動計算基數統計資訊。作為使用者,您需要手動執行
ANALYZE TABLE
才能計算這些值。在 5.6.27-76.0 之前創建的所有表和索引也不會保持準確的行數。在 5.6.27-76.0 之後,新表和索引以及具有
RECOUNT ROWS
分析的表都將準確跟踪行數。這對於基數度量非常重要,特別是對於分區表的基數。請參閱以下描述分析更改的文件:
- TokuDB 後台分析表(Percona Server 5.6)
- TokuDB 後台分析表(Percona Server 5.7)
在 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 可能不適合你。
我現在還注意到您說您只有 100GB 的數據,但有 500GB 的記憶體(帶有 100GB innodb 緩衝池)。在這種情況下,您的大部分/所有數據都將適合記憶體。InnoDB 應該是這裡明顯的性能贏家。TokuDB(尚未)針對記憶體中的工作負載進行了優化,在這種情況下,InnoDB 幾乎 100% 會擊敗它。現在,如果你有 100GB 的記憶體和 1TB 的數據和索引,那麼 TokuDB 就值得考慮。
(我是 Percona 的軟體工程師。)