分布式复制和分区

Posted by 令德湖周杰伦 on 03-27,2024

1. 分布式复制

分布式复制是指:在多个节点上保存相同数据的副本,复制可以提供冗余:如果某些节点发生不可用,则可以通过其他节点继续提供数据访问服务。复制也可以帮助提高系统性能。

  • 使数据在地理位置上更接近用户,从而降低访问延迟。
  • 当部分组件出现位障,系统依然可以继续工作,从而提高可用性。
  • 扩展至多台机器以同时提供数据访问服务,从而提高读吞吐量。

架构上包括:

  • 主从复制
  • 多主节点复制
  • 无主节点复制

复制方式又分为:

  • 同步复制
  • 异步复制

1.1 主从复制

1.1.1 原理:

主从复制的工作原理如下:

  1. 指定某一个副本为主副本(或称为主节点) 。当客户写数据库时,必须将写请求首先发送给主副本,主副本首先将新数据写入本地存储。
  2. 其他副本则全部称为从副本(或称为从节点)。主副本把新数据写入本地存储后,然后将数据更改作为复制的日志或更改流发送给所有从副本。每个从副本获得更改日志之后将其应用到本地,且严格保持与主副本相同的写入顺序。
  3. 客户端从数据库中读数据时,可以在主副本或者从副本上执行查询。只有主副本才可以接受写请求,从副本都是只读的(从客户端的角度来看,不包括数据复制)

典型应用:

  • 数据库:MySql、MongoDB 等
  • 消息队列:Kafka、RabbitMQ 等

1.1.2 同步复制与异步复制

  • 同步复制的优点: 一旦向用户确认,从节点可以明确保证完成了与主节点的更新同步,数据已经处于最新版本。万一主节点发生故障,总是可以在从节点继续访问最新数据。

  • 同步复制的缺点:如果同步的从节点无法完成确认(例如由于从节点发生崩愤,或者网络故障,或任何其他原因), 写入就不能视为成功。主节点会阻塞其后所有的写操作,直到同步副本确认完成。

  • 异步复制的优点:不管从节点上数据多么滞后,主节点总是可以继续响应写请求,系统的吞吐性能更好。

  • 异步复制的缺点:如果主节点发生失败且不可恢复,则所有尚未复制到从节点的写请求都会丢失。

1.1.3 增加新的从节点

当如果出现以下情况时,如需要增加副本数以提高容错能力,或者替换失败的副本,就需要考虑增加新的从节点。但如何确保新的从节点和主节点保持数据一致呢?

  • 简单地将数据文件从一个节点复制到另一个节点通常是不够的。主要是因为客户端仍在不断向数据库写入新数据,数据始终处于不断变化之中,
  • 考虑锁定数据库(使其不可写)来使磁盘上的文件保持一致,但这会违反高可用的设计目标。

正确的思路是:
在不停机、数据服务不中断的前提下,也有一种可行性复制方案,其主要操作步骤如下:

  1. 在某个时间点对主节点的数据副本产生一个一致性快照,这样避免长时间锁定整个数据库。目前大多数数据库都支持此功能,快照也是系统备份所必需的。而在某些情况下,可能需要第三方工具, 如 MySQL 的 inno backup ex。
  2. 将此快照拷贝到新的从节点。
  3. 从节点连接到主节点并请求快照点之后所发生的数据更改日志。因为在第一步创建快照时,快照与系统复制日志的某个确定位置相关联,这个位置信息在不同的系统有不同的称呼,如 PostgreSQL 将其称为“ log sequence number” (日志序列号),而 MySQL 将其称为“ binlog coordinates ” 。
  4. 获得日志之后,从节点来应用这些快照点之后所有数据变更,这个过程称之为追赶。接下来,它可以继续处理主节点上新的数据变化。井重复步骤 1 ~步骤 4 。

1.3.4 处理节点失效

系统中的任何节点都可能因故障或者计划内的维护(例如重启节点以安装内核安全补丁)而导致中断甚至停机。我们的目标是,尽管个别节点会出现中断,但要保持系统总体的持续运行,并尽可能减小节点中断带来的影响。

1.3.4.1 从节点失效:追赶式恢复

从节点的本地磁盘上都保存了副本收到的数据变更日志,从节点可以知道在发生故障之前所处理的最后一笔事务,然后连接到主节点,并请求自那笔事务之后中断期间内所有的数据变更。进行追赶,之后就是正常主从数据流。

1.3.4.2 主节点失效:主从切换

切换可以手动,这里说的是自动切换,步骤通常如下:

  1. 确认主节点失效:基于超时的机制,节点间互相发生发送心跳悄息,如果发现某一个节点在一段比较长时间内(例如 30s )没有响应,即认为该节点发生失效。
  2. 选举新的主节点:可以通过选举的方式(超过多数的节点达成共识)来选举新的主节点,或者由之前选定的某控制节点来指定新的主节点。候选节点最好与原主节点的数据差异最小,这样可以最小化数据丢失的风险。让所有节点同意新的主节点是个典型的共识问题。
  3. 重新配置系统使新主节点生效:客户端现在需要将写请求发送给新的主节点,且保证原主又上线后不会抢主。

其他问题:

  • 如果使用了异步复制,且失效之前,新的主节点并未收到原主节点的所有数据。原主快速重新上线后还会同步数据,而新的主已经在接受写请求了,发生数据冲突
常见的解决方案是,原主节点上未完成复制的写请求就此丢弃,但这可能会违背数据更新持久化的承诺。
  • 在某些故障情况下,可能会发生两个节点同时-都自认为是主节点。这种情况被称为【脑裂】,两个主节点都可能接受写请求
作为一种安全应急方案,有些系统会采取措施来强制关闭其中一个节点。然而,如果设计或者实现考虑不周,可能会出现两个节点都被关闭的情况。
  • 如何设置合适的超时检测时间
超时时间设置得越长也意味着总体恢复时间就越长。但如果超时设置太短,可能会导致很多不必要的切换。例如,突发的负载峰值会导致节点的响应时间变长甚至超肘,或者由于网络故障导致延迟增加。如果系统此时已经处于高负载压力或网络已经出现严重拥塞,不必要的切换操作只会使总体情况变得更糟。

1.3.5 复制日志的实现

1.3.5.1 基于语句的复制

主节点记录所执行的每个写请求(操作语句)井将该操作语句作为日志发送给从节点。对于关系数据库,这意味着每个 INSERT 、UPDATE 或 DELETE 语句都会转发给从节点,并且每个从节点都会分析井执行这些 SQL 语句

存在的问题:

  • 任何调用非确定性函数的语句,如 NOW() 获取当前时间,或 RAND() 获取一个随机数等,可能会在不同的副本上产生不同的值。
  • 如果语句中使用了自增列,或者依赖于数据库的现有数据(例如, UPDATE ... WHERE <某些条件>),则所有副本必须按照完全相同的顺序执行,否则可能会带来不同的结果。
  • 有副作用的语句(例如,触发器、存储过程、用户定义的函数等),可能会在每个副本上产生不同的副作用。

MySQL 5.1 版本之前采用基于操作语句的复制。现在由于逻辑紧凑,依然在用,但是默认情况下,如果语句中存在一些不确定性操作,则 MySQL 会切换到基于行的复制

1.3.5.2 基于预写日志(WAL)传输

  • 通常每个写操作都是以追加写的方式写入到日志中。
  • 所有对数据库写入的字节序列都被记入日志。因此可以使用完全相同的日志在另一个节点上构建副本:除了将日志写入磁盘之外, 主节点还可以通过网络将其发送给从节点。

存在的问题:

  • 日志描述的数据结果非常底层,一个 WAL 包含了哪些磁盘块的哪些字节发生改变,诸如此类的细节。
  • 不支持跨版本、跨应用升级:该复制方案和存储引擎紧密耦合。如果数据库的存储格式从一个版本改为另一个版本,那么系统通常无能支持主从节点上运行不同版本的软件。

1.3.5.3 基于行的逻辑日志复制

也叫逻辑日志复制,以区分物理存储引擎的数据表示。
关系数据库的逻辑日志通常是指一系列记录来描述数据表行级别的写请求:

  1. 对于行插入,日志包含所有相关列的新值。
  2. 对于行删除,日志里有足够的信息来唯一标识已删除的行,通常是靠主键,但如果表上没有定义主键,就需要记录所有列的旧值。
  3. 对于行更新,日志包含足够的信息来唯一标识更新的行,以及所有列的新值(或至少包含所有已更新列的新值)。

如果一条事务涉及多行的修改,则会产生多个这样的日志记录,并在后面跟着一条记录,指出该事务已经提交。MySQL 的二进制日志 binlog (当配置为基于行的复制时)使用该方式。

特点:

  • 逻辑日志与存储引擎逻辑解耦,因此可以更容易地保持向后兼容,从而使主从节点能够运行不同版本的软件甚至是不同的存储引擎
  • 对于外部应用程序来说,逻辑日志格式也更容易理解和解析

1.3.5 主从延迟问题(复制滞后问题)

  1. 实现数据最终一致性即可

  2. dbproxy层处理

    1. 写请求到proxy时,写入的数据也会写入缓存中,缓存时间大于主从延迟的时间
    2. 读请求,到dbproxy时,直接读缓存,返回
    3. 超时后直接,读从即可
  3. 强制读主(需要慎重评估场景和性能)

1.2 多主节点复制

对主从复制模型进行自然的扩展,则可以配置多个主节点,每个主节点都可以接受写操作,后面复制的流程类似: 处理写的每个主节点都必须将该数据更改转发到所有其他节点。这就是多主节点(称为主-主)复制,此时,每个主节点还同时扮演其他主节点的从节点。

1.2.1 使用场景

  • 多数据中心:在一个数据中心内部使用多主节点基本没有太大意义,其复杂性已经超过所能带来的好处。
    • 性能:而在多主节点模型中,每个写操作都可以在本地数据中心快速响应,然后采用异步复制方式将变化同步到其他数据中心
    • 容忍数据中心失效:在多主节点模型中,每个数据中心则可以独立于其他数据中心继续运行,发生故障的数据中心在恢复之后更新到最新状态
    • 容忍网络问题
  • 离线客户端操作:应用在与网络断开后还需要继续工作
    • 每个设备都有一个充当主节点的本地数据库(用来接受写请求),
    • 在所有设备之间采用异步方式同步这些多主节点上的副本,同步滞后可能是几小时或者数天,具体时间取决于设备何时可以再次联网。

1.2.1 存在的问题

多主复制的最大问题是可能发生:写冲突

解决方案:处理冲突最理想的策略是避免发生冲突,即如果应用层可以保证对特定记录的写请求总是通过同一个主节点,这样就不会发生写冲突。

1.3 无主节点复制

单主节点和多主节点复制,都是基于这样一种核心思路,即客户端先向某个节点(主节点)发送写请求,然后数据库系统负责将写请求复制到其他副本。由主节点决定写操作的顺序, 从节点按照相同的顺序来应用主节点所发送的写日志。

2. 分布式分区

采用数据分区的主要目的是提高可扩展性。不同的分区可以放在一个无共享集群的不同节点上,这样一个大数据集可以分散在更多的磁盘上和服务器上。

  • 对单个分区进行查询时,每个节点对自己所在分区可以独立执行查询操作,因此添加更多的节点可以提高查询吞吐量。
  • 跨分区查询,通常是一些管理者视角的复杂查询场景,需要在多分分区上并行处理

2.1 复制和分区的区别

分区通常与复制结合使用,即每个分区在多个节点都存有副本,提高容错。

  • 一个节点上可能存储了多个分区。每个分区都有自己的主副本
  • 一个节点可能既是某些分区的主副本,同时又是其他分区的从副本。

2.2 分区策略和均衡

常见的分区策略有:

  • 基于时间戳分区
  • 基于关键词字hash分区
  • 更常见的分区策略是:一致性hash + 虚拟节点技术, 详情见 1.3 一致性hash