【问题标题】:How can I optimize my database query without denormalizing?如何在不进行非规范化的情况下优化我的数据库查询?
【发布时间】:2013-11-10 21:10:23
【问题描述】:

我有一个 percona mysql 5.6.13 数据库,其表如下:

CREATE TABLE `table1` (
  `table1_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `created_at` datetime NOT NULL,
  PRIMARY KEY (`table1_id`),
  KEY `created_at` (`created_at`)
) ENGINE=InnoDB;

CREATE TABLE `table2` (
  `table1_id` int(10) unsigned NOT NULL,
  `cost` decimal(6,2) NOT NULL DEFAULT '0.00',
  KEY `table1_id` (`table1_id`)
) ENGINE=InnoDB;


CREATE TABLE `table3` (
  `table1_id` int(10) unsigned NOT NULL,
  `partner` enum('partner1', 'partner2', 'partner3', 'partner4') NOT NULL DEFAULT 'partner1',
  KEY `table1_id` (`table1_id`)
) ENGINE=InnoDB;

每个表都有大约 150 万行。

当我运行以下查询时,每次都需要 18 秒。

SELECT t3.partner, SUM(t2.cost) AS cost FROM table1 t1 JOIN table2 t2 ON t1.table1_id = t2.table1_id JOIN table3 t3 ON t1.table1_id = t3.table1_id WHERE t1.created_at >= '2005-07-01' AND t1.created_at < '2008-09-20' GROUP BY 1;

如果我将成本/合作伙伴字段非规范化为 table1,如下所示:

ALTER TABLE table1 ADD `cost` decimal(6,2) NOT NULL DEFAULT '0.00', ADD `partner` enum('partner1', 'partner2', 'partner3', 'partner4') NOT NULL DEFAULT 'partner1', ADD KEY `partner` (`partner`);
UPDATE table1 t1 JOIN table2 t2 ON t1.table1_id = t2.table1_id SET t1.cost = t2.cost;
UPDATE table1 t1 JOIN table3 t3 ON t1.table1_id = t3.table1_id SET t1.partner = t3.partner;

然后运行这个查询:

SELECT t1.partner, SUM(t1.cost) AS cost FROM table1 t1 WHERE t1.created_at >= '2005-07-01' AND t1.created_at

第一次需要 6 秒,之后每次 2 秒(估计是 mysql 缓存的缘故)。

我想我希望找到的可能是优化/缓存原始查询而不会使数据非规范化的某种方式。
我不能只合并表格(因为示例中未包含其他字段,但出于测试/此处准确的目的我将其删除)。我可以跨表复制数据,但我不喜欢这样做,而且似乎应该有比这更好的解决方案。
可以尝试任何数据库设置吗?
也许 NoSQL 具有更完全非规范化的数据——在这种情况下聚合工作会相当快吗?
谢谢:)

附言一条评论要求查询计划——where 子句选择的行数是所有行数。如果我不考虑 where,结果相同,这是查询计划:

+----+-------------+-------+-------+--------------------+------------+---------+------------------------+--------+-----------------------------------------------------------+
| id | select_type | table | type  | possible_keys      | key        | key_len | ref                    | rows   | Extra                                                     |
+----+-------------+-------+-------+--------------------+------------+---------+------------------------+--------+-----------------------------------------------------------+
|  1 | SIMPLE      | t1    | range | PRIMARY,created_at | created_at | 5       | NULL                   | 766380 | Using where; Using index; Using temporary; Using filesort |
|  1 | SIMPLE      | t3    | ref   | table1_id,partner  | table1_id  | 4       | lsfs_main.t1.table1_id |      1 | NULL                                                      |
|  1 | SIMPLE      | t2    | ref   | table1_id          | table1_id  | 4       | lsfs_main.t1.table1_id |      1 | NULL                                                      |
+----+-------------+-------+-------+--------------------+------------+---------+------------------------+--------+-----------------------------------------------------------+

【问题讨论】:

  • 索引您在联接中使用的列?
  • 如果您查看创建表查询,我相信我已经在那里创建了所有需要的索引。连接中使用的唯一字段是 table1_id,它在所有 3 个表中都有索引。
  • 查询执行计划是什么?结果中的典型行数是多少?
  • 我在上面添加了执行计划。所有行都被选中。

标签: mysql optimization database-design


【解决方案1】:

您缺少 table2table3 的主键。我建议至少有一个包含所有两列的table3 的多列主键。由于 InnoDB-Tables 是索引组织的表,这应该会显着减少 table3 的查找。有了这样的主键,MySQL 能够直接从索引中检索所有相关数据,而无需进一步查找。字段table1_id 必须位于多列主键的首位。

对于table2 这并不容易,因为(table1_id, cost) 不是唯一的。

【讨论】:

  • 至少在我的示例中,我实际上可以为 table2 和 table3 添加多列主键。我喜欢这个想法,所以我会赞成你的答案,尽管它实际上并不能解决我的问题。我尝试添加您建议的密钥,但没有任何区别。我认为加入成本很高,这就是问题所在。也许我会以不同的方式重新表述这个问题。我希望得到一些关于 mysql 服务器参数的建议以尝试调整,或者可能是关于 nosql 的信息,所以我会更具体地说明这一点。谢谢:)
猜你喜欢
  • 2021-08-04
  • 2010-10-22
  • 2017-10-05
  • 2014-04-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-04
  • 2017-01-27
相关资源
最近更新 更多