数据复制
数据复制
了解数据在多个节点之间复制的模型。
数据是组织的资产,因为它推动整个业务。
数据为业务提供了重要的商业见解,揭示了什么是重要的,需要改变什么。
组织还需要安全地保存并按需提供其客户的数据。
在不同的条件下(如读写增加、磁盘和节点故障、网络和电源故障等),及时访问所需数据是成功运营在线业务所必需的。
我们需要从数据存储中获得以下特性:
- 容错性(故障下可用,如磁盘、节点、网络和电源故障)。
- 可扩展性(随着读取、写入和其他操作的增加)。
- 性能(客户端的低延迟和高吞吐量)。
在单个节点上实现上述特性是具有挑战性的,甚至不可能。
复制
复制指在各个节点(最好是地理分布)保留数据的多个副本,以实现可用性、可扩展性和性能。
在本课程中,我们假设单个节点足以容纳我们的整个数据。
在讨论将数据分区到多个节点时,我们不会使用此假设。通常,复制和分区的概念是相互关联的。
然而,与可用性等诸多好处相比,复制也带来了其复杂性。
如果复制的数据不需要频繁更改,则复制相对简单。在复制数据的过程中,主要问题在于如何随时间维护复制的数据的更改。
复制可能会引起的其他复杂性如下:
- 如何使多个数据副本相互一致?
- 如何处理失败的复制节点?
- 我们应该同步复制还是异步复制?
- 在异步复制的情况下,如何处理复制滞后?
- 如何处理并发写入?
- 需要向终端程序员公开哪种一致性模型?
我们将在本课程中探讨这些问题的答案。
复制实例
在解释不同类型的复制之前,让我们了解同步和异步复制的方法。
同步与异步复制
有两种方法可以将更改分发到副本节点:
- 同步复制
- 异步复制
在同步复制中,主节点等待从节点关于更新数据的确认。在收到所有从节点的确认后,主节点向客户端报告成功。而在异步复制中,主节点不等待从节点的确认,更新自身后向客户端报告成功。
同步复制的优点是所有从节点都完全与主节点保持最新。但是,这种方法存在缺点。如果由于故障或网络故障而导致其中一个从节点无法确认,主节点将无法向客户端确认,直到它收到崩溃节点的成功确认。这会导致主节点向客户端响应的高延迟。
另一方面,异步复制的优点是即使所有从节点都已关闭,主节点也可以继续工作。但是,如果主节点失败,则未复制到从节点的写入将丢失。
上面的段落解释了当系统的不同组件可能失败时,数据一致性和可用性之间的权衡。
同步与异步复制
数据复制模型
现在,让我们讨论各种数据复制机制。在本节中,我们将讨论以下模型以及它们的优缺点:
- 单个领导者或主要-次要复制
- 多个领导者复制
- 对等或无领导复制
单个领导者/主要-次要复制
在主要-次要复制中,数据在多个节点之间进行复制。
其中一个节点被指定为主节点。它负责处理存储在群集上的数据的任何写入。
它还将所有写入发送到从节点并保持它们同步。
如果我们的工作负载以读为主,那么主要-次要复制是合适的。
为了更好地随着读者的增加进行扩展,我们可以添加更多的跟随者,并将读取负载分布到可用的跟随者上。
但是,向许多跟随者复制数据可能会使主节点成为瓶颈。
此外,如果我们的工作负载以写为主,主要-次要复制是不合适的。
主要-次要复制的另一个优点是它具有读取弹性。
在主节点故障的情况下,从节点仍然可以处理读取请求。
因此,对于读取密集型应用程序,这是一种有用的方法。
如果我们使用异步复制,则通过此方法进行的复制可能会出现不一致性。
在主节点无法将更新的数据传播到从节点的情况下,从不同副本读取的客户端可能会看到不一致的数据。
因此,如果主节点失败,则未传递给从节点的任何丢失的更新都可能会丢失。
警告
问题
当主节点失败时会发生什么?
答案:
在主节点失败的情况下,可以任命一个从节点作为主节点,从而加快恢复初始主节点的过程。
选择新的主节点有两种方法:手动和自动。
在手动方法中,操作员决定哪个节点应该成为主节点,并通知所有从节点。
在自动方法中,当从节点发现主节点失败时,它们通过进行一种称为领导者选举的选举任命新的主节点。
主要-次要复制方法
在主要-次要复制中有许多不同的复制方法:
- 基于语句的复制
- 写前日志(WAL)传送
- 逻辑(基于行的)日志复制
让我们详细讨论每个方法。
基于语句的复制
在基于语句的复制方法中,主节点保存执行的所有语句,例如插入、删除、更新等,并将它们发送给从节点执行。
这种类型的复制在MySQL 5.1版本之前使用过。
这种方法似乎很好,但它也有缺点。例如,任何非确定性函数(如NOW()
)可能会导致跟随者和主节点上的不同写操作。
此外,如果写操作依赖于先前的写操作,并且它们以错误的顺序到达从节点,则从节点上的结果将不确定。
日志先写(WAL)复制
在日志先写(WAL)复制方法中,主节点在执行查询之前将查询保存在称为日志先写文件的日志文件中。然后使用这些日志将数据复制到从节点。
这在PostgreSQL和Oracle中使用。
WAL的问题在于它只在非常低的级别上定义数据。
它与数据库引擎的内部结构紧密耦合,这使得升级领导者和从节点上的软件变得复杂。
逻辑(基于行)日志复制
在逻辑(基于行)日志复制方法中,所有从节点都复制实际的数据更改。
例如,如果在表中插入或删除一行,则从节点将在该特定表中复制该更改。
二进制日志记录主节点上数据库表的更改。
为了创建主节点的副本,从节点读取此数据并相应地更改其记录
基于行的复制不像WAL那样困难,因为它不需要关于数据库引擎内部数据布局的信息。
多主节点复制
如上所述,使用异步复制的单个主节点复制存在缺点。
只有一个主节点,所有写操作都必须通过它,这限制了性能。
如果主节点出现故障,从节点可能没有更新的数据库。
多主节点复制是单个主节点复制的替代方法。有多个主节点处理写入并将它们发送到所有其他主节点和从节点以进行复制。这种复制类型与外部工具(如MySQL的Tungsten Replicator)一起在数据库中使用。
这种复制在应用程序中非常有用,即使我们离线也可以继续工作。
例如,在日历应用程序中,我们可以设置会议,即使没有互联网访问。
一旦联网,它将从我们的本地数据库(我们的移动电话或笔记本电脑作为主节点)复制其更改到其他节点。
多主数据复制模型(全部到全部拓扑)
冲突
多主节点复制比单个主节点复制具有更好的性能和可扩展性,但它也有一个重要的缺点。由于所有主节点同时处理写请求,它们可能修改相同的数据,这可能会在它们之间创建冲突。例如,假设两个客户端同时编辑相同的数据,则它们的写入操作将在它们各自的主节点上成功,但当它们异步到达其他主节点时,会创建冲突。
处理冲突
冲突可能导致不同节点上的不同数据。这些应该有效地处理,而不会丢失任何数据。让我们讨论一些处理冲突的方法:
冲突避免
解决冲突的一个简单策略是在第一时间防止它们发生。如果应用程序能够验证给定记录的所有写操作都经过相同的领导者,就可以避免冲突。
但是,如果用户移动到不同的位置并且现在靠近不同的数据中心,冲突可能仍会发生。
如果发生这种情况,我们需要重新路由流量。在这种情况下,冲突避免方法失败并导致并发写操作。
最后写入获胜
所有节点都使用本地时钟为每个更新分配一个时间戳。当发生冲突时,选择具有最新时间戳的更新。
这种方法也可能带来困难,因为在分布式系统中,节点之间的时钟同步具有挑战性。时钟偏差可能会导致数据丢失。
自定义逻辑
在这种方法中,我们可以编写自己的逻辑来处理冲突,以满足应用程序的需求。
这个自定义逻辑可以在读写操作时执行。当系统检测到冲突时,它将调用我们的自定义冲突处理程序。
多领导者复制拓扑结构
有许多拓扑结构可以实现多领导者复制,例如环形拓扑结构、星形拓扑结构和全互连拓扑结构。
其中最常见的是全互连拓扑结构。在星形和环形拓扑结构中,存在与主从复制相似的缺点,即如果其中一个节点失败,它可能会影响整个系统。
这就是为什么全互连拓扑结构是最常用的拓扑结构。
对等/无领导者复制
在主从复制模型中,主节点成为瓶颈和单点故障。此外,它有助于实现读的可伸缩性,但无法提供写的可伸缩性。
对等/无领导者复制模型通过没有单个主节点来解决这些问题。所有节点具有相同的权重,并且可以接受读和写请求。
亚马逊在他们的DynamoDB数据存储中流行了这种方案。
对等数据复制模型,所有节点都对所有数据执行读写操作
与主从复制类似,这种复制也可能导致不一致性。
这是因为当多个节点接受写请求时,可能会导致并发写入。
用于解决写-写不一致性问题的有用方法称为Quorums。
Quorums
假设我们有三个节点。如果至少有三个节点中的两个保证返回成功更新,则表示只有一个节点失败。
这意味着如果我们从两个节点读取,至少其中一个将具有更新的版本,我们的系统可以继续工作。
如果我们有 n 个节点,那么每次写操作至少要在 w 个节点上进行更新才被视为成功,并且我们必须从 r 个节点上进行读取。
只要 w+r>n ,我们就可以通过读取获取更新后的值,因为至少有一个节点必须具有我们可以读取的已更新写入。
Quorum 读取和写入遵守这些 r 和 w 值。
这些 n、 w 和 r 可以在 Dynamo 风格的数据库中进行配置。
警告
当我们的工作负载以读为主时,哪种复制机制最适合(高吞吐量、低延迟、低实现开销)?
A) 主-备份/单主复制
B) 多主复制
C) 点对点复制