什么是非规范化 mysql 数据库的好方法?
Posted
技术标签:
【中文标题】什么是非规范化 mysql 数据库的好方法?【英文标题】:What is a good way to denormalize a mysql database? 【发布时间】:2010-09-05 23:42:27 【问题描述】:我有一个规范化订单数据的大型数据库,查询报告变得非常缓慢。我在报告中使用的许多查询会连接五六个表,并且必须检查数万或数十万行。
有很多查询,并且大多数都已尽可能优化以减少服务器负载并提高速度。我认为是时候开始以非规范化格式保存数据副本了。
关于方法的任何想法?我应该从几个最糟糕的查询开始,然后从那里开始吗?
【问题讨论】:
【参考方案1】:对于 mysql,我喜欢这个演讲:Real World Web: Performance & Scalability, MySQL Edition。这包含许多不同的建议,以提高 MySQL 的速度。
【讨论】:
【参考方案2】:我一直在玩复合索引,并看到了一些真正的好处...也许我会设置一些测试,看看是否可以在这里节省我的时间.. 至少更长一点。
【讨论】:
【参考方案3】:在我之前的回答中,我们在某些情况下采取的另一种方法是将关键报告数据存储在单独的汇总表中。即使在非规范化和优化之后,某些报告查询也会变得很慢,我们发现创建一个表并存储整个月的运行总计或摘要信息,这使得月末报告也更快。
我们发现这种方法易于实施,因为它不会破坏任何已经在工作的东西 - 它只是在某些点进行额外的数据库插入。
【讨论】:
【参考方案4】:与其他一些 cmets 一致,我肯定会看看您的索引。
我今年早些时候在我们的 MySQL 数据库中发现的一件事是复合索引的强大功能。例如,如果您要报告日期范围内的订单号,则订单号和订单日期列的复合索引可能会有所帮助。我相信 MySQL 只能为查询使用一个索引,所以如果您只是在订单号和订单日期上有单独的索引,它就必须决定只使用其中一个。使用 EXPLAIN 命令可以帮助确定这一点。
为了说明良好索引(包括大量复合索引)的性能,我可以运行查询连接我们数据库中的 3 个表,并在大多数情况下获得几乎即时的结果。对于更复杂的报告,大多数查询在 10 秒内运行。这 3 个表分别有 3300 万、1.1 亿和 1.4 亿行。请注意,我们也已经对这些进行了轻微的规范化,以加快我们对数据库最常见的查询。
有关您的表格和报告查询类型的更多信息可能会提供进一步的建议。
【讨论】:
【参考方案5】:我对 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;
对于大型查询,这可以节省大量时间并明确数据的来源,并且您可以重复使用已有的查询。
请记住,我只是提倡将此作为最后的手段。我敢打赌,有一些索引会对您有所帮助。当你去规范化时,不要忘记考虑磁盘上的额外空间,并确定何时运行查询来填充新表。这可能应该在晚上,或者活动量低的时候。当然,该表中的数据永远不会是最新的。
[另一个编辑]不要忘记您创建的新表也需要索引!好的部分是您可以根据自己的意愿编制索引,而不必担心更新锁争用,因为除了批量插入之外,表格只会看到选择。
【讨论】:
【参考方案6】:您可能还需要考虑选择一个临时表,然后对该临时表执行查询。这将避免为您发出的每个查询重新加入您的表(当然,假设您可以将临时表用于大量查询)。这基本上为您提供了非规范化数据,但如果您只进行选择调用,则无需担心数据一致性。
【讨论】:
【参考方案7】:MySQL 5 确实支持views,这在这种情况下可能会有所帮助。听起来您已经做了很多优化,但如果没有,您可以使用 MySQL 的 EXPLAIN 语法来查看实际使用了哪些索引,以及是什么降低了查询速度。
就数据规范化(无论您是使用视图还是只是以更有效的方式复制数据)而言,我认为从最慢的查询开始并逐步完成是一个不错的方法。
【讨论】:
【参考方案8】:我知道这有点切题,但是您是否尝试过查看是否可以添加更多索引?
我没有太多的DB背景,但我最近经常使用数据库,我发现很多查询可以通过添加索引来改进。
我们正在使用 DB2,并且有一个命令叫做 db2expln 和 db2advis,第一个将指示是否正在使用表扫描与索引扫描,第二个将推荐您可以添加的索引以提高性能。我确定 MySQL 也有类似的工具...
无论如何,如果这是你还没有考虑过的事情,它对我有很大帮助......但如果你已经走这条路,那么我想这不是你想要的。
另一种可能性是“物化视图”(或者他们在 DB2 中称之为),它允许您指定一个基本上由多个表的部分组成的表。因此,您可以提供此视图来访问数据,而不是规范化实际列...但我不知道这是否对插入/更新/删除有严重的性能影响(但如果它是“物化的”,那么它应该有助于选择,因为这些值是单独物理存储的)。
【讨论】:
以上是关于什么是非规范化 mysql 数据库的好方法?的主要内容,如果未能解决你的问题,请参考以下文章