如何在不进行非规范化的情况下优化数据库查询?

时间:2013-10-31 01:11:46

标签: mysql optimization database-design

我有一个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;

如果我将cost / partner字段反规范化为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&gt; ='2005-07-01'AND t1.created_at&lt; '2008-09-20'集团   BY 1;

第一次需要6秒,然后每次需要2秒(因为可能是因为mysql缓存)。

我想我希望找到的可能是优化/缓存原始查询的一些方法,而不会对数据进行非规范化。
我不能只合并表(因为示例中没有包含其他字段,但为了测试/准确,我删除了这些字段)。我可以在表格中复制数据,但我不是那个人的忠实粉丝,似乎应该有更好的解决方案。
要尝试的任何数据库设置?
也许NoSQL具有更完全非规范化的数据 - 在这种情况下聚合工作会合理地快速完成吗? 谢谢:))

P.S。一条评论要求查询计划 - 由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                                                      |
+----+-------------+-------+-------+--------------------+------------+---------+------------------------+--------+-----------------------------------------------------------+

1 个答案:

答案 0 :(得分:1)

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

对于table2而言,这并不容易,因为(table1_id, cost)并非唯一。