`

转:MySQL 对 Uber 来说可能是最合适的,但不一定适合你

阅读更多

 

https://coyee.com/article/10766-mysql-might-be-right-for-uber-but-not-for-you?fromoschina

 

前几天 Uber 发布了一篇文章 "为什么 Uber 工程师将 Postgres 数据库换成 MySQL?" ,我没有马上阅读这篇文章,因为我的内心告诉我还不如回家干点别的事呢。但是我的邮箱被塞满了这样的问题 —— PostgreSQL 真的很糟糕吗?因为知道 PostgreSQL 没那么烂,所以这些邮件促使我想知道这篇文章到底在写什么鬼。本文主要解读 Uber 这篇文章中的问题。

在我看来,Uber 的文章基本上是在说他们发现 MySQL 比 PostgreSQL 更适合他们的环境。不过这篇文章传递消息的方式却非常的糟糕。他们应该写“PostgreSQL 在重度更新操作场景下的一些限制” 而不是“写架构的缺陷”。例如,如果你的使用场景并非重度写操作的,就无需担心 Uber 描述的这些问题。

 

0我的这篇文字中将解释说为什么我认为 Uber 的文章不能作为一般数据库选型的建议文章;为什么 MySQL 对 Uber 来说比较适合,以及为什么成功会导致更多的问题,而不只是数据存储的缩放问题。

UPDATE 操作

Uber 文章描述的第一个大问题,但仍没有提供完整细节的问题是 PostgreSQL 在更新某个表的记录时,总是需要更新一个表的所有索引。而 MySQL/InnoDB 只需要更新被修改的列的索引。PostgreSQL 采取的方式导致了更新操作时需要执行更多的磁盘 IO 操作来更新那些非索引列(文章里说的是“写应用”)。如果这对 Uber 来说是一个大问题,这些更新操作可能是其整个负载的一大组成部分。

 
0然后,Uber 文章中有一点炒作成分的原因是:该文章并未提及 PostgreSQL 的 Heap-Only-Tuples (HOT) 。根据 PostgreSQL 官方资料, HOT 非常适合这种特定场景 "元组数据重复更新,但不改变它的索引列" ,在这种场景下 PostgreSQL 可以在不改动索引的情况下更新数据,前提是新的行版本数据和前一个版本的数据存在于同一个页中。后者可以通过 fillfactor 设置项来进行调整。我猜测 Uber 工程师已经意识到 HOT 并非他们问题的解决方案,因为其高频更新操作至少影响一个索引列。
 

这个假设也得到了文章的支持,请看这句话:“如果我们的一个表定义了十几个索引,对单一索引字段的更新需要传播到所有十几个的索引,以便 ctid 可以指向新的数据行。”,非常明显的一句话 “只受单个索引影响” ,这是一个边界使用场景 —— 只有一个索引 —— 否则,PostgreSQL 的 HOT 技术就可以解决这个问题。

旁注:我真的很好奇,他们所拥有的索引数量是否会减少 —— 我建议 重新设计索引。这完全有可能的,这些索引很少使用,但用时却非常重要。

 

看起来 Uber 运行了很多更新操作,改变至少一个索引列。但是对一个拥有很多索引列的表来说,这个数量还是相对要少很多。如果这是占主导的使用场景,那么文章中使用 MySQL 替代 PostgreSQL 的论点是有道理的。

SELECT 查询

这是 Uber 文章中另外一个引起我注意的声明:文章解释说 MySQL/InnoDB 使用的是簇集索引,同时承认了“InnoDB 的这种设计方案相对于 PostgreSQL 有一些轻微的不足,主要体现在二级索引的查找上,因为 InnoDB 要做两次索引的查找,而 PostgreSQL 只需要做一次。我之前写过一篇文章来描述这个问题 ("the clustered index penalty") in context of SQL Server.

 
0引起我注意的是他们描述簇集索引有一些轻微的劣势,但我觉得,如果你运行使用二级索引来运行很多查询,那么这个劣势就非常明显。如果对他们来说只是很轻微的劣势,这表明这些索引应该不怎么使用。也就是说大多数都是基于主键索引进行搜索(那么就没必要用簇集索引)。注意我这里写的是“搜索” 而不是“查询”,原因是簇集索引的缺点影响到任何一个带 where 的语句,而不仅仅是 select 语句。这同时也意味着其高频的更新主要是基于主键进行查询的。
 

最后在查询上这篇文章也存在一些疏漏:他们并未提及 PostgreSQL 在做索引扫描(index-only scans)的限制,特别是对更新非常频繁的数据库来说。PostgreSQL 对 index-only 扫描的实现几乎是没什么用的。这个简单的问题影响了我的大多数客户。我曾经在 2011 年写了一篇博客来描述这个问题。在 2012 年 PostgreSQL 9.2 有限的支持 index-only 扫描(只对大多数静态数据有效)。在 2014 年我在 PgCon 上重新提及此问题。但是 Uber 并未抱怨该问题。Select 查询的速度并不是他们关心的问题,我猜他们是通过在复制节点上运行查询来解决这个查询速度的问题的。

 

到现在为止,根据 Uber 的使用场景,我觉得使用 Key/Value 存储更适合。你猜怎么着? InnoDB 本身就是一个非常可靠而且受欢迎的 Key/Value 存储。甚至还有很多 InnoDB 的封装包,并提供一些(非常有限)的 SQL 前端,其中 MySQL 和 MariaDB 就是使用非常普遍的产品。请原谅我的讽刺,但认真的说,如果你只是需要一个 Key/Value 存储并且偶尔需要运行简单的 SQL 查询,那么 MySQL 或者 MariaDB 是一个合理的选择。我想在提供有限的 SQL 查询支持的要求下,MySQL 是比其他任何随机的 NoSQL 存储系统更好的选择。而 Uber 从另外一个角度讲就是在构建他们自己的(Schemaless)基于 InnoDB 和 MySQL 的架构。

 

重做索引

文章索引描述的最后一个注意点:它使用了 rebalancing 这个词,用在 B 树索引上。同时这个词链接到了维基百科上的文章 "Rebalancing after deletion." 不行的是维基百科的文章不适用于数据库索引,因为维基百科的文章上描述的算法要求每个节点至少要有半满的要求。为了提升并发处理能力,PostgreSQL 使用 Lehman, Yao 的 B-trees 变种, 从而支持稀疏索引。作为一个侧面说明,PostgreSQL 仍从索引中移除空页(请看 slide 15 of "Indexing Internals"). 然后,这仅仅是一个侧面的问题。

 

真正让我担心的是这句话:“ 使用 B 树索引一个至关重要的地方就是,它们必须定期重做 (rebalancing)。。。” 这里我想澄清的是,这并不是每天都要执行的过程。索引的平衡是在每次索引变更的时候维护的(有更糟糕的吗?),但是文章继续写到 “ 索引重做的操作会改变树的结构,因为子树会被移动到磁盘上新的位置。” 如果你以为 rebalancing 操作会引发大量的数据迁移,那你就理解错了。

B 树最重要的操作是节点分割。节点分割发生在当一个节点无法挂接属于它的一个新节点时。节点分割一般大概发送在大约 100次插入操作之后。节点分割时会分配一个新的节点,移动当前节点大约一半的条目到新的节点上,然后将新的节点与上一个节点、下一个节点以及父一级节点关联。这就是 Lehman Yao 算法节省了大量的锁资源的处理方式。在某些情况下,新的节点无法直接添加到父节点,因为父节点没有足够的空间来存放新的子节点条目,那么父节点就会开始进行节点分割,并依此类推。

 

最坏的情况下,分割操作会直接上升到根节点,那么根节点也会被分割,并在其上产生一个新的根节点。只有在这种情况下 B 树不会变得更深。注意,一个根节点的分割高效的将整棵树下移,因此保持了树的平衡。不管怎样,这并不会导致大量的数据移动。最坏的情况它可能会触及每个级别上的三个节点以及新的根节点。不过很明确的一点是:真实环境下的索引一般不会超过 5 级。更明确的是:最坏的情况下,也就是根节点分割只会在每10亿次插入才会发生一次。其他的情况无需遍历整棵树。毕竟,索引的维护不是周期性的,甚至不是很频繁的,它也不会完全的改变树的结构,至少不会发生磁盘上的物理操作。

 

物理复制

这是我对那篇文章关于 PostgreSQL 部分的另外一个关注点 —— 物理复制。文章中谈及索引重做是因为 Uber 碰到了 PostgreSQL 一个复制方面的 Bug 导致下游服务器的数据损坏(此 Bug 只影响特定版本的 PostgreSQL 9.2 ,而且很久以前就已经修复)。

因为 PostgreSQL 9.2 是核心中提供物理的复制功能,一个复制的 Bug “会导致树的大部分大部分不可用”。详细点说就是:如果一个节点分割没有正确的被复制,它就无法指向正确的子节点,那么子树就不可用。这完全正确,就像这么一句话:“如果存在一个 Bug,就大事不好了”。你无需修改大量的数据来破坏整个树结构,一个简单的坏指针足以。

 

Uber 文章中提及另外一些物理复制的问题是:1. 超大的复制流量;2. 因为更新操作导致的写操作放大;3. 更新 PostgreSQL 版本导致的停机时间太长。第一个问题我比较敏感,第二个问题我不能评论什么(但是可以通过 PostgreSQL-hackers 邮件列表中的声明 了解更多细节)。

最后,文章还声称 “Postgres 没有真正的复制 MVCC 支持” 。所幸的是文章链接到的 PostgreSQL 文档对这个问题有进行说明。问题的根本是复制的主节点不知道从节点在做什么,因此可能会删除一些仍然在副本上完成查询所需的数据。

 

根据 PostgreSQL 文档 的介绍,有两种方法来处理这些问题:

  1. 通过一个可配置的超时时间来延迟应用的复制流,因此读事务就有充足时间来完成查询。如果一个查询无法在要求的时间内完成,停止这个查询并继续进行复制。

  2. 配置复制节点给主节点发送关于查询执行的反馈,这样主节点就不会清空从节点所需的行版本数据。Uber 的文章使用了第一种方法,但是压根没提及第二种方法。这对 Uber 开发人员来说是打脸了。

 

关于开发者

引述一些常见的情况:"假设,开发者需要通过电子邮件发送收据给用户。这取决于开发者怎么写这个代码,该代码会打开一个数据库事务并在邮件发送完成后提交事务。让你的代码长时间打开一个数据库事务去等待一些毫无关联的堵塞操作是一种不好的做法,而现实中绝大多数开发者并非数据库专家,无法理解这个问题,特别是使用一些 ORM 框架屏蔽了底层操作时尤为如此。”

 

很不幸,我理解甚至是认同这种观点。与其说“绝大多数开发者不是数据库专家” 我想说的是绝大多数开发者对数据库的理解非常浅薄,每个接触到 SQL 的开发者都应该了解数据库事务 —— 不只是数据库专家。

我目前主要的工作是给开发者做一些 SQL 培训,我给各种规模的公司做过这样的培训。如果说有一件事我可以肯定的话,那就是多数人对 SQL 的知识是非常不足的。在“打开事务”的问题上,我可以确认几乎没有一个开发者知道只读事务的存在。大多数开发者知道事务可以在操作失败时候回滚写操作。我经常遇到有这样误解的开发者,所以我准备了一些幻灯片来解释这个问题。

 

关于成功

这是我最后想说的一个问题:一个公司雇佣越多的开发者,那么这个公司越趋近于其平均水平。夸张的说,如果你雇佣了整个星球的人,水平就是所有人的平均水平。招聘更多的人只是增加了这个取样的样本量。

打破这个规律的两种方法是:

  1. 只招聘最好的开发者,这种方法非常困难,因为你可能需要等很久才能找到很好的人。

  2. 招聘中等水平的人并提供培训。这就需要一个新员工入职后有比较长的上手时间,也可能会需要现有员工花时间去对这些新来的进行培训。两种方法共同的问题就是时间。因为你的业务快速发展,你可能没多少时间去等,所以你只能招聘那些对数据库了解并不多的开发者(2014年的经验数据)。换句话说,对一个快速增长的公司而言,变更技术比变更人员要容易得多。

 

随着时间的推移,成功的因素还会影响对技术栈要求的变化。在早期阶段,初创企业需要的是立即可用以及灵活的技术,这足以满足业务的需求。SQL 是一个很好的选择,因为它的确很灵活(你可以有很多种方式来做数据查询),而且很容易招聘到懂一点 SQL 知识的开发者。很好,那我们就开始吧。对很多公司,或者说是绝大多数的公司来说,这个故事就结束了。即使是这些公司比较成功,业务也进一步增长,它们仍将永远停留在 SQL 数据库的限制中。不过 Uber 不是如此。

 

一些幸运的初创企业最终会摆脱 SQL。如果这个事情发生了,就有更多的资源(或者几乎是无限的)然后奇妙的事情就发生了:他们意识到他们自己可以解决很多问题,例如替换一个通用的数据库系统,然后自己开发一个来代替它。这也是 NoSQL 数据库诞生的原因,在 Uber ,他们称之为 Schemaless.

Uber 的数据库选择

到现在为止,我相信 Uber 不需要像他们文章写的那样把 PostgreSQL 数据库替换成 MySQL。看起来他们只需要使用自己开发的解决方案去替换 PostgreSQL 就足够了,Uber 的这个解决方案恰好是 MySQL/InnoDB 目前所能提供的。

 

那篇文章只是解释了为什么作为 Schemaless 无模式的数据后端来说 MySQL/InnoDB 比 PostgreSQL 更加适合。如果你也在使用 Schemaless 的话,那可以听从 Uber 的建议。不过该文章并未清楚的说明其需求变更的情况,因为在 2013 年他们将数据库从 MySQL 移植到 PostgreSQL

可悲的是,这样的文章只留给读者一个糟糕的印象 —— PostgreSQL 很糟糕。

分享到:
评论

相关推荐

    也许MySQL适合Uber,但它不一定适合你

     你可能听说了 Uber 从 PostgreSQL 迁移到 MySQL 。这对于他们来说,或许是一个好的迁移,但是这未必适合你。且听我说来。  2016 年 8 月,Uber 发表了一篇名为《为什么 Uber 工程从 PostgreSQL 迁移到了 MySQ

    Uber H3 封装

    总的来说,Uber H3的OC封装为开发者提供了一个强大而灵活的工具,能够帮助他们在iOS和macOS应用中处理和展示地理数据,提高地图应用的功能和用户体验。通过深入学习和实践,开发者可以解锁更多地理空间分析的可能性...

    ios-仿Uber首页(低仿).zip

    6. **数据管理**:虽然这是一个低仿项目,但可能仍涉及一定的数据处理,比如保存用户的常用地点、历史行程等。开发者可能使用UserDefaults或CoreData进行轻量级的数据存储。 7. **网络请求**:如果项目中包含了模拟...

    人工智能在Uber的外卖服务_Uber_Eats_中的应用.pdf

    总的来说,Uber Eats借助人工智能技术,提升了外卖服务的效率、准确性和用户满意度,同时也为自身带来了显著的商业价值。随着AI技术的不断发展,我们可以期待Uber Eats在未来会有更多的创新和优化,进一步改善全球...

    Uber-ATG-Self-Driving-Safety-Report2018.pdf

    总体来说,《Uber自动驾驶安全报告》是Uber对其在自动驾驶领域安全实践的全面概述,反映了公司在技术创新的同时,对安全问题的深思熟虑和负责任的态度。这份报告对于理解Uber在自动驾驶领域的策略和挑战,以及整个...

    Uber地图样式:这是Uber地图样式

    在IT行业中,地图应用是不可或缺的一部分,而Uber作为全球知名的打车应用,其地图样式设计对用户体验至关重要。本文将深入探讨“Uber地图样式”的特点、实现原理以及与Android开发相关的技术点。 首先,Uber地图...

    uber-cli::automobile:Uber,触手可及

    就是说,作为一个懒惰的人,每次打开我的手机,打开Uber应用程序,键入我的目的地并查看估计的价格时,我都会感到痛苦,仅是因为我内心,负责,削减成本,受信的自我最终承担了巴士一路回家。 我认为我们都可以同意...

    DBeaver链接hive驱动包下载: hive-jdbc-uber-2.6.5.0-292.jar

    《DBeaver与Hive连接:hive-jdbc-uber-2.6.5.0-292.jar驱动详解》 在大数据处理领域,Hive作为一个基于Hadoop的数据仓库工具,广泛用于数据查询和分析。而DBeaver,作为一款跨平台的数据库管理工具,以其用户友好的...

    Android 仿Uber引导页

    在Android开发中,Uber应用程序的引导页是一种常见的用户体验设计,用于在用户首次打开应用时提供一个引人入胜的互动...对于Android开发者来说,掌握这些技术不仅能够提高应用的用户体验,还能在项目中展现出专业性。

    Uber的欢迎界面

    Uber的欢迎界面也不例外,它不仅展示了Uber的品牌形象,还可能在后台进行一些必要的初始化操作。 UberSplash项目是由开发者KobeGong在GitHub上开源的,它模仿了Uber原生应用的欢迎界面设计,为开发者提供了一个参考...

    Presto在Uber的使用

    具体来说,当客户端向协调器发送一个查询请求时,协调器会首先解析这个SQL语句,并对其进行优化,然后将整个查询拆分成多个子任务,再将这些子任务分发给不同的工作节点去执行。工作节点执行完各自的子任务后,将...

    hive-jdbc-uber-2.6.5.0-292.zip

    总结来说,这个压缩包提供的Hive JDBC Uber JAR是解决DataGrip连接Hive问题的关键,它确保了IDE与特定版本Hive的兼容性,并且简化了驱动管理,让用户无需关心具体依赖。在日常开发和数据分析工作中,正确配置和使用...

    Uber-UBER2020年投资关系PPT:公司即将迎来盈利(英文)-2020.2.6-39页.rar

    这份名为"Uber-UBER2020年投资关系PPT:公司即将迎来盈利(英文)-2020.2.6-39页.rar"的压缩文件包含了一个重要的行业报告,它揭示了2020年时全球知名出行服务提供商Uber(UBER)的财务状况和未来盈利前景。...

    hive-jdbc-uber-2.6.5.0-292.jar

    总的来说,Hive JDBC Uber Driver 2.6.5.0-292.jar是Hive与Java应用之间的重要接口,它的存在使得数据处理和分析工作变得更加便捷和高效。无论是在大数据处理、ETL流程还是数据分析项目中,这个驱动都是连接Hive与...

    hive驱动包hive-jdbc-uber-2.6.5.0-292.jar(用户客户端连接使用)

    Hive是Apache Hadoop生态系统中的一个数据仓库工具,...了解并正确使用Hive JDBC驱动,是进行Hive数据操作的重要一环,尤其对于那些希望通过编程方式或数据库管理工具访问Hive数据的开发人员来说,它是必不可少的工具。

    20210511-中金公司-优步-UBER.US-Uber:提供“移动”价值的全球共享经济巨头.pdf

    20210511-中金公司-优步-UBER.US-Uber:提供“移动”价值的全球共享经济巨头.pdf

    flink-shaded-hadoop-2-uber-2.7.5-10.0.jar.zip

    总的来说,`flink-shaded-hadoop-2-uber-2.7.5-10.0.jar.zip` 提供了一个方便、兼容且冲突解决的 Flink-Hadoop 集成方案,是大数据开发者在处理涉及 Flink 和 Hadoop 的复杂场景时的重要工具。理解并熟练使用这种 ...

    Uber提出有创造力的POET:自行开发更困难环境和解决方案.docx

    总结来说,POET是一种创新的机器学习技术,它促进了环境与智能体之间的交互,推动智能体在自我生成的挑战中持续学习和进化。这种技术有望在解决复杂问题、应对未知挑战以及提高人工智能的泛化能力方面发挥重要作用,...

    Uber的流处理系统及实践

    具体来说,优步流处理系统的实践包括但不限于以下几个方面: 1. 市场动态分析:优步通过分析实时数据流来追踪市场动态,这包括全球各地可用的车辆数量。了解各地区的车辆供需关系,可以帮助公司预测需求高峰期,并...

Global site tag (gtag.js) - Google Analytics