本文翻自于 Apache ShardingSphere PMC 潘娟发表于 Stack Overflow 上技术文章 “How sharding a database can make it faster”。
原文链接:https://stackoverflow.blog/2022/03/14/how-sharding-a-database-can-make-it-faster/
数据分片往往是分布式数据库用以提高性能的众多首选方法之一,而近些年来的技术创新使其逐渐成为最佳选择。
如今,数据库受到广泛关注,这是因为其管理着公司最重要的数据资产。30 年前,数据大多存储在纸、磁带或某种类型的磁盘上。当时人均生产和消费的数据量较少,所以这些方式可以有效支持存储、管理和访问数据。
然而,“数据为王”的时代,情况早已截然不同。随着智能手机的出现与普及,日常生活越来越离不开手机,手机应用程序所消耗和产生的数据量之大,是十五年前人们无法想象的。海量数据意味着数据库集群需要处理庞大的流量,一些头部网站和服务器每周接收访问甚至达到数十亿次级,数据库集群承受着巨大压力。
我们又该如何处理这些到达数据库集群的庞大数据流量呢?
答案可能是采用数据分片。也许,你从未听说过数据分片,或者你早已将其视为无法应对当代挑战的过时解决方案,而选择忽略它。数据库分片架构听起来可能不像其他解决方案一样抢眼或是花里胡哨,但它绝对兼具有效性和实用性。
近期,数据分片方式实现了重大技术创新,而不久前人们还是无法想象的(例如能够使分片更易于实现和管理的分布式 SQL/DistSQL)。也许这就是在一些寻求实现数据可扩展性的区块链公司中越来越受欢迎的原因。
数据库碎片化
数据库诞生距今已有 50 多年,在此期间你可能认为数据库已无创新的空间。但事实上,数据库碎片化已成为科技行业发展最快的垂直领域之一,因为现有数据基础设施的复杂性似乎只会恶化。
许多现代应用程序都建立在多个并且通常是特定用途的数据库之上。单个应用程序可能包括用于存储和访问内容的关系数据库(例如 PostgreSQL)、用于内容缓存的内存数据库(例如 Redis)、自定义数据库(例如时间序列数据库)和用于分析的数据仓库。现在,试想一下,一家企业拥有多种应用程序,或者不同部门各自采用不同的应用,又或者更糟糕的,不同供应商同时应用不同数据库时会发生什么?
如上所述,数据已成为所有企业最重要的资产之一,而数据库技术近期迅猛发展,离不开高速发展的人工智能、机器学习、区块链和云技术。
据 DB-Engines 统计,目前已有超过 350 个数据库管理系统,当然还有更多数据库系统甚至没有被其列入名单。
根据卡内基梅隆大学设计并维护的 “Database of Databases” 网站统计,目前已有 792 个值得关注且具有差异的数据库管理系统。
行业中存在这么多不同的数据库管理系统(Database Management Systems,简称 DBMS)也反映出企业在数据库管理系统选型时有着多种潜在需求。
例如,银行或其他金融机构可能会选择 SQL Server 或 PostgreSQL 等关系 DBMS 以确保其结构化数据具有ACID(Atomicity, Consistency, Isolation & durability)事务特性。对于有着大型在线多人游戏或在线会话的 Web 应用程序业务的企业而言,通常偏好 Redis 等键值 NoSQL 数据库。从事社交媒体分析的企业通常会选择图形数据库,而物联网(IoT)企业会选择时间序列数据库来支持其传感器或网络数据。
如果你认为这些选择不错,随着未来几年更多的解决方案进入市场,你也会喜欢这种多样化的选择。这些解决方案可能是由具有创新性的初创公司实现,而更成熟的数据库供应商也将继续发布新产品或增强已有解决方案。
不久后,数据库市场碎片化趋势会更加明显,而碎片化也带来了重大挑战,例如需考虑供应商技术是否具有兼容性、旧系统是否具有适应性和高昂的更换成本等。
为什么需要数据分片?
传统数据库难以处理海量数据和查询流量,因此 NoSQL 和 NewSQL 概念日益流行,受此启发,越来越多新型数据库产品正在投放市场,但是,仅凭这些概念并不能实际解决数据增长问题。
分片技术可将数据拆分为单独的行和列,并将其保存在单独的数据库服务器实例上,以分散流量负载。每个小表称为一个分片。Apache HBase 或 MongoDB 等 NoSQL 产品具有分片功能,此外分片架构也内置于一些 NewSQL 系统中。
让我们看一下一种特定类型的 NewSQL 架构,即与当今的 OLTP 问题息息相关的分片架构。
虽然许多解决方案都可以最大限度地减少数据库负载,但数据分片解决方案具有以下优点:
• 在多台机器上实现分布式数据存储
• 轻松平衡不同分片上的流量负载
• 显著提高查询性能
• 无需额外操作即可扩展数据库
• 高效实现重用和升级传统 DBMS
• 使用代理支持多租户,实现多个数据库跨用户使用单个服务器或云计算资源。
如何进行数据库分片
接下来,我们将通过介绍数据分片的基本流程,展示如何为 DBMS 实现分片。此外,在介绍完配置和基本概念之后,也会更深入地解释一些重点内容。
创建分片的最佳技术之一是将数据拆分为多个小表,也称为分区。
原表可以分为垂直分片或水平分片,即将一列或多列存储在单独的表中,或者将一行或多行存储在单独的表中。这些表可以标记为垂直分片的 “VS1” 和水平分片的 “HS1”,其中数字代表第一个表或第一个 schema,以此类推。这些数据子集综合构成了该表的原始 schema。
以下是分片相关的两个关键概念:
• 分片键:特定的列值,用于表示该行存储在哪个分片中。
• 分片算法:一种将数据分布到一个或多个分片的算法。
步骤 1:分析场景查询和数据分布情况以确定适合的分片键和分片算法
确定存储指定行的分片,需将分片算法应用于分片键。不同的分片策略适合不同的场景,常见分片策略包括:
MOD:Modulo 的缩写,取模分片算法,用于将每第 n 行或每列发送到特定的分片。例如,MOD 3 算法会将第一、四、七行发送到第一个分片,将第二、五、八行发送到第二个分片,将第三、六、九行发送到第三个分片,以此类推。
HASH:哈希取模分片算法,将 Hash 分片在分片之间均匀随机分布数据。根据对该行的分片列值计算的一致 Hash 将每行放置在一个分片中。
RANGE:基于分片边界的范围分片算法,将特定范围的行或列发送到各个分片。
TAG:发送与特定值匹配的所有行或列。
例如,如果分片键是“ID”且分片算法是“ID modulo 2”(即拆分偶数行和奇数行),则行将按如下方式进行分配:
因此,要做的就是设计一个使用该分片键的合适算法,而分片策略将会极大程度影响查询效率和未来的水平扩展。不正确或较差的分片算法总是会在不同的分片之间创建冗余数据进行计算,最终导致整体计算性能不佳。
在决定如何进行数据库分片时,需考虑的关键点在于明确业务查询和数据分布的特征。虽然每个数据库都会有影响数据分片策略选择的个性因素,但我们可以通过提供场景案例来解释好的分片算法如何可以有效地实现数据分布。
RANGE
例如,当对包括时间戳日志详细信息的表进行分片时,建议使用以创建日期作为分片键的 RANGE 分片算法,因为人们传统上倾向于只在特定的时间范围内查询这些详细记录。
然而,使用日期-时间时,RANGE 算法可能会导致另外一个问题:由于历史记录的更新频率通常较低,而最近的记录更新和查询频繁,大多数查询会针对有最新存储记录的分片,导致大多数查询相互进行竞争以获得更新该数据的专属权利。
MOD
MOD 分片算法则可以有效避免上述的激烈竞争。它通过 shardingKey MOD shards number
分割行,最新的行将被拆分为不同的分片,以便将最新的查询发送到不同的分片,避免最新的行之间的竞争。分片键是字符串值(并且可能对泄露敏感)时,可以使用 HASH 算法创建一个值,MOD 算法可以使用该值将数据分布到分表上。
TAG
需按单元格的值对数据进行分片时,建议使用 TAG 分片算法。假设,为遵守通用数据保护条例(GDPR),需将所有欧盟相关数据存储在位于欧盟的服务器上,为此,我们该如何操作一个基于分片的分布式数据库系统呢?假设,该数据库管理员(Databse Administrator, DBA) 使用 TAG 分片算法,则可以将带有标记国家/地区的数据的行发送到位于特定国家/地区的指定分片,如果想了解有多少记录受到影响,该分片数据库系统只需从与欧盟相关分片返回 COUNT(*) 即可回答此查询 SELECT COUNT(*) FROM registrant_table WHERE region = "EU"
, 由此,必须从整个分布式系统计算最终结果的分布式查询被简化成来自一个分片的单个查询。
没有哪种方法是放之四海而皆准的。要获得最佳数据库性能,就得多花些时间对特定业务场景进行深入分析。当然,为实现用户快速入门,基于分片的分布式数据库系统开发者通常会选择满足大多数用户案例的通用策略。
步骤 2: 迁移现有数据
如果决定执行分片,不需要将所有原始数据迁移至某一分片集群中。但这样做是有风险的,你将面临以下问题:
如何在分片的同时保证业务不间断运转
如何在新的分片集群中重放增量数据
如何比较原数据库和新分片集群的数据
如何找到将流量切换到新分片集群的最佳时机
但是,如果你决定将历史数据迁移至分片,传统方法如下:
1. 首先,通过分片算法将历史数据划分至新的数据库分片集群。建议使用一个自动数据迁移程序,它将运行所需的所有 SQL 查询。
2. 其次,运行平台或程序来拉取和解析数据库日志,以了解数据划分过程中发生了哪些变更,并将这些变更应用至新的分片集群(增量数据分片)。
3. 第三,选择一种数据检查策略来比较原始数据库和新分片集群中的数据。这些数据检查策略很灵活,既可以选择高精度检查也可以选择快速检查,亦或选择折中的检查策略。是比较每个单元格,还是只检查总额,都由你来决定。为达到最精确的数据检查策略,可以采取逐行比较,这也是最为费力的一种检查方式。而只比较原始数据库和新集群的行值是最为快速的检查方式,但准确性也就大打折扣。其他策略,如 CRC32,正在实现准确性和速度之间的平衡。
步骤 3:将流量转移至新集群
假设上述步骤已顺利完成,那么下一步就是将在线流量切换到新的分片集群。该操作适用于无法写入数据库集群的时间段,以便两个数据集保持一致并保留可选查询——非高峰时间此步骤为常见选择。
应禁止所有更新请求以保持分布式数据一致性。但可以允许查询,因为查询不会引起分布式系统中的任何变动。
这个过程很简单,但每个部分处理起来可能会有难度。自动执行将最大限度缩短停机时间,但由于处理的数据十分重要,建议应保持谨慎。
不过,好消息是,你并非第一个面临这些挑战的人。开源项目帮助我们站在了巨人的肩膀上。
整个分片过程是 Apache ShardingSphere(我也是该项目的 Contributor 之一) 的主要功能之一。它提供各种分片策略、数据迁移、重新分片和管理现有分片等功能。
它还提供了许多更为高级的功能来帮助解决我们在下一节中将提到的问题。此外,有一个额外的好处是,ApacheShardingSphere 拥有一个活跃的社区,这意味着你的大多数问题已经得到了解决。
怎样才是好的分片?
现在我们已经了解了分片工作流程以及在数据库上执行分片的必要步骤。但是好的分片应该是什么样的呢?
无须对边缘理论或背景和场景特定要求进行太多扩展,好的分片通常具有六个特点。
如果负责运行操作的 DBA(数据库管理员)发生了变动,分片应易于设置和理解。分片具有高可用性、弹性伸缩能力、高分布式系统性能、可观察性和低迁移成本。
这六个要素代表了理想的分片,但它还取决于你所选择的分片客户端。
使用分片和副本
由于数据库场景多种多样,而需求会随着应用程序的扩展而变化,因此除了上述核心流程之外,你还需要了解以下内容。
提升数据库性能和扩展性的另一种方法是通过副本。副本会创建独立的重复数据库节点,写入某一节点的数据将被复制到另一个重复节点上。
一般来说,无论是专业人士还是致力于 passion projects 的开发人员都在努力最大限度地利用数据库,以实现高可用和高性能。然而,分片和副本的体系架构可能会让数据库管理和路由策略复杂化。
设想每个分片都有副本节点,结果将如下图所示。如果一个主节点有多个副本,则访问它们的应用程序情况会更为复杂。
那么,分片和副本有何不同呢?如上所述,分片意味着将一个大表拆分为几个小表以创建许多分片;另一方面,副本将对原始表创建多个副本。每个副本将包含原始节点(主节点)的全部数据。
分片可以帮助用户对存在于多个服务器上的数据进行负载均衡,以实现可扩展性;而副本将创建主库备份,以提高系统可用性。这两种不同的体系架构给分布式系统带来了不同的优势。基于这一论证,一些用户希望同时拥有这两种功能,因此同时利用分片和副本的体系架构并不少见。
如下图所示,用户可能会希望跨不同的服务器(如P1、P2、P3)对一个包含大量数据的数据库进行分片。每次查询也会被分成不同的分片,以提高该分布式数据库系统的 TPS 或 QPS。但是,一旦其中一个分片崩溃下线,可用性将降至 2/3。不仅如此,重新调用离线副本非常耗时,这会造成严重损失。为提高该分片系统的可用性,对每个分片进行复制,即对前面提到的主节点 P1、P2、P3 进行复制,将是一种高效的方法。
R1、R2、R3 的存在能很好的解释我上面说到的解决方案。当 P1 不可用时,其副本 R1 将成为主节点以服务于业务。这是一个安全的选择,其理念是停机率越小,业务和服务的损失就越小。
这个想法乍听上去不错,但是这一分布式分片数据库系统的拓扑结构让应用程序访问变得十分复杂。假设每个主节点有两个副本,那么由 P1、P2、P3 和它们的六个副本组成的网络就会让开发人员感到困惑和负担。他们可能会提出这样的问题:哪个主节点适合该查询?如何访问他们的副本?如何在不同副本之间进行负载均衡?一旦主节点无法正常工作,谁来负责重新路由此查询?
在这一假设场景中,开发人员负责代码编写以保证业务效率。这种架构的确有其优势,但其复杂性也意味着难以对其加以利用和维护。
如何对应用程序隐藏这种复杂性?
一般来说,有两种客户端或访问模式可供用户选择,外加一种新的“额外”类型客户端。用户可以通过专门的数据库连接驱动程序或通过将应用程序连接到路由数据的代理应用程序来启动分片。
Sidecar 是可用分片模式中相对较新的概念,它起源于 service meshes。简单地说,它是一个与某种服务一起部署的代理实例,用于处理不同服务之间的通信、监控等。这种模式的操作方式类似于摩托车的跨斗。就是说跨斗被附加到母应用程序上,同时为该应用程序提供支持性功能。
如果我们使用专门的驱动程序或代理而非使用 Sidecar,它将仅仅是单个数据库服务器,能够帮助用户管理数据库集群。如此一来,应用程序就不会受到这些复杂的访问拓扑的影响,也不必进行重构代码以适应新的框架。
总结与趋势
分片是解决网络应用迅速发展所带来新挑战的方法之一。其他解决方案包括 DBaaS(或云上数据库)、新的数据库体系架构,或者仅仅是增加用于存储的数据库数量这一老办法。
兜了一大圈,希望这篇文章至少能帮助你了解分片架构。如果你早已听说过分片架构,但认为它已经过时了,那么希望这篇文章能改变你的想法。
实际上,我不喜欢时尚这个词,因为它给我的感觉是转瞬即逝的,因为今天的时尚明天就有可能过时。虽然生活中有许多事物的确如此,尤其是技术,但我更愿意以技术在特定场景下的实用性、效率和成本优势来评判一项技术。
所有这些都表明,对新趋势持开放态度固然很好,但也不要忘记,有时现有和成熟的技术可能才是最佳解决方案。
*参考文档
[1] https://db.cs.cmu.edu/papers/2016/pavlo-newsql-sigmodrec2016.pdf
[2] https://github.com/apache/shardingsphere
[3] https://opensource.com/article/21/9/distsql