我正在对数据库进行一些研究,并且正在研究关系数据库的一些局限性。
我知道大表的联接非常昂贵,但是我不确定为什么。 DBMS需要执行什么才能执行联接操作,瓶颈在哪里?
非规范化如何帮助克服这一费用?其他优化技术(例如,索引编制)如何提供帮助?
欢迎亲身经历!如果要发布资源链接,请避免使用Wikipedia。我知道在哪里可以找到。
与此相关,我想知道诸如BigTable和SimpleDB之类的云服务数据库使用的非规范化方法。参见this question。
最佳答案
去规范化以提高性能?听起来很令人信服,但它没有水。
与Ted Codd博士一起工作的Chris Date是关系数据模型的最初支持者,他对耐不规范化的误导论证不耐烦,并使用科学方法系统地拆除了它们:他拥有大型数据库并测试了这些断言。
我认为他是在1988-1991年关系数据库写作中写的,但后来被本书《数据库系统入门》第六版(这是有关数据库理论和设计的权威文章)编入第八版,在我撰写时可能还会保留几十年来一直在打印。当我们大多数人仍赤脚奔跑时,克里斯·达特曾是该 Realm 的专家。
他发现:
一切都归结为减轻工作集的规模。包含正确选择的索引且具有正确设置的索引的联接便宜而又不昂贵,因为它们可以在实现行之前对结果进行大量修剪。
实现结果涉及批量磁盘读取,这是该练习中最昂贵的一个数量级。相比之下,执行联接在逻辑上仅需要检索键。实际上,甚至不获取键值:键哈希值用于连接比较,从而减轻了多列连接的成本,并从根本上降低了涉及字符串比较的连接的成本。不仅更适合缓存,而且磁盘读取的工作量也要少得多。
而且,一个好的优化器将选择限制性最强的条件并在执行联接之前应用它,非常有效地利用联接对基数高的索引的高选择性。
诚然,这种类型的优化也可以应用于非规范化的数据库,但是那种想要对规范进行非规范化的人通常在设置索引时就不会考虑基数。
重要的是要了解,表扫描(在生成联接的过程中检查表中的每一行)在实践中很少见。仅当以下一项或多项成立时,查询优化器才会选择表扫描。
执行一项操作要比不执行一项操作花费更多。但是,执行错误的操作(被迫进入无意义的磁盘I/O,然后在执行您真正需要的连接之前丢弃浮渣)要昂贵得多。即使预先计算了“错误”的操作并且明智地应用了索引,仍然存在很大的代价。尽管需要进行更新异常,但通过非规范化来预先计算联接是对特定联接的 promise 。如果您需要其他联接,那么该 promise 将使您付出巨大代价。
如果有人想提醒我这是一个不断变化的世界,我认为您会发现gruntier硬件上的更大数据集只会夸大Date的发现的范围。
对于所有在计费系统或垃圾邮件生成器上工作(对您感到羞耻)并且愤慨地动手写键盘的人,告诉我您知道反规范化速度更快的事实,对不起,但您生活在特殊的环境中情况-具体来说,是按顺序处理所有数据的情况。这不是一般情况,您的策略是合理的。
您没有理由错误地概括它。有关在数据仓库场景中适当使用非规范化的更多信息,请参见注释部分的末尾。
我也想回应
多么胡扯。限制要尽早应用,首先要限制最大。您已经阅读了该理论,但尚未理解。仅查询优化器将联接视为“谓词适用于笛卡尔积”。这是一种符号表示形式(实际上是一种归一化),有助于符号分解,因此优化器可以生成所有等效转换,并按成本和选择性对它们进行排名,以便可以选择最佳查询计划。
获得生产笛卡尔乘积的优化器的唯一方法是不提供谓词:
SELECT * FROM A,B
笔记
David Aldridge提供了一些重要的附加信息。
实际上,除了索引和表扫描之外,还有许多其他策略,而现代的优化器将在制定执行计划之前将它们全部花费。
一条实用的建议:如果可以将其用作外键,则对其进行索引,以便优化器可以使用索引策略。
我曾经比MSSQL优化器更聪明。那在两个版本之前改变了现在,它通常教会了我。实际上,它是一个专家系统,可以将许多非常聪明的人的所有智慧汇集到一个足够封闭的 Realm 中,从而使基于规则的系统有效。
“Bollocks”可能没有 react 。我被要求不要那么傲慢,并提醒我数学不是在撒谎。的确如此,但数学模型的所有含义不一定都应从字面上理解。如果您仔细地避免检查它们的荒谬性(双关),并确保在尝试解释方程式之前先将其全部抵消,则负数的平方根非常有用。
我之所以如此大胆地回应的原因是,措词如下:
这可能不是什么意思,但是它是写的,而且绝对是错误的。笛卡尔积是一个关系。联接是一种功能。更具体地说,联接是关系值函数。如果谓词为空,则将生成笛卡尔乘积,对其进行检查是对数据库查询引擎的一次正确性检查,但实际上没有人编写不受约束的联接,因为它们在类外没有任何实用值(value)。
我之所以这样称呼,是因为我不希望读者陷入将模型与建模对象混淆的古老陷阱。模型是一个近似值,为方便操作而特意简化。
在不同数据库引擎之间,选择表扫描联接策略的临界值可能会有所不同。它受许多实现决策的影响,例如,树节点填充因子,键值大小和算法的精妙之处,但从广义上讲,高性能索引的执行时间为k log n + c。 C项是固定的开销,主要由建立时间组成,曲线的形状意味着直到n达到数百时,您才能获得 yield (与线性搜索相比)。
有时非正规化是个好主意
非规范化是对特定联接策略的 promise 。如前所述,这会干扰其他联接策略。但是,如果您有大量的磁盘空间,可预测的访问模式以及倾向于处理很多或全部磁盘的趋势,那么预先计算联接可能非常值得。
您还可以找出您的操作通常使用的访问路径,并为这些访问路径预先计算所有联接。这是数据仓库背后的前提,或者至少是在知道他们为什么要做自己的事情的人而不是仅仅为了遵守流行语而构建它们的时候。
经过适当设计的数据仓库会通过从规范化交易处理系统中进行批量转换而定期生成。操作和报告数据库的这种分离具有消除OLTP和OLAP(在线事务处理即数据输入和在线分析处理即报告)之间的冲突的非常理想的效果。
这里的重要一点是,除了定期更新之外,数据仓库是只读的。这就提出了更新异常的问题。
不要犯对OLTP数据库(进行数据输入的数据库)进行规范化的错误。计费运行可能会更快,但如果这样做,则会出现更新异常。有没有试图让《读者文摘》停止向您发送东西?
这些天磁盘空间很便宜,所以请自行解决。但是非规范化只是数据仓库故事的一部分。预先计算的汇总值可带来更大的性能提升:每月总计,诸如此类。总是要减少工作量。
类型不匹配的ADO.NET问题
假设您有一个SQL Server表,其中包含varchar类型的索引列,并且使用AddWithValue传递参数来约束对该列的查询。 C#字符串是Unicode,因此推断的参数类型将是NVARCHAR,与VARCHAR不匹配。
从VARCHAR到NVARCHAR的转换是一个扩大的转换,因此它隐式发生-但是,告别索引,祝您好运。
“计算磁盘命中率”(Rick James)
如果所有内容都缓存在RAM中,则
JOINs
相当便宜。也就是说,规范化不会带来太多性能损失。如果“规范化”模式导致
JOINs
大量击中磁盘,但是等效的“非规范化”模式则不必击中磁盘,那么非规范化将赢得性能竞争。关于database - 什么时候以及为什么数据库连接昂贵?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/173726/