【问题标题】:Mysql Partitioning Query PerformanceMysql 分区查询性能
【发布时间】:2020-03-03 23:31:30
【问题描述】:

我在定价表上创建了分区。下面是alter语句。

ALTER TABLE `price_tbl` 
PARTITION BY HASH(man_code)
PARTITIONS 87;

一个分区由 435510 条记录组成。 price_tbl 中的总记录为 600 万。

EXPLAIN 查询只显示一个部分用于查询。查询仍然需要 3-4 秒 才能执行。以下是查询

 EXPLAIN SELECT vrimg.image_cap_id,vm.man_name,vr.range_code,vr.range_name,vr.range_url, MIN(`finance_rental`) AS from_price, vd.der_id AS vehicle_id FROM `range_tbl` vr 
    LEFT JOIN `image_tbl` vrimg ON vr.man_code = vrimg.man_code AND vr.type_id = vrimg.type_id AND vr.range_code = vrimg.range_code 
    LEFT JOIN `manufacturer_tbl` vm ON vr.man_code = vm.man_code AND vr.type_id = vm.type_id 
    LEFT JOIN `derivative_tbl` vd ON vd.man_code=vm.man_code AND vd.type_id = vr.type_id AND vd.range_code=vr.range_code 
    LEFT JOIN `price_tbl` vp ON vp.vehicle_id = vd.der_id AND vd.type_id = vp.type_id AND vp.product_type_id=1 AND vp.maintenance_flag='N'  AND vp.man_code=164 
    AND vp.initial_rentals_id =(SELECT rental_id FROM `rentals_tbl` WHERE rental_months='9') 
    AND vp.annual_mileage_id =(SELECT annual_mileage_id FROM `mileage_tbl` WHERE annual_mileage='8000') 
    WHERE vr.type_id = 1 AND vm.man_url = 'audi' AND vd.type_id IS NOT NULL GROUP BY vd.der_id

解释的结果。

没有分区的相同查询需要 3-4 秒。 分区查询需要 2-3 秒。

我们如何提高查询性能,因为它太慢了。

附加创建表结构。

  1. 价格表 - 包含 600 万条记录
CREATE TABLE `price_tbl` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `lender_id` bigint(20) DEFAULT NULL,
  `type_id` bigint(20) NOT NULL,
  `man_code` bigint(20) NOT NULL,
  `vehicle_id` bigint(20) DEFAULT NULL,
  `product_type_id` bigint(20) DEFAULT NULL,
  `initial_rentals_id` bigint(20) DEFAULT NULL,
  `term_id` bigint(20) DEFAULT NULL,
  `annual_mileage_id` bigint(20) DEFAULT NULL,
  `ref` varchar(255) DEFAULT NULL,
  `maintenance_flag` enum('Y','N') DEFAULT NULL,
  `finance_rental` decimal(20,2) DEFAULT NULL,
  `monthly_rental` decimal(20,2) DEFAULT NULL,
  `maintenance_payment` decimal(20,2) DEFAULT NULL,
  `initial_payment` decimal(20,2) DEFAULT NULL,
  `doc_fee` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`,`type_id`,`man_code`),
  KEY `type_id` (`type_id`),
  KEY `vehicle_id` (`vehicle_id`),
  KEY `term_id` (`term_id`),
  KEY `product_type_id` (`product_type_id`),
  KEY `finance_rental` (`finance_rental`),
  KEY `type_id_2` (`type_id`,`vehicle_id`),
  KEY `maintenanace_idx` (`maintenance_flag`),
  KEY `lender_idx` (`lender_id`),
  KEY `initial_idx` (`initial_rentals_id`),
  KEY `man_code_idx` (`man_code`)
) ENGINE=InnoDB AUTO_INCREMENT=5830708 DEFAULT CHARSET=latin1
/*!50100 PARTITION BY HASH (man_code)
PARTITIONS 87 */
  1. 派生表 - 这包含 18k 条记录。
CREATE TABLE `derivative_tbl` (
  `type_id` bigint(20) DEFAULT NULL,
  `der_cap_code` varchar(20) DEFAULT NULL,
  `der_id` bigint(20) DEFAULT NULL,
  `body_style_id` bigint(20) DEFAULT NULL,
  `fuel_type_id` bigint(20) DEFAULT NULL,
  `trans_id` bigint(20) DEFAULT NULL,
  `man_code` bigint(20) DEFAULT NULL,
  `range_code` bigint(20) DEFAULT NULL,
  `model_code` bigint(20) DEFAULT NULL,
  `der_name` varchar(255) DEFAULT NULL,
  `der_url` varchar(255) DEFAULT NULL,
  `der_intro_year` date DEFAULT NULL,
  `der_disc_year` date DEFAULT NULL,
  `der_last_spec_date` date DEFAULT NULL,
  KEY `der_id` (`der_id`),
  KEY `type_id` (`type_id`),
  KEY `man_code` (`man_code`),
  KEY `range_code` (`range_code`),
  KEY `model_code` (`model_code`),
  KEY `body_idx` (`body_style_id`),
  KEY `capcodeidx` (`der_cap_code`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
  1. 范围表 - 包含 1k 条记录
CREATE TABLE `range_tbl` (
  `type_id` bigint(20) DEFAULT NULL,
  `man_code` bigint(20) DEFAULT NULL,
  `range_code` bigint(20) DEFAULT NULL,
  `range_name` varchar(255) DEFAULT NULL,
  `range_url` varchar(255) DEFAULT NULL,
  KEY `range_code` (`range_code`),
  KEY `type_id` (`type_id`),
  KEY `man_code` (`man_code`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

【问题讨论】:

  • 您应该更担心查询的正确性,因为这通常不是如何使用GROUP BY .. 启用 sql_mode ONLY_FULL_GROUP_BY 的 MySQL 5.7.5+ 可以信任此查询为那里支持检测功能依赖性,您在这里信任它.. .. 假设查询是功能依赖性的,我没有检查...
  • 分区并不是提高性能(喷气机)的万能子弹,这可能会在未来发生变化,因为 MySQL 开发团队正在努力对 InnoDB 引擎的Parallel Query Execution 支持。还有另一篇文章MySQL 8.0.14: A Road to Parallel Query Execution is Wide Open! 确实表明它仍然非常有限,但这是一个正确的开始,我们等了很久吗?..
  • ... 同样从查询和解释的角度来看,我很确定 SQL 重写是获得更好性能的最佳选择,因为优化器接缝以“错误”顺序访问表防止临时表和排序..
  • 请参阅Why should I provide a Minimal Reproducible Example for a very simple SQL query? 以提供示例数据和预期结果,以便我们可以验证 SQL 重写。也请执行SHOW VERSION(),因为 MySQL 优化器在主要 MySQL 版本之间可能非常不同。.跨度>
  • 附加信息请求。在 pastebin.com 上发布并分享链接。 RAM 大小,# 核心,MySQL 主机服务器上的任何 SSD 设备来自 SSH 登录根,文本结果:B) SHOW GLOBAL STATUS;至少 24 小时正常运行时间后 C) 显示全局变量; D) 显示完整的处理程序; E) 完整的 MySQLTuner 报告和可选的非常有用的信息,如果可用包括 - htop OR top OR mytop 用于大多数活动应用程序,ulimit -a 用于 linux/unix 限制列表,iostat -xm 5 3 用于按设备和核心/cpu 的 IOPS count,为服务器工作负载调优分析提供建议。

标签: mysql partitioning mysql-5.7


【解决方案1】:

如果您希望提高性能,PARTITION BY HASH 基本上是无用的。 BY RANGE几个用例中很有用_。

大多数情况下,索引的改进与尝试使用分区一样好。

一些可能的问题:

  • 对于 InnoDB 表没有明确的 PRIMARY KEY。添加自然 PK(如果适用),否则添加 AUTO_INCREMENT
  • 没有“复合”索引——它们通常可以提高性能。例子:vrvrimg之间的LEFT JOIN涉及3列; “右”表中这 3 列的复合索引可能有助于提高性能。
  • 当较小的数据类型可以工作时,盲目使用BIGINT。 (当表很大时,这是一个 I/O 问题。)
  • VARCHAR中盲用255。
  • 考虑大部分列是否应为NOT NULL
  • 该查询可能是“explode-implode”综合症的受害者。这是您执行JOIN(s) 的地方,它会创建一个大的中间表,然后是GROUP BY 以降低行数。
  • 不要使用LEFT,除非“正确”表确实是可选的。 (我看到LEFT JOIN vd ... vd.type_id IS NOT NULL。)
  • 不要标准化“连续”值(annual_mileage 和 rent_months)。它对“=”测试并没有真正的好处,而且会严重影响“范围”测试的性能。

没有分区的相同查询需要 3-4 秒。分区查询需要 2-3 秒。

在分区和非分区之间切换时,索引几乎总是需要更改。使用每种情况的最佳索引,我预测性能将接近相同。

索引

无论是否分区,这些都应该有助于提高性能:

vm:     (man_url)
vr:     (man_code, type_id)  -- either order
vd:     (man_code, type_id, range_code, der_id)
              -- `der_id` 4th, else in any order (covering)
vrimg:  (man_code, type_id, range_code, image_cap_id)
              -- `image_cap_id` 4th, else in any order (covering)
vp:     (type_id, der_id, product_type_id, maintenance_flag,
         initial_rentals, annual_mileage, man_code)
             -- any order (covering)

“覆盖”索引是一种额外的提升,因为它可以在索引的 BTree 中完成所有工作,而无需触及数据的 BTree。

实施我推荐的一些内容,然后回来(在另一个问题中)进行进一步调整。

通常“分区键”应该在复合索引中的最后。

【讨论】:

    猜你喜欢
    • 2011-08-13
    • 1970-01-01
    • 2012-07-07
    • 2011-05-06
    • 2011-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多