21

我有一个规范化订单数据的大型数据库,查询报告变得非常缓慢。我在报表中使用的许多查询连接了五六个表,并且必须检查数万或数十万行。

有很多查询,并且大多数都已尽可能优化以减少服务器负载并提高速度。我认为是时候开始以非规范化格式保存数据副本了。

关于方法的任何想法?我应该从几个最糟糕的查询开始,然后从那里开始吗?

4

8 回答 8

11

我对 mssql 和 mysql 有更多的了解,但我认为您所说的连接数或行数不会导致您在正确的索引上出现太多问题。您是否分析了查询计划以查看是否缺少任何内容?

http://dev.mysql.com/doc/refman/5.0/en/explain.html

话虽如此,一旦您对索引感到满意并用尽了所有其他途径,反规范化可能是正确的答案。如果您只有一个或两个查询有问题,那么手动方法可能是合适的,而某种数据仓库工具可能更适合创建一个平台来开发数据立方体。

这是我发现的一个涉及该主题的网站:

http://www.meansandends.com/mysql-data-warehouse/?link_body%2Fbody=%7Bincl%3AAggregation%7D

这是一种简单的技术,您可以使用它来保持非规范化查询的简单性,如果您一次只做几个(我不会替换您的 OLTP 表,只是创建一个新的用于报告目的)。假设您的应用程序中有此查询:

select a.name, b.address from tbla a 
join tblb b on b.fk_a_id = a.id where a.id=1

您可以创建一个非规范化表并使用几乎相同的查询进行填充:

create table tbl_ab (a_id, a_name, b_address); 
-- (types elided)

注意下划线与您使用的表别名匹配

insert tbl_ab select a.id, a.name, b.address from tbla a
join tblb b on b.fk_a_id = a.id 
-- no where clause because you want everything

然后修复您的应用程序以使用新的非规范化表,将点切换为下划线。

select a_name as name, b_address as address 
from tbl_ab where a_id = 1;

对于大型查询,这可以节省大量时间并明确数据的来源,并且您可以重复使用已有的查询。

请记住,我只是提倡将其作为最后的手段。我敢打赌,有一些索引会对您有所帮助。当你去规范化时,不要忘记考虑磁盘上的额外空间,并确定何时运行查询来填充新表。这可能应该在晚上,或者活动量低的时候。当然,该表中的数据永远不会是最新的。

[又一次编辑] 不要忘记您创建的新表也需要索引!好的部分是您可以索引到您的心脏内容而不必担心更新锁争用,因为除了您的批量插入之外,该表只会看到选择。

于 2008-08-16T01:36:31.040 回答
2

MySQL 5 确实支持视图,这在这种情况下可能会有所帮助。听起来您已经做了很多优化,但如果没有,您可以使用 MySQL 的EXPLAIN语法来查看实际使用的索引以及降低查询速度的原因。

至于规范化数据(无论您是使用视图还是以更有效的方式复制数据),我认为从最慢的查询开始并逐步完成是一个很好的方法。

于 2008-08-16T00:04:20.523 回答
1

我知道这有点切题,但是您是否尝试过查看是否可以添加更多索引?

我没有很多数据库背景,但我最近经常使用数据库,我发现很多查询可以通过添加索引来改进。

我们正在使用 DB2,并且有一个命令叫做 db2expln 和 db2advis,第一个将指示是否正在使用表扫描与索引扫描,第二个将推荐您可以添加的索引以提高性能。我敢肯定 MySQL 有类似的工具......

无论如何,如果这是你还没有考虑过的事情,它对我有很大帮助......但如果你已经走了这条路,那么我想这不是你想要的。

另一种可能性是“物化视图”(或者他们在 DB2 中称之为),它允许您指定一个基本上由多个表的部分构建的表。因此,您可以提供此视图来访问数据,而不是规范化实际列……但我不知道这是否对插入/更新/删除有严重的性能影响(但如果它是“物化的”,那么它应该有助于选择,因为这些值是单独物理存储的)。

于 2008-08-15T23:58:13.907 回答
1

根据其他一些评论,我肯定会看看你的索引。

我今年早些时候在我们的 MySQL 数据库中发现的一件事是复合索引的强大功能。例如,如果您要报告日期范围内的订单号,则订单号和订单日期列的复合索引可能会有所帮助。我相信 MySQL 只能使用一个索引进行查询,因此如果您只是在订单号和订单日期上有单独的索引,则必须决定只使用其中一个。使用 EXPLAIN 命令可以帮助确定这一点。

为了说明良好索引(包括大量复合索引)的性能,我可以运行查询连接我们数据库中的 3 个表,并且在大多数情况下几乎可以立即获得结果。对于更复杂的报告,大多数查询在 10 秒内运行。这 3 个表分别有 3300 万、1.1 亿和 1.4 亿行。请注意,我们也已经对这些进行了轻微的规范化,以加快我们对数据库最常见的查询。

有关您的表格和报告查询类型的更多信息可能会提供进一步的建议。

于 2008-08-16T02:30:31.947 回答
1

对于 MySQL,我喜欢这个演讲:Real World Web: Performance & Scalability, MySQL Edition。这包含许多不同的建议,以提高 MySQL 的速度。

于 2008-08-18T15:03:36.463 回答
0

您可能还需要考虑选择一个临时表,然后对该临时表执行查询。这将避免为您发出的每个查询重新加入您的表(当然,假设您可以将临时表用于大量查询)。这基本上为您提供了非规范化数据,但如果您只进行选择调用,则无需担心数据一致性。

于 2008-08-16T00:20:37.887 回答
0

除了我之前的回答,我们在某些情况下采取的另一种方法是将关键报告数据存储在单独的汇总表中。即使在非规范化和优化之后,某些报告查询也会变得很慢,我们发现创建一个表并存储整个月的运行总计或摘要信息,这使得月末报告也更快。

我们发现这种方法很容易实现,因为它不会破坏任何已经在工作的东西——它只是在某些点进行额外的数据库插入。

于 2008-08-16T02:53:00.027 回答
0

我一直在玩复合索引,并看到了一些真正的好处......也许我会设置一些测试,看看是否可以在这里拯救我......至少更长一点。

于 2008-08-17T16:52:00.257 回答