2011-09-07 118 views
1

我有一个设计问题与db非规范化。
我正在制作一个相对较大的数据库,需要尽可能优化它。mysql数据库链表非规范化

这是一个非常简化的问题模型。
图片中的所有表格都链接在一起,并且使用规范化数据库来获取来自特定国家的所有用户,例如我必须加入所有表格。那是cca 250个国家x cca 12000个城市x cca 625000个地区x?地址x?用户...总之,这是一个很多的加入,这需要很长的时间。

我想要做的是在user表中使country_id冗余,因此我可以在没有任何连接的情况下获得相同的查询。

问题是,在这样一个模型中保持一致性的最佳实践是什么(使用MySql btw)?

单向和可能最快的方法是在插入/更新/删除数据时确保应用程序级别的一致性。

其他是存储过程,我真的没有看到任何优势。只有直接调用才能确保一致性。如果你想在没有程序的情况下进行一些修改,一致性就会中断。

我也一直在寻找触发器......不太清楚如何实现它,以及我会在性能上获得多少收益。

无论如何,最好确保数据库级别的一致性。

有什么建议吗?

Sample model

回答

0

这不是真的很多加盟假设你是不是要反规范化您的所有数据。另一方面,大多数人只需要一个地址表,或者(gasp!)将地址信息保存在用户表中。您希望支持多少个国家/城市,以及有多少用户?

+0

正如我所说,这是一个简化的模型。这种结构是必要的。至于记录的数量,你必须能够在世界的任何地方进行注册,所以帖子中的数字几乎可以召唤它。有多少用户很难预测......无论如何它必须能够处理几十万用户。另外值得一提的是,很多查询都是针对特定国家/地区的,因此一次又一次地加入所有内容似乎太多了 – ZolaKt

+0

然后在users表中有一个countryID,一个StateID和一个......等等。除非您需要水印一些复杂的商业目的,我强烈建议避免它。 –

+0

好的,但问题是如何确保一致性是一个这样的模型,而不是真正的使用它。 – ZolaKt

4

所有的图片中的表链式,并用标准化 数据库获得例如所有来自特定国家的用户我 必须参加所有表。

您必须加入所有表格,因为您使用代理键(id号),而不是因为表格是“标准化”的。使用像id号这样的代理键与标准化没有任何关系。

自然键和外键约束是您解决问题所需的全部。

查看工作原理的最简单方法是从完整数据开始,并完全倒退。假设所有的数据都是正确的。

addr_id street   street_num  region city   country 
-- 
1  Babukiaeeva  3a    10000  Zagreb  Croatia 
2  Riva    16    51000  Rijeka  Croatia 
3  Andrije Hebranga 2-4    10000  Zagreb  Croatia 
4  Andrijeviaeeva 2    110000  Zagreb  Croatia 

录制像“地区‘10000’与城市‘萨格勒布’在全国‘克罗地亚’相关”的事实,创建一个新表,并从该查询填充它。

SELECT DISTINCT region, city, country from addresses; 

表看起来是这样的。

Table: regions 
Primary key: {region, city, country} 

region city  country 
-- 
10000 Zagreb Croatia 
51000 Rijeka Croatia 
110000 Zagreb Croatia 

然后设置外键引用。

ALTER TABLE addresses 
ADD CONSTRAINT FOREIGN KEY  (region, city, country) 
       REFERENCES regions (region, city, country); 

录制像“城市‘萨格勒布’是在国家“克罗地亚”的事实,创建一个新的表,从该查询填充它。

SELECT DISTINCT city, country from regions; 

表看起来像这样。

Table: cities 
Primary key: {city, country} 

city  country 
-- 
Zagreb Croatia 
Rijeka Croatia 

然后设置外键引用。

ALTER TABLE regions 
ADD CONSTRAINT FOREIGN KEY  (city, country) 
       REFERENCES cities (city, country); 

对国家重复。表格中的国家,城市和地区都非常重要,所以他们在5NF。 (他们不能有任何非关键的依赖关系,因为他们没有非关键列。)在像大部分欧洲一样的大面积的情况下,地址表很可能也在5NF。

查询性能的条件可能会围绕您当前的模式运行,因为它不需要连接。

您可能会想要使用ON UPDATE CASCADE;虽然你可能想要级联删除而不是

+0

这是一个完全冗余的方法。我认为最好不要因为性能原因使用组合键。但我仍然不相信这是实现它的方式,因为它复制了一切/无处不在。我认为生病时最终会采用物化视图方法 – ZolaKt

+0

“冗余”是关系理论中的一个技术术语;它与存储外键没有任何关系。 (这就是外键的用途。)*这些*组合键消除了所有联接。在大多数情况下,这种模式将比3或4个连接执行得更快。我已经运行了这样的测试,执行速度提高了200倍。在提交此模式或实体化视图之前,您应测试性能。 –

+0

是的,它们消除了连接,这是正确的......但是它们复制了大量数据(每个表格中的所有“较低”键),并且存在前置问题。合成键具有较低的性能(至少在MySql中),所以我认为它更好地介入一个新增的ID,并使合成UNIQUE成为可能。但是,无论如何:我更喜欢这种物化视图方法。仍然存在持续性问题,但至少在“原始”表中没有冗余 – ZolaKt

1

首先 - 它真的太慢了​​吗? 你试过了吗? 你有一个应用程序在哪里转储所有用户(为什么?),或者你现在或时间只抓取一个/几个用户。由于您在所有这些ID上都有主键,因此检索速度应该不会那么慢,毕竟背景中存在B树。其次,我不会在街道号码处规范化,你很难从中获得任何好处,并且你最终可能会在用户和地址之间产生几乎1:1的关系。因此,将您的街道号码移动到客户端,或者甚至可以将整个地址表移动到用户。我可能会将地区表也移到客户端(这些是城市地区?),并最终得到用户,城市和国家的表格。

然后你会有两个连接,如果这仍然太慢,你可以把冗余的国家关键字(或者,因为我们正在反规范 - 国名)在用户。我会使用触发器来保持完整性,更确切地说,您必须编写以下内容:插入和更新触发器(更新需要仅在regionId/cityId更改时触发)以及国家/地区的更新触发器(如果您的国家/地区名称为在不太可能发生国家名称变化的情况下用户表)。性能方面,你不会获得,但会失去触发器,但我想用户表上的插入和更新并不是很频繁,以至于你会注意到它。

最后,由于您没有详细解释您(web?)应用的性质和规模,只是提醒您可能还想考虑/在关系数据库之外考虑其他优化技术(缓存, sql dbs等)。

+0

地址用于几件事情,不仅仅是用户......所以我不能将它移动到用户表中。是的地区是城市地区。他们也应该保持独立,因为他们在很多地方(比城市更多)使用。好吧,我使用提到的物化视图方法使用触发器。 – ZolaKt