查看原文
其他

百密一疏,防不胜防,细数那些大型数据库建设过程中绕不开的坑

CSDN 2020-12-18

构建大型数据库时,无论最开始的设计多么精妙,到后续操作的时候或多或少都会遇到一些问题,本文将来细数大型数据库中不可避免会遇到的问题。

原文标题:Feature Casualties of Large Databases

原文链接:https://brandur.org/large-database-casualties


译者 | 弯月  责编 | 张文
头图 | CSDN 下载自东方 IC
出品 | CSDN(ID:CSDNnews)

以下为译文:

项目之初,开发人员精心设计了一个非常精巧的小数据库,然而几年后,随着数据量的膨胀,数据库就会变得臃肿不堪,惨不忍睹。你是否也有同样的经历?
出现这种情况的原因可以大致归结为以下几个方面:
  • 技术上的限制:基础技术不支持规模扩展,无法保证分布式系统的事务特性和参照完整性。
  • 稳定性:某些操作的风险太大,例如无法预估性能的批处理更新操作。
  • 成本:最佳实践经验太过于昂贵,例如迁移大量的现有数据。
  • 省事:有时为了快速交付项目,我们不得不“走一些捷径”。
由于上述种种原因,我们不得不在一开始就考虑复杂的数据库系统。然而,高级数据库也并非万能良药,如果使用不当,最终的结果与简单的键值存储也没有区别。而且,我们将数据的可靠性和正确性统统推给了数据库,应用程序本身也会千疮百孔。
下面,我们就来看一看大型数据库中的一些不可避免的问题。


ACID

 
数据库事务具有四大特性 ACID:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)以及持久性(Durability)
然而,在实践中,这四大特性往往会被率先打破,因为这些特性带来的价值在系统初期、流量较小、业务简单的时候往往无法体现出来,而且它们本身也会给编写代码带来一些困难。所以,这四大特性首当其冲无法被保全,特别是当团队中有经验不足的程序员时。


参照完整性

 
参照完整性规则不允许关系中有不存在的实体引用,即外键只能引用某个已有的主键。在删除数据时,如果该数据被外键引用,则需要通过 ON DELETE RESTRICT 来阻止删除操作,或通过 ON DELETECASCADE 将外键数据一起删除(即级联删除)。
参照完整性可以确保数据的正确性。这种数据库的强制操作可以方便程序的编写,否则我们就需要在每次读取数据库后,立即判空。
然而,在现实世界中,我们往往会牺牲参照完整性。有时是由于技术限制,有时是由于可靠性问题(担心级联删除误删数据),而有时仅仅是因为参照完整性会带来“不必要”的麻烦,特别是在数据迁移时,我们需要特别小心表的迁移顺序。


可为空字段

 
通常,大型数据库中都会包含很多可为空字段。出现这种情况的主要原因是数据结构的定义不理想。然而,在编写代码的时候,我们需要检查每个可为空字段的值是否为空,否则一旦出现空值,代码就会抛出异常。很多 bug 都是由空值引发的。
一般,数据库都会默认字段可为空,除非你明确指定 NOT NULL。然而,一旦指定 NOT NULL 之后,诸多烦恼也会随之而来,比如后添加的字段需要迁移现有数据或添加默认值等。
此外,我们还面临一些技术限制。例如,在 Postgres 中,即使在迁移完数据后,在可空列上执行 SET NOT NULL 也是不安全的,因为 Postgres 会验证表中没有空值,这会导致对整个表进行扫描,从而引发严重的性能问题。


索引

 
索引可以为数据库的查找提供极大的便利性。然而,大型系统中的索引可能会非常复杂,常见的原因包括:
  • 需要在多个集群之上建立索引。
  • 由于构建索引会影响生产操作,因此在忙碌的节点上构建索引的风险很大。开发人员需要构建专门的工具来暂停索引的构建。
  • 数据量非常庞大时,构建索引的耗时非常长。
  • 复杂索引的存储成本很高。
索引最大的好处就是可以提升性能,然而由于大型数据库中的索引无法得到最佳优化,因此受益也非常有限。此外,如此复杂的索引本身对于系统来说就是一个负担。


数据查询

 
SQL 是非常强大的数据库查询语言。然而,过于复杂的 SQL 语句也可能对生产带来负面影响,例如不可预估的性能或锁定等问题。
一般,我们会禁止使用大型的 SQL 语句,并要求开发人员将拆分成多个语句,逐步运行。有些系统甚至禁止更新一行以上的数据,这种做法虽然有助于提高系统性能,但对开发人员而言则苦不堪言。
此外,很多数据库产品都提供了 SQL 诊断工具,帮助我们分析造成性能瓶颈的SQL 语句。


数据库的扩展

 
通常,小型的开源项目都会采用键值存储类的数据库,这类数据库的使用和维护简单直白,且非常灵活。但为了系统的可扩展性,特别是对于分布式系统,在达到一定的规模后,底层需要功能更为强大的大型数据库支持。
尽管大型数据库固有一些弊端,但我们也不必过于悲观,如今 Citus、Spanner和 CockroachDB 等系统都在努力改善分布式数据库的操作性,例如:
  • 可在紧急情况下,暂停索引的构建。
  • 将可为空字段改为不可为空时,不必扫描整个表。
  • 默认字段为 NOT NULL。
  • 构建更好的 SQL 语句诊断工具,及时发现 SQL 语句中的性能问题。
希望将来的大型数据库在保证良好的扩展性的同时,能够克服自身固有的缺陷,并多多吸收小型数据库的优势,为所有人提供稳定可靠的数据库。

原文:https://brandur.org/large-database-casualties

本文为 CSDN 翻译,转载请注明来源出处。


更多精彩推荐

为什么苹果 M1 芯片如此之快?

一行代码能做什么? 看到最后一个我终于忍不住...

JavaScript:对象都是这样生成的!

网易首支 AI 生成歌曲《醒来》正式发布;FSF :苹果 OCSP 事故在道德上不可接受;CentOS 8.3 发布|极客头条

买房必看!又一程序员自编“购房宝典”火爆 GitHub

挑战 TensorFlow、PyTorch,谁才是中国 AI 开源框架之星?

沉痛哀悼!现代无线网络之父 Norman Abramson 辞世,享年 88 岁

点分享点点赞点在看

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存