Percona 5.7 tokudb 查询性能不佳 - 选择了错误的(非聚集)索引

Ros*_*oss 2 mysql locking percona-server update tokudb

我有一个大约有 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'
Run Code Online (Sandbox Code Playgroud)

执行此更新需要 40 秒以上的时间。还有其他类似的更新经常发生,但是这台机器的 I/O 子系统并没有受到丝毫压力(RAID SSD),并且还有大量可用的 RAM。

EXPLAIN 产量:

 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'
Run Code Online (Sandbox Code Playgroud)

基于此 - 它选择PRIMARY索引而不是其他索引之一,例如cl_unique_idx在前两个位置的 where 语句中具有两列的索引。所以我很难过为什么计划者选择了PRIMARY而不是导致性能如此糟糕。以下是索引列表:

+----+-------------+-------------------+------------+-------+------------------------------------------------------------+---------+---------+------+------+----------+------------------------------+
| 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)
Run Code Online (Sandbox Code Playgroud)

我不得不将tokudb_lock_timeout4 秒增加到 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
Run Code Online (Sandbox Code Playgroud)

每次更新时更新的行数最多为 1-3。更新可以以大约每秒 1 次到大约 3-4 次或高达每秒几十次的速度发生。

这是在 Percona Server 5.7 上。

小智 6

与 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/blob (> 32K)、大量快速存储(尽管 TokuDB 可以减少闪存磨损)或具有小数据集这是物理内存大小的一小部分,TokuDB 可能不适合您。

我现在还注意到你说你只有 100GB 的数据但有 500GB 的内存(100GB innodb 缓冲池)。在这种情况下,您的大部分/所有数据都适合内存。InnoDB 应该是这里明显的性能赢家。TokuDB(尚未)针对内存工作负载进行了优化,在这种情况下,InnoDB 几乎 100% 会击败它。现在,如果您有 100GB 的内存和 TB 的数据和索引,那么 TokuDB 值得考虑。

(我是 Percona 的软件工程师。)