数据库Databases
数据库是一个有组织的数据集合,可以轻松管理和访问。数据库的创建旨在使存储、检索、修改和删除数据与不同的数据处理程序相关联变得更加容易。
有两种基本类型的数据库:
- SQL(关系数据库)
- NoSQL(非关系数据库)
关系数据库具有明确定义的结构,例如属性(表的列)。诸如文档数据库之类的 NoSQL 数据库通常具有应用程序定义的数据结构。
关系数据库,就像记录联系电话号码和地址的电话簿一样,是有组织的并且具有预先确定的模式。非关系数据库,就像存储从个人常用信息到购物偏好等各种信息的文件目录一样,是无结构的、分散的,并具有动态模式。
优势
- 管理大数据:大量数据可以通过数据库轻松处理,这是使用其他工具无法实现的。
- 检索准确数据(数据一致性):由于数据库中的不同约束,我们可以随时检索准确的数据。
- 简单更新:使用数据操作语言(DML)更新数据库中的数据非常容易。
- 安全性:数据库确保数据的安全。数据库只允许授权用户访问数据。
- 数据完整性:数据库通过使用不同的数据约束来确保数据完整性。
- 可用性:数据库可以在不同的服务器上复制(使用数据复制),这些服务器可以同时更新。这些副本确保可用性。
- 可扩展性:数据库被分割(使用数据分区)以管理单个节点上的负载。这增加了可伸缩性。
关系数据库
关系数据库在存储数据之前遵循特定的模式。关系数据库中存储的数据具有先前的结构。大多数情况下,这种模型将数据组织成一个或多个关系(也称为表),每个元组(实例)都有一个唯一的键。数据的每个实体由实例和属性组成,其中实例存储在行中,每个实例的属性存储在列中。由于每个元组都有一个唯一的键,一个表中的元组可以通过在其他表中存储主键来与其他表中的元组关联,通常称为外键。
结构化查询语言(SQL)用于操作数据库。这包括数据的插入、删除和检索。
关系数据库流行和主导的原因有很多,包括简单性、稳健性、灵活性、性能、可扩展性以及在管理通用数据方面的兼容性。
关系数据库提供了原子性、一致性、隔离性和持久性(ACID)属性,以维护数据库的完整性。ACID 是一个强大的抽象,简化了与数据的复杂交互,并将许多异常(如脏读、脏写、读偏差、丢失更新、写偏差和幻读)隐藏在简单的事务中止之后。
但是 ACID 的设计就像一个大锤子,因此它足够通用以解决所有问题。如果某些特定应用程序只需要处理一些异常情况,那么就有机会利用定制解决方案来获得更高的性能,尽管会增加复杂性。
让我们详细讨论 ACID:
- 原子性Atomicty:事务被视为一个原子单位。因此,事务中的所有语句要么全部成功执行,要么全部不执行。如果事务中的语句失败,应该中止并回滚。
- 一致性Consistency:在任何给定时间,数据库应该处于一致状态,并且每次事务后它应该保持在一致状态。例如,如果多个用户想要查看数据库中的记录,它应该每次返回类似的结果。
- 隔离Isolation:在多个事务同时运行的情况下,它们不应相互影响。数据库的最终状态应与事务按顺序执行时相同。
- 持久性Durability:系统应保证完成的交易即使在系统故障事件中也能永久存储在数据库中。
各种数据库管理系统(DBMS)用于定义关系数据库模式以及其他操作,例如存储、检索和运行数据上的 SQL 查询。一些流行的 DBMS 如下:
- MySQL
- Oracle Database
- Microsoft SQL Server
- IBM DB2
- Postgres
- SQLite
优点
关系数据库是软件专业人员对结构化数据存储的默认选择。这些数据库有许多优点。关系数据库最大的优势之一是其对 ACID 事务和相关编程语义的抽象。这使得终端程序员非常方便地使用关系数据库。让我们重新审视一些关系数据库的重要特性:
- Flexibility:在 SQL 的背景下,数据定义语言(DDL)为我们提供了灵活性,可以修改数据库,包括表、列、重命名表以及其他更改。DDL 甚至允许我们在其他查询正在进行和数据库服务器正在运行时修改模式。
- Reduced Redundancy:关系数据库的最大优势之一是消除数据冗余。与特定实体相关的信息出现在一张表中,而与该特定实体相关的数据出现在通过外键链接的其他表中。这个过程称为规范化,还有一个额外的好处是消除不一致的依赖性。
- Concurrency: 并发性是设计企业数据库时的一个重要因素。在这种情况下,数据同时被许多用户读取和写入。我们需要协调这些交互以避免数据不一致,例如酒店客房的重复预订。关系数据库中的并发性是通过对数据的事务访问来处理的。正如前面所解释的,事务被视为原子操作,因此在错误处理中也可以回滚或提交事务以确保成功执行。
- Integration:从多个来源聚合数据的过程是企业应用程序中的常见做法。执行此聚合的常见方法是集成一个共享数据库,多个应用程序在其中存储其数据。通过这种方式,所有应用程序可以轻松访问彼此的数据,同时并发控制措施处理多个应用程序的访问。
- Backup and disaster recovery:关系数据库保证数据的状态在任何时候都是一致的。导出和导入操作使备份和恢复更加容易。大多数基于云的关系数据库执行持续镜像,以避免数据丢失,并使恢复过程更加简单和快速。
缺点
阻抗不匹配是关系模型和内存数据结构之间的差异。模型将数据组织成具有关系和元组的表格结构。对这种结构化数据的 SQL 操作产生与关系代数对齐的关系。然而,它也有一些限制。特别是,表中的值采用无法是结构或列表的简单值。而内存中的情况则不同,复杂的数据结构可以被存储。为了使复杂结构与关系兼容,我们需要根据关系代数对数据进行翻译。
非关系数据库
NoSQL 数据库旨在为各种数据模型提供访问和管理数据。
优点
这些数据库用于需要大量半结构化和非结构化数据、低延迟和灵活数据模型的应用程序。这可以通过放宽其他数据库的一些数据一致性限制来实现。以下是 NoSQL 数据库的一些特点:
- 简单设计:与关系数据库不同,NoSQL 不需要处理阻抗不匹配,例如,将所有员工数据存储在一个文档中,而不是需要联接操作的多个表中。这种策略使得编写更少的代码、调试和维护变得简单而容易。
- 水平扩展:主要是由于 NoSQL 能够在大型集群上运行数据库,所以它更受青睐。这解决了当并发用户数量增加时的问题。NoSQL 使得扩展变得更容易,因为与特定员工相关的数据存储在一个文档中,而不是分布在多个节点上的多个表中。NoSQL 数据库通常会将数据分布在多个节点上,并自动平衡数据和查询。在节点故障的情况下,可以在不影响应用程序的情况下透明地替换节点。
- 可用性:为了增强数据的可用性,可以在不影响应用程序运行的情况下进行节点替换。大多数非关系型数据库的变体支持数据复制,以确保高可用性和灾难恢复。
- 支持非结构化和半结构化数据:许多 NoSQL 数据库处理在数据库配置或数据写入时没有模式的数据。例如,文档数据库是无结构的;它们允许文档(JSON、XML、BSON 等)具有不同的字段。例如,一个 JSON 文档可能比另一个文档少一些字段。
- 成本:许多关系型数据库的许可证价格相当昂贵,而许多 NoSQL 数据库是开源的并且免费提供。同样,一些关系型数据库依赖昂贵的专有硬件和存储系统,而 NoSQL 数据库通常使用廉价商品服务器的集群。
NoSQL 数据库根据操作和特性的性质分为各种类别,包括文档存储、列数据库、键值存储和图数据库。
NoSQL数据库的类型
以下描述了各种类型的 NoSQL 数据库:
Key-Value Database
键值数据库使用类似哈希表的键值方法来存储数据键值对。
在这里,键作为唯一或主键,值可以是从简单标量值到复杂对象的任何内容。这些数据库允许数据的轻松分区和水平扩展。一些流行的键值数据库包括 Amazon DynamoDB、Redis 和 Memcached DB。
用例:键值数据库对于面向会话的应用程序非常高效。面向会话的应用程序,如 Web 应用程序,在会话期间将用户数据存储在主内存或数据库中。这些数据可能包括用户个人资料信息、推荐、定向促销、折扣等。为每个用户会话分配一个唯一 ID(一个键)以便于访问和存储。因此,存储此类数据的更好选择是键值数据库。
Document Database
文档数据库旨在存储和检索诸如 XML、JSON、BSON 等格式的文档。这些文档由层次树数据结构组成,可以包括地图、集合和标量值。这种类型的数据库中的文档可能具有不同的结构和数据。MongoDB 和 Google Cloud Firestore 是文档数据库的示例。
用例:文档数据库适用于非结构化目录数据,如 JSON 文件或其他复杂结构的分层数据。例如,在电子商务应用中,产品具有数千个属性,由于对读取性能的影响,将其存储在关系数据库中是不可行的。这就是文档数据库的作用,它可以高效地将每个属性存储在单个文件中,以便进行简单管理和更快的读取速度。此外,它也是内容管理应用的一个很好选择,比如博客和视频平台。在这些应用中,所需的实体被存储为单个文档。
Graph Database
图形数据库使用图形数据结构存储数据,其中节点表示实体,边表示实体之间的关系。基于关系的节点组织导致节点之间出现有趣的模式。这种数据库允许我们一次存储数据,然后根据关系以不同方式解释它。流行的图形数据库包括 Neo4J、OrientDB 和 InfiniteGraph。图形数据保存在存储文件中以进行持久存储。每个文件包含图的特定部分的数据,如节点、链接、属性等。
在下图中,一些数据使用图数据结构存储在通过边连接在一起的节点中,这些边表示节点之间的关系。每个节点都有一些属性,比如 Name , ID 和 Age 。具有 ID: 2 的节点具有 James 和 29 年的 Name 。
用例:图数据库可以用于社交应用程序,并在不同类型的用户及其活动之间提供有趣的事实和数据。图数据库的重点是存储数据,并为基于实体之间关系的分析和决策铺平道路。图数据库的性质使其适用于各种应用程序,如数据监管和隐私、机器学习研究、基于金融服务的应用程序等等。
Columnar Database
列式数据库将数据存储在列中,而不是行中。它们能够快速高效地访问数据库列中的所有条目。流行的列式数据库包括 HBase、Hypertable 和 Amazon Redshift。
使用案例:列式数据库对大量聚合和数据分析查询非常高效。它大大降低了磁盘 I/O 需求和从磁盘加载所需的数据量。例如,在与金融机构相关的应用程序中,需要在一段时间内对金融交易进行求和。列式数据库通过仅读取金额列,忽略客户的其他属性,使此操作更快。
下图显示了一个列式数据库的示例,其中数据以列向格式存储。这与关系数据库不同,关系数据库以行向方式存储数据。
缺点
- Lack of standardization: NoSQL 不遵循任何特定标准,就像关系数据库遵循关系代数一样。将应用程序从一种 NoSQL 数据库移植到另一种可能是一个挑战。
- Consistency: NoSQL 数据库根据一致性和可用性之间的特定权衡提供不同的产品,当发生故障时。我们不会像关系数据库中的主键和外键完整性那样具有强大的数据完整性。数据可能不是强一致的,而是使用弱模型逐渐收敛,如最终一致性。
关系、非关系总结
各种因素影响了在应用程序中使用的数据库的选择。下表显示了关系型和非关系型数据库之间的比较,以帮助我们做出选择:
| Relational Database | **Non-relational Database ** |
|---|---|
| If the data to be stored is structured | If the data to be stored is unstructured |
| If ACID properties are required | If there’s a need to serialize and deserialize data |
| If the size of the data is relatively small and can fit on a node) | If the size of the data to be stored is large |
数据复制
背景
数据是组织的资产,因为它推动整个业务。数据为业务提供关键洞察,揭示重要性和需要改变的内容。组织还需要安全地保存和提供客户数据。在不同条件下(增加读写、磁盘和节点故障、网络和电力中断等)及时访问所需数据对成功运营在线业务至关重要。
我们需要从我们的数据存储中获得以下保证:
- 在故障下的可用性(某些磁盘、节点、网络和停电的故障)。
- 可伸缩性(随着读取、写入和其他操作的增加)。
- 性能(客户端的低延迟和高吞吐量)。
在单个节点上实现上述特征是具有挑战性的,甚至是不可能的。
问题
复制是指在各个节点(最好是地理分布的)保留数据的多个副本,以实现可用性、可扩展性和性能。
然而,尽管有许多好处,比如可用性,复制也伴随着其复杂性。如果复制的数据不需要频繁更改,那么复制就相对简单。在复制中的主要问题是当我们必须随着时间保持复制数据中的更改时。
由于复制可能引起的额外复杂性如下:
- 我们如何确保多个数据副本之间保持一致?
- 我们如何处理失败的副本节点?
- 我们应该同步复制还是异步复制?
- 在异步复制的情况下,我们如何处理复制延迟?
- 我们如何处理并发写入?
- 需要向最终程序员公开什么一致性模型?
同步 vs 异步 复制
将更改传播到副本节点有两种方法:
- Synchronous replication 同步复制
- Asynchronous replication 异步复制
在同步复制中,主节点等待来自辅助节点关于更新数据的确认。在收到所有辅助节点的确认后,主节点向客户端报告成功。而在异步复制中,主节点不等待来自辅助节点的确认,在更新自身后向客户端报告成功。
同步复制的优势在于所有辅助节点与主节点完全保持同步。然而,这种方法也有一个缺点。如果其中一个辅助节点由于故障或网络故障而未能确认,主节点将无法向客户端发送确认,直到收到来自崩溃节点的成功确认。这会导致主节点向客户端响应的延迟较高。
另一方面,异步复制的优势在于,即使所有辅助节点都宕机,主节点仍然可以继续工作。然而,如果主节点失败,未复制到辅助节点的写入将会丢失。
数据复制模型
Simgle leader/primary-secondary replication 单领导、主从复制
在主从复制中,数据被复制到多个节点上。一个节点被指定为主节点。它负责处理对集群中存储的数据的任何写操作。它还将所有写操作发送到从节点并保持它们同步
主从复制在我们的工作负载以读为主时是合适的。为了更好地扩展随着读者数量的增加,我们可以添加更多的跟随者,并将读负载分布到可用的跟随者中。然而,将数据复制到许多跟随者可能会导致主服务器成为瓶颈。此外,如果我们的工作负载以写为主,主从复制是不合适的。
主次复制的另一个优势是它具有读取弹性。在主节点故障的情况下,辅助节点仍然可以处理读取请求。因此,这对于读取密集型应用程序是一种有益的方法。
通过这种方法进行复制,如果我们使用异步复制,就会出现不一致性。在主节点无法将更新数据传播到辅助节点的情况下,从不同副本读取数据的客户端可能会看到不一致的数据。因此,如果主节点失败,未传递给辅助节点的任何遗漏更新可能会丢失。
Primary-secondary replication methods 主从复制方法
在主从复制中有许多不同的复制方法:
- Statement-based replication
- Write-ahead log(WAL) shipping
- Logical (row-based) replication
Statement-based replication
基于语句的复制(SBR)是 MySQL 数据库中使用的一种方法。在这种方法中,主节点执行 SQL 语句,如 INSERT , UPDATE , DELETE 等,然后将这些语句写入日志文件。在下一步中,日志文件被发送到辅助节点进行执行。这种类型的复制在 MySQL 5.1 版本之前使用过。
尽管这种复制方式看起来不错,但它也有一些缺点。例如,任何非确定性函数,如 NOW() ,可能导致主节点和辅助节点上的写入不同。
Write-ahead log(WAL) shipping
写前日志(WAL)传输是 PostgreSQL 和 Oracle 中使用的数据复制技术。在这种技术中,当事务发生时,首先将其记录在事务日志文件中,然后将日志文件写入磁盘。随后,在将记录的操作传输到辅助节点执行之前,这些操作会在主数据库上执行。与 SBR 不同,WAL 维护事务日志而不是将 SQL 语句记录到日志文件中,确保在处理非确定性函数时保持一致性。将数据写入磁盘还有助于在发生崩溃故障时进行恢复。
例如,在 PostgreSQL 中执行像 UPDATE 这样的操作时,它首先被写入事务日志文件和磁盘,然后才被应用到数据库。事务日志中的条目可以包括事务 ID、操作类型、受影响的表和新值等详细信息,之后更改会被复制到辅助节点。然而,WAL 的缺点是它与数据库引擎内部结构紧密耦合,使得在主节点和从节点上进行软件升级变得复杂。
Logical (row-based) replication
逻辑(基于行的)复制被应用于各种关系型数据库,包括 PostgreSQL 和 MySQL。在这种方法中,对数据库所做的更改被捕获在单个行的级别,然后被复制到辅助节点。这种方法不是复制对数据库所做的实际物理更改,而是以逻辑格式捕获操作,然后在辅助节点上执行这些操作。
例如,当执行类似 INSERT 或 UPDATE 的操作时,整个受影响的行会在主节点上被捕获,包含指定行的所有列值。然后在辅助节点上执行这个捕获的更改,以确保数据与主节点上的数据保持一致。它在灵活性和与不同类型模式的兼容性方面提供了优势。
Multi-leader replication
如上所述,使用异步复制的单领导者复制存在一个缺点。只有一个主节点,所有写操作都必须经过它,这限制了性能。如果主节点发生故障,次要节点可能没有更新的数据库。
多领导者复制是单领导者复制的一种替代方案。有多个主节点处理写操作并将其发送到所有其他主节点和辅助节点以进行复制。这种复制类型在数据库中与外部工具(如 MySQL 的 Tungsten Replicator)一起使用。
这种复制在应用程序中非常有用,即使我们离线也可以继续工作——例如,一个日历应用程序,即使没有互联网访问,我们也可以安排会议。一旦我们联机,它会将更改从我们的本地数据库(我们的手机或笔记本电脑充当主节点)复制到其他节点。
冲突Conflicts
多领导者复制比单领导者复制具有更好的性能和可伸缩性,但它也有一个显著的缺点。由于所有主节点同时处理写请求,它们可能修改相同的数据,这可能会在它们之间创建冲突。例如,假设两个客户端同时编辑相同的数据。在这种情况下,它们的写入将在它们各自关联的主节点上成功,但当它们异步到达其他主节点时,就会创建冲突。
处理冲突
突可能导致不同节点上的不同数据。这些应该在不丢失任何数据的情况下高效处理。让我们讨论一些处理冲突的方法:
冲突避免Conflict avoidiance
处理冲突的一个简单策略是在第一时间防止它们发生。如果应用程序可以验证所有给定记录的写入都通过同一领导节点,则可以避免冲突发生。
然而,如果用户移动到不同位置并且现在靠近不同的数据中心,则冲突仍可能发生。 如果发生这种情况,我们需要重新路由流量。 在这种情况下,冲突避免方法失败并导致并发写入。
最后写入为准Last write wins
使用本地时钟,所有节点为每个更新分配一个时间戳。当发生冲突时,选择具有最新时间戳的更新。
这种方法也可能会带来困难,因为在分布式系统中节点之间的时钟同步是具有挑战性的。时钟偏差可能导致数据丢失。
自定义逻辑Custom logic
在这种方法中,我们可以根据应用程序的需要编写自己的逻辑来处理冲突。这种自定义逻辑可以在读取和写入时执行。当系统检测到冲突时,它会调用我们的自定义冲突处理程序。
Multi-leader replication ropologise多领导者复制拓扑
多领导者复制实现的拓扑结构有很多种,例如环形拓扑结构、星形拓扑结构和全互联拓扑结构。最常见的是全互联拓扑结构。在星形和环形拓扑结构中,同样存在一个类似的缺点,即如果其中一个节点失败,可能会影响整个系统。这就是为什么全互联是最常用的拓扑结构。
Peer to peer/leaderless replication点对点、无主复制
在主从复制中,主节点是一个瓶颈和单点故障。此外,它有助于实现读取可伸缩性,但无法提供写入可伸缩性。点对点复制模型通过没有单个主节点来解决这些问题。所有节点具有相同的权重,并且可以接受读取和写入请求。这种复制方案可以在 Cassandra 数据库中找到。
与主从复制一样,这种复制也可能导致不一致性。这是因为当多个节点接受写请求时,可能会导致并发写入。用于解决写-写不一致性的一种有用方法称为仲裁。
选举Quorums
假设我们有三个节点。如果至少三个节点中的两个节点保证返回成功的更新,那么只有一个节点失败。这意味着如果我们从两个节点读取,至少其中一个将有更新版本,我们的系统可以继续工作。
数据分区
背景
数据是任何组织的资产。增加数据和并发读/写流量对传统数据库施加了可扩展性压力。因此,延迟和吞吐量受到影响。传统数据库具有诸如范围查询、二级索引和具有 ACID 属性的事务等特性,因此备受青睐。
在某个时候,基于单个节点的数据库已经不足以处理负载。我们可能需要将数据分布在许多节点上,但仍然要导出关系数据库的所有优点。实践中,证明在分布式数据库上提供类似单节点数据库的特性是具有挑战性的。
一种解决方案是将数据移动到类似 NoSQL 的系统中。然而,历史代码库及其与传统数据库的紧密联系使其成为一个昂贵的问题。
组织可以通过使用第三方解决方案来扩展传统数据库。但通常,整合第三方解决方案会有其复杂性。更重要的是,有丰富的机会来针对特定问题进行优化,并获得比通用解决方案更好的性能。
数据分区(或分片)我们将一个大型数据集分割成存储在网络上不同节点的较小数据块,使我们能够使用多个节点,其中每个节点管理整个数据的一部分。为了处理不断增加的查询速率和数据量,我们努力实现平衡的分区和平衡的读/写负载。
区必须平衡,以便每个分区接收大致相同数量的数据。如果分区不平衡,大多数查询将落入少数几个分区。负载过重的分区将导致系统瓶颈。分区的有效性将受损,因为大部分数据检索查询将被发送到承载高度拥挤分区的节点。这些分区被称为热点。通常,我们使用以下两种方式来分片数据:
- Vertical sharding 垂直分区
- Horizontal sharding 水平分区
垂直分区Vertical sharding
我们可以将不同的表放在不同的数据库实例中,这些实例可能在不同的物理服务器上运行。我们可以将一个表分成多个表,使得一些列在一个表中,而其余列在另一个表中。如果多个表之间存在连接,我们应该小心。我们可能希望将这些表放在一个分片上保持在一起。
通常,垂直分片用于增加从包含非常宽文本或二进制大对象(blob)列的表中检索数据的速度。在这种情况下,具有大文本或 blob 的列被拆分到不同的表中。
如下图所示, Employee 表分为两个表:一个是简化的 Employee 表,另一个是 EmployeePicture 表。 EmployeePicture 表只有两列, EmployeeID 和 Picture ,与原始表分开。此外, Employee 表的主键 EmployeeID 也添加到了分区表中。这样可以使数据的读写更加容易,表的重建也更加高效。
垂直分片有其复杂性,并更适合手动分区,利益相关者会仔细决定如何分割数据。相比之下,水平分片适合在动态条件下自动化。
水平分区Horizontal sharding
有时,数据库中的一些表变得太大,影响读写延迟。水平分片或分区用于通过按行拆分数据将表分成多个表,如下一节中的图所示。分布在数据库服务器上的原始表的每个分区称为一个分片。通常有两种可用的策略:
- 基于键范围的分区Key-rang based sharding
- 基于哈希的分区Hash based sharding
Key-rang based sharding
在基于键范围的分片中,每个分区被分配一个连续的键范围。
在下图中,使用基于键范围的分片,对 Invoice 表进行水平分区, Customer_Id 作为分区键。两个不同颜色的表代表分区。
有时,数据库由多个通过外键关系绑定的表组成。在这种情况下,使用相同的分区键在关系中的所有表上执行水平分区。属于相同分区键的表(或子表)被分发到一个数据库分片。下图显示了几个具有相同分区键的表放置在一个单独的数据库分片中:
优点如下:
- 使用基于键范围的分片方法,基于范围查询的方案易于实现。我们精确地知道在哪里(哪个节点,哪个分片)查找特定范围的键。
- 范围查询可以使用分区键执行,并且这些分区可以按排序顺序保留。随着新数据的输入,这样的排序如何随时间发生是特定于实现的。
缺点如下:
- 范围查询不能使用分区键以外的键执行
- 如果密钥没有正确选择,一些节点可能需要存储更多的数据,因为流量分布不均匀。
Hash based sharding
基于哈希的分片使用属性上的哈希函数。这个哈希函数产生一个哈希值,用于执行分区。主要概念是在密钥上使用哈希函数以获得哈希值,然后通过分区数取模。一旦我们为密钥找到了合适的哈希函数,我们可以为每个分区分配一系列哈希(而不是一系列密钥)。任何哈希出现在该范围内的密钥将被保留在该分区中。
在下面的插图中,我们使用哈希函数 ValueValue mod=nmod=n 。 nn 是节点数,即四个。我们通过检查每个键的模来为节点分配键。具有模值为 2 的键分配给节点 2。具有模值为 1 的键分配给节点 1。具有模值为 3 的键分配给节点 3。因为没有模值为 0 的键,节点 0 被空置。
优点:
- 密钥在节点之间均匀分布。
缺点:
- 我们无法使用这种技术执行范围查询。密钥将分布在所有分区上。
分区流程
根据经验,我们可以确定每个节点可以提供多少可接受的性能。这可以帮助我们找出我们希望在任何一个节点上保留的最大数据量。例如,如果我们发现我们可以在一个节点上放置最多 50 GB 的数据,我们有以下情况:
数据库大小 == 10 TB
单个分片的大小为 == 50 GB
数据库应该分布在 == 10 TB/50 GB == 200 个分区
一致性哈希Consitent hashing
一致性哈希将分布式哈希表中的每个服务器或项分配到一个抽象圆环上的位置,称为环,而不考虑表中的服务器数量。这使得服务器和对象可以扩展,而不会影响系统的整体性能。
优点:
- 横向扩展很容易。
- 它增加了吞吐量并改善了应用程序的延迟。
缺点:
- 在环中随机分配节点可能会导致不均匀的分布。
重新平衡分区
查询负载可能由于多种原因在节点之间不平衡,包括以下原因:
- 数据的分布不均匀。
- 单个分区负载过重。
- 查询流量增加,我们需要添加更多节点以跟上。
我们可以应用以下策略来重新平衡分区。
避免哈希取模
通常,我们避免对密钥进行哈希分区(我们之前使用这样的方案来简单解释哈希概念)。在 hashmodnhashmodn 的情况下,添加或移除节点的问题在于每个节点的分区号都会改变,导致大量数据移动。例如,假设我们有 hash(key)=1235hash(key)=1235 。如果一开始有五个节点,密钥将从节点 1 开始( 12351235 modmod 5=05=0 )。现在,如果添加了一个新节点,密钥将需要移动到节点 6( 12351235 modmod 6=56=5 ),以此类推。将密钥从一个节点移动到另一个节点会导致重新平衡成本高昂。
固定数量的分区
在这种方法中,创建的分区数量在设置数据库时是固定的。我们创建比节点更多的分区,并将这些分区分配给节点。因此,当系统添加新节点时,它可以从现有节点中获取一些分区,直到分区被平均分配。
这种方法存在一个缺点。每个分区的大小随着集群中的数据总量增长而增加,因为所有分区都包含总数据的一小部分。如果一个分区非常小,那么会导致太多的开销,因为我们可能需要创建大量小型分区,每个分区都会带来一些开销。如果分区非常大,重新平衡节点和从节点故障中恢复将会很昂贵。选择正确数量的分区非常重要。在 Elasticsearch、Riak 等许多系统中使用了固定数量的分区。
动态分区
在这种方法中,当一个分区的大小达到阈值时,它会被平均分成两个分区。这两个分区中的一个分配给一个节点,另一个分配给另一个节点。这样,负载被平均分配。分区的数量会根据整体数据量进行调整,这是动态分区的优势。
然而,这种方法也有缺点。在提供读写服务时应用动态重新平衡是困难的。在读写过程中进行动态重新平衡是具有挑战性的,因为它涉及在节点之间移动数据,导致延迟和潜在冲突。确保数据一致性(因为数据同时被移动和访问)和可用性(可能需要在重新平衡期间暂停读写操作)引入了可能影响系统性能和可靠性的复杂性。这种方法在 HBase 和 MongoDB 中被使用。
按节点比例划分
在这种方法中,分区的数量与节点数量成比例,这意味着每个节点有固定的分区。在早期的方法中,分区的数量取决于数据集的大小。但在这里不是这种情况。虽然节点数量保持不变,但每个分区的大小会根据数据集的大小而增加。然而,随着节点数量的增加,分区会缩小。当新节点进入网络时,它会随机分割一定数量的当前分区,然后取分割的一半,留下另一半。这可能导致不公平的分割。这种方法被 Cassandra 和 Ketama 使用。
分区和二级索引
我们已经讨论了基于键值数据模型的分区方案,其中记录是通过主键检索的。但是如果我们必须通过辅助索引访问记录怎么办?辅助索引是不通过主键标识的记录,而只是一种搜索某个值的方式。例如,上面关于水平分区的示例包含了客户表,搜索所有创建年份相同的客户。
我们可以通过以下方式使用辅助索引进行分区。
通过分区生成对应的二级索引
在这种索引方法中,每个分区都是完全独立的。每个分区都有其次要索引,仅覆盖该分区中的文档。它不关心其他分区中保存的数据。如果我们想要向数据库写入任何内容,我们需要处理仅包含我们正在写入的文档 ID 的那个分区。这也被称为本地索引。在下面的示例中,有三个分区,每个分区都有自己的标识和数据。如果我们想要获取所有具有名称 John 的客户 ID,我们必须从所有分区请求。
然而,对辅助索引进行此类查询可能会很昂贵。由于受到性能差的分区延迟的限制,读取查询的延迟可能会增加。
通过对应待索引内容生成全局二级索引
不必为每个分区创建二级索引(本地索引),我们可以为涵盖所有分区数据的二级索引的内容创建全局索引。
在下面的插图中,我们在名称上创建索引(我们正在分区的术语),并将所有名称的索引存储在分开的节点上。要获取所有名为“ John ”的客户的 cust_id ,我们必须确定我们的术语索引位于何处。 index 0 包含所有以“A”至“M”开头的客户。 index 1 包括所有以“N”至“Z”开头的客户。因为 John 位于 index 0 中,我们从 index 0 中提取名为 John 的 cust_id 列表。
通过术语对辅助索引进行分区比通过文档对辅助索引进行分区更具读取效率。这是因为它只访问包含该术语的分区。然而,在这种方法中,单个写入会影响多个分区,使得该方法写入密集且复杂。
请求路由
在发出请求时,客户端如何知道要连接到哪个节点?在重新平衡后,分区分配给节点的方式会有所变化。如果我们想要读取特定的键,我们如何知道需要连接到哪个 IP 地址来读取?
这个问题也被称为服务发现。以下是解决这个问题的几种方法:
- 允许客户端请求网络中的任何节点。如果该节点不包含所请求的数据,则将该请求转发到包含相关数据的节点。
- 第二种方法包含一个路由层。所有请求首先被转发到路由层,它确定要连接到哪个节点来满足请求。
- 客户已经拥有与分区相关的信息,知道哪个分区连接到哪个节点。因此,他们可以直接联系包含他们需要的数据的节点。
在所有这些方法中,主要挑战是确定这些组件如何了解节点分区更新的情况。
了跟踪集群中的变化,许多分布式数据系统需要像 ZooKeeper 这样的单独管理服务器。ZooKeeper 跟踪网络中的所有映射,每个节点都连接到 ZooKeeper 获取信息。每当分区发生变化,或者添加或删除节点时,ZooKeeper 会更新并通知路由层有关变化。HBase、Kafka 和 SolrCloud 都使用 ZooKeeper。
总结
水平分片和垂直分片都涉及向我们的计算基础设施添加资源。我们的业务利益相关者必须决定哪种适合我们的组织。我们必须根据我们的组织和业务的增长来适当扩展我们的资源,以防止停机,并减少延迟。我们可以通过调整 CPU、物理内存需求、硬盘调整和网络带宽的组合来扩展这些资源。
以下部分解释了不分片与分片的优缺点。
集中式数据库优缺点
优点:
- 数据维护,如更新和备份集中数据库,是容易的。
- 集中式数据库提供比分布式数据库更强的一致性和 ACID 事务。
- 集中式数据库为最终程序员提供了比分布式数据库更简单的编程模型。
- 对于只有少量数据需要存储且可以驻留在单个节点上的企业来说,这更加高效。
缺点:
- 集中式数据库可能会变慢,当每秒访问集中式数据库的查询次数接近单节点限制时,会导致终端用户延迟高。
- 集中式数据库存在单点故障。因此,
- 它不可访问的概率要高得多。
分布式数据库优缺点
优点:
- 访问分布式数据库中的数据快速且简便,因为数据是从最近的数据库分片或经常使用的分片中检索的。
- 数据可以存储在不同透明度级别的地方。
- 由查询组成的密集交易可以分为多个优化的子查询,可以并行处理。
缺点:
- 有时候,需要从多个站点获取数据,这比预期的时间更长。
- 关系在不同节点之间垂直或水平分区。因此,诸如连接之类的操作需要通过仔细获取数据来重建完整的关系。这些操作可能变得更加昂贵和复杂。
- 在分布式数据库中保持数据的一致性很困难,需要额外的措施。
- 分布式数据库中的更新和备份需要时间来同步数据。