为什么我喜欢数据库?没那么复杂和吓人

发表于:2015-2-10 10:20

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:伍翀    来源:51Testing软件测试网采编

  去年我在 Square 的工作中接触到了很多种数据库。包括:
  发现和解决数据库性能问题。
  为新应用设计数据模型和分片策略。
  评估和推行新的数据库。
  起初是为需求所迫,但我很快就对数据库着迷了。数据库的交叉研究几乎横贯了计算科学的每个领域——它的理论和实现都非常复杂,而且富有挑战性。 然而,我很快意识到这并非所有的人都像我一样热衷于数据库。对于我的很多同事和朋友而言,数据库是一个具有魔力的黑盒子系统,太吓人太复杂了以至于不能理解。我想要改变一下这个现状。 当谈论到数据库时,分布式系统的话题是不能忽略的。大部分现代数据库都是分布式的,要么是隐式的(分布式集群数据库),要么是显式的(通过应用程序级的分片连接到多个数据库的单个应用程序)。 这篇文章是我喜欢数据库和分布式系统的告白。它主要针对向我一样的程序员,经常接触数据库的应用开发者。我们主要用 Java、Python、或是 Ruby 编码,用来写服务端的应用。本文会覆盖到以下话题:
  比较和评估不同的数据库。
  如何理解和充分利用你的数据库。
  从大的层面上去理解数据库是如何工作的。
  首先,什么是数据库
  在这篇文章中,任何接收并存储数据以备将来获取之用的软件就是数据库。这包括了传统的 RDBMS 和 NoSQL 数据库,以及如 Apache Zookeeper 和 Kafka 一样的系统。
  CAP 理论
  CAP 理论。这是继图灵的停机问题和 P≠NP (技术上不可解)后,我最喜欢的不可解问题。CAP 理论表明,任何分布式系统最多只能同时满足 CP(一致性 & 分区容忍性), AP (可用性 & 分区容忍性),或者介于这两者之间。因此,一致性和可用性之间有很有趣的权衡出现。 关于CAP定理几个重要的误解:
  传统的“三选二”争论是没有意义的。你不能抛弃分区容忍性,因为那意味着“在分区中执行的操作行为是不确定的”,而且在这种情况下,数据库并不是真正一致性的。
  到达 CAP 理论的限制并没有默认给定。有很多的数据库,它们既不是一致的,可用的,也不是分区容忍的。要实现 CAP 理论的限制需要精心的设计和实现。
  分布式系统
  如前所述,许多现代数据库都以某种方式实现了分布式。推动数据库的分布式化的两个因素:
  为了规模上超越单机 —— 在多节点上存储和处理数据。
  为了增加可用性 —— 确保数据库不会发生单点故障。
  这两个目标是相互紧密关联的。一般来说,通过增加机器数量来扩展系统会对可用性产生负面影响,因为发生单个机器故障的机会增加了。所以,实现高可用性几乎是可扩展性的先决条件。
  正确性和效率
  正确性和效率两者都重要,而且在分布式数据库中两者也是紧密关联的。 在任何软件中正确性都是重要的,但是对于数据库来说它是必不可少的。因为(1)数据库存储数据,错误的数据在重启后仍然存在。(2)数据库被认为是软件栈(software stack)中最值得信赖的基础。 数据库是正确的是什么意思呢?许多分布式数据库的代码很难懂,有惊人一致的语义。在这里你可以做一个权衡。在一般情况下,对于效率和可用性的成本来说,更严格的一致性使得编写应用程序更容易。 除了理论之外,还有实现和业务挑战的正确性。分布式系统本质上是复杂的。像 Paxos 这种算法是很难理解和正确实现的。随着系统越来越复杂,更多隐蔽的故障场景会出现。像 Redis 和 ElasticSearch 就承受了由他们分布式系统的非常规设计带来的考验。 除了上述的权衡,效率也是很重要的,因为以前遇到的困难。我学到越多的底层编码,我就更了解一台机器美秒可以执行多少原始操作(raw operations)。在许多情况下,效率降低了复杂度,使整个系统更简单。事实是分布式系统比底层编码更激发我去追求效率。在给定相同负载的情况下,我更乐意选择需要更少机器的数据库。 最后,用于机器间协调的计算和延迟开销会是很显著的。总之:运行的部件越少越好。 为了从代码中优化出更多的性能和效率,深入更底层的抽象中是必须的旅程,包括:
  内存分配 和 垃圾收集器 [2]
  文件系统调度 和 IO 设备特性
  内核设置
  各种系统调用的实现细节(fork,execv,malloc)
  它们中产生任何的不协调,都会导致性能不佳。你不必成为一个内核黑客(kernel hacker),但是你需要对这些组件之间的交互有一定高度的了解。
  给应用授权
  由于不同的编程语言都有自己的优缺点,数据库也有自己独特的特点。完全理解它们是很重要的。它可以让你实现高效和复杂的应用程序,同时委托大部分复杂易错的工作给数据库。 一般来说,如果你没有严格的性能或可用性要求,那么传统 RDBMS 是一个好选择。ACID 的保证是非常强大的,而且工具也很不错。分片 RDBMS 虽然痛苦,但它是很好理解的。MySQL 和 Postgres 是两个普遍的选择。 全文搜索引擎允许你构建高级的索引和搜索功能。集成这些系统后的最终一致性,很少是个问题,因为搜索本身就是一个模糊操作。Lucene 和它的变种数据库(Solr,ElasticSearch)是普遍的选择。 消息队列和事件处理系统消除了那些很难正确和高效实现的代码。Kafka、Storm、Spark SQL、RabbitMQ 和 Redis 是普遍的选择。 具有跨域复制的数据库使得区域故障切换和高可用性容易地多。在这方面没有很多开源选择,但是 Cassandra 可能是最成熟的一个。 一致性,leader 选举(leader election),以及分布式锁都是很难实现和测试的。不要自己去实现。用 Zookeeper 等,或者 raft 类库。 现在走向细节。数据库本身是一个抽象泄露(leaky abstraction)。他们一般在隐藏底层的复杂性上做了很好的工作,但是忽视其局限性最终会伤到你自己。以下是一些重要的东西:
  理解数据库的正确性保证。一个失败的操作是指什么[3]? 哪些操作是完全一致的,哪些又不是?
  理解数据是如何被存储和获取的。哪些操作是有效的,哪些又是无效的?有没有一个查询规划(query planner),或者每个操作的详细统计信息?
  分片和集群架构。理解数据是如何在集群中分布的。你的分片策略均衡地分布数据了吗?或者有没有热点存在?
  数据建模模式和反模式。
  业务挑战
  一旦你的软件栈中的一部分,数据库和你的基础架构保证24*7提供不中断服务 。它就引入了独特的业务挑战。 操作数据库就像在海洋中航行。无论何时你遇到了问题,你都要不让数据库下沉的同时解决它,即使是在风暴中心。因此,数据库需要有:
  内省(introspect)和监视系统的方法。
  维护和管理系统的把手。
  复制,备份和恢复。丢失数据是极其糟糕的。机器会在某个点奔溃。由于数据库是有状态的,你不能简单地只部署代码。
  在运行的同时具备以上所有。坦白说,所有数据库在这点上都有缺点。在完全运行的同时允许任何配置都可以修改,是一个艰难的挑战。许多操作需要一个数据库层面的互斥量,额外的系统资源,或者重新启动。例如包括:
  在 MySQL 5.6 以前,添加一列需要全表锁,而黑客们喜欢 pt-online-schema-change 的存在就是为了缓解这一问题。MySQL 现在支持在线模式迁移。
  Cassandra 允许你简单地添加、删除、和修复节点。然而,这些操作给系统添加了额外的负载,而且需要容量空间。
  此外,你不能简单的替换一个数据库。即使是在同一数据库内迁移数据的任务也不简单。迁移到另一个数据库中更是难上加难,如果不是不可行[4]。一个应用程序的代码是非常容易逐步铺开并恢复(如果需要的话)的。数据比代码存活地更久。数据模式和存储的数据通常会在多个应用程序之间共享。因此,初始的数据库系统和对应的数据模型的选择是非常重要的。 最后,数据库总会发生故障。不管你用的是什么平台/服务即架构,有些故障是避免不了的:
  应用程序代码弄脏或丢失数据的 Bug
  误解了数据库的安全性和一致性保证,丢失了写操作。
  数据模型和数据库不匹配 —— 例如,有一个数据集并不适合在一个独立 shard 上。期待原子切换(compare-and-swap)到最终一致性的数据库中工作。
  运行故障 —— 机器崩溃。硬盘损耗。操作系统升级。
  网络分区[6]。
  惊群效应 —— 单个系统故障,并级联到其他系统。
  而最糟糕的是 —— “突然慢下来了”。“随机尖峰延迟”。“每天一次偶发错误”。“这条记录应该存在但是没有了”。
  对于这些却没有单一的解决方法。操作数据库的艺术真的属于维护一个高 SLA 系统的艺术,但是如果我需要给出一些技巧:
  应用程序开发人员理解的限制和故障模式的行为。
  编写一个有弹性的应用程序。多数据中心部署。自动故障转移。
  拥有一支由熟悉业务并且了解不同故障场景和恢复方法的工程师(网站可靠性工程师,DBAs)所组成的队伍。
  PS:在 Square ,我们有一个超酷的在线数据存储(ODS)团队把这些问题从我们这里抽离出去。
  基础构建模块
  通过数据库提供的抽象真的很神奇。数据提取(ingestion),查询,复制,以及故障恢复都在同一个包里?但是当你习惯了它,你就开始认识到有一些基础的构建模块 —— 在所有数据库之间共享的通用模式和组件。 首先,数据检索降低了以下中的一个:
  键值查找(哈希表)
  范围查找(树和 LSM 树)
  文件偏移量查找(Kafka,HDFS)
  在本文的最后,我想说的是,电脑并不懂 SQL、索引、联接、或是其他的花哨的装饰。上层的操作需要被翻译成机器能执行的东西。 对于可持久化数据结构,B-trees, hash tables,和 LSM树 (log-structured-merge-trees)都是很普遍的选择。很可能你的数据存储在其中的一个,除非它需要一些特定的查找(例如:地理空间查询)。LSM 树是一种流行的现代的选择,在BigTable,HBase, Cassandra, LevelDB, 以及 RocksDB 中都有使用,因为其一流的写入性能和合理的读取性能。 最后,还有流行的模式和算法用于整个不同的系统: Paxos, Raft, 一致性哈希, Quorum 读/写, Merkel 树, 以及 Vector Clocks 都是一些基本的构建模块。
21/212>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号