分布式架构

分布式架构

一致性hash

用于构建分布式缓存、负载均衡和分布式存储系统的**算法。它的原理是在哈希环上均匀地分布数据节点,使得数据可以快速定位**到对应的节点。

原理:

  • hash环:虚拟的环状空间,使用hash函数将数据和节点映射到固定的hash值,然后将hash值顺时针排列的环上

  • 节点映射:将数据通过相同的hash函数计算得到一个哈希值,并在环上找到离这个哈希值最近的节点位置。从这个节点开始,按顺时针方向寻找下一个节点,直到找到一个存储数据的节点。

  • 节点变动处理:当有新的节点加入或节点离开系统时,只需重新计算受影响的数据在环上的位置,然后将其映射到新的节点。通过这种方式,只有少量的数据需要重新映射,而大部分数据仍然可以定位到原来的节点上,从而保持了系统的稳定性。

动态变化时,能够最小化数据的重新分布和迁移,减少系统的负载和数据的丢失。同时,一致性哈希能够提供较好的负载均衡,因为节点在环上均匀分布,数据在环上的位置也会相对均匀地分布,避免了热点数据集中在某些节点上的问题。

缺陷:数据(请求)倾斜问题:

  • 节点分布不均时,数据会存在倾斜。引入了虚拟节点机制,即对每⼀个服务节点计算多个哈希,每个计算结果位置都放置⼀个此服务节点。

应用:

  • nginx固定路由

分布式下mysql架构

主从复制 MySQL Replication

  • 原理:主从复制中,从库利用主库上的 binlog 进行**重播**,实现主从同步

  • 流程:

    1. 主库收到更新命令,执行更新操作,生成 binlog;

    2. 主库 dump_thread 从本地读取 binlog 传送刚给从库;

    3. 从库从主库获取到 binlog 后存储到本地,成为 relay log(中继日志);

    4. 从库sql_thread 线程读取 relay log 解析、执行命令更新数据。

  • 主从同步模式

    • 异步复制:默认异步,master执行完事务立即返回结果给客户端,不关心slave是否接收处理binlog。若同步binlog前master宕机将导致数据不完整;

    • 同步复制:与异步复制相反,master等待所有slave都同步成功,才返回结果给客户端。但是数据库的性能必然受到影响;

    • 半同步复制:master生成 binlog 后开始等待slave的ACK,直到至少一个slave写入 Relay Log 并将数据落盘返回给主库 ACK,然后master再进行事务commit,就是说事务commit时在至少一个slave同步成功后进行的

  • 解决主从延迟问题

    • 强行读主库

    • semi-sync 半同步复制

      • AFTER_COMMIT策略:

        • MASTER将事务写入到二进制日志并刷盘保存,同时将事务发送给SLAVE,然后将事务提交给**存储引擎**处理并进行提交,然后等待SLAVE返回确认信息,在收到确认信息后,MASTER将结果返回给客户端,然后当前客户端可以继续工作。

        • 人话:master处理事务的同时发送事务到slave,然后master提交事务,接着等待slave确认,最后将结果返回给客户端

        • 这个策略过时了,会出现master宕机后slave数据不全的情况

      • AFTER_SYNC策略 : MASTER将事务写入到二进制日志并刷盘保存,同时将事务发送给SLAVE,然后等待SLAVE返回确认信息,收到确认信息后,将事务提交给存储引擎处理并进行提交,并将结果返回给客户端,然后当前客户端可以继续工作。

      • 人话:master处理事务的同时发送事务到slave,然后等待slave确认,收到slave确认后master提交事务,最后将结果返回给客户端

组复制 MySQL Group Replication(MGR)

  • 原理:由若干个节点(每个节点都是master)共同组成一个复制组,一个事务的提交,必须经过组内大多数节点 (N / 2 + 1) 决议并通过,才能得以提交。发起一个更新事务时事务先在本地执行,执行完成之后在还没有真正提交之前,需要将产生的复制写集**广播**出去,复制到其它成员。

  • 特性:

    • 全都是master节点,不会存在脑裂

    • 数据一致性保障:因为是多节点投票机制,某个节点宕机时的数据集只会等于或小于其他节点,在重新加入组群是数据会被同步。

    • 多节点写入支持:多写模式下支持集群中的所有节点都可以写入

  • 场景:

    1、弹性复制:需要非常灵活的复制基础设施的环境,其中MySQL Server的数量必须**动态增加或减少**,并且在增加或减少Server的过程中,对业务的副作用尽可能少。例如,云数据库服务;

    2、高可用分片:分片是实现写扩展的一种流行方法。基于 组复制 实现的高可用分片,其中每个分片都会映射到一个复制组上(逻辑上需要一一对应,但在物理上,一个复制组可以承载多个分片);

    3、替代主从复制:在某些情况下,使用一个主库会造成单点争用。在某些情况下,向整个组内的多个成员同时写入数据,对应用来说可能伸缩性更强;

    4、自治系统:可以利用组复制内置的自动故障转移、数据在不同组成员之间的原子广播和最终数据一致性的特性来实现一些运维自动化。

分库分表解决方案

  • 垂直切分:字段拆分(大表拆成小表),不同业务使用独立的表。无限增长数据问题无法解决,但是能一定程度提高业务性能

  • 水平切分:数据量切分、冷热数据切分

    • 根据数值范围切分,自增id、创建时间等数字字段

    • hash取模切分

    • 一致性hash切分

  • 现有中间件

    • sharding-jdbc(当当)

    • Vitess(谷歌)

    • MyCAT(基于Cobar)

    • TSharding(蘑菇街)

  • 水平分库分表下的分页

    • 分表加上 ES 搜索做分页

    • 通过合表读写分离的方案

    • sharding-jdbc方案:

      • 第一种:limit m,n 每个表都查询m+n条数据,内存合并所有数据数据进行一次内存排序分页

      • 在第一种的基础上有下一页的场景,把**当前页的排序字段最大值拿出来,作为偏移量**对所有分片进行下一页查询,然后内存合并排序分页

      • 二次查询:limit offset, pSize、子表数量m

        • 所有子表查询 limit offset/m , pSize,合并所有子表的查询结果得到S1,S1的最大值就是limit offset, pSize*m 的最大值,由于子表数据随机分布S1数据是不全的;

        • 由于子表都是查询pSize个数据,所有并集的最大值肯定包含了总表pSize的最大值,所以缺的是**各子表中offset/m前面的几个数据**;

        • 第一步子表查询都是有序的,所以S1中最小值gMin在总表中的位置肯定在总表offset的位置前面,即gMin的数据比offset的数据小,也就是说**各子表中offset/m前面的几个数据肯定都大于gMin,所以各子表把大于gMin但小于第一步结果各自的最大值**的数据查出来,合并得到并集S2

        • 由于S2是在第一步S1的查询基础上拓宽了左边界,所以S2比S1多的数据量就是offset与gMin的下标差,gMin的偏移量是offset-(S2-S1)

        • 于是limit offset,pSize等于 S2 limit (S2-S1),pSize

  • 垂直分库下的JOIN

    • 全局表:每个数据库中均保存一份,数据字典表

    • 字段冗余:空间换时间,这类数据通常也**很少发生修改**,分表进行字段冗余

    • 系统层组装

2、业务重构后数据迁移方案

数据一致性方案

本地消息表+MQ

生产者通过**消息发送记录以及定时重试任务确保消息通过MQ发送成功,消费者幂等消费MQ消息**

  1. 生产者在处理本地事务时,同时向**消息表新增一条消息记录,状态为发送中,通过定时任务将消息记录**发送到MQ

  2. 消费者**幂等消费MQ消息**(可通过消息id+消费结果入库实现幂等),并将消费结果发送到MQ

  3. 生产者接收消费结果消息,更新本地消息记录状态

缺点:

1)本地消息表与业务耦合,难于做成通用性,不可独立伸缩。

2)本地消息表是基于数据库来做的,而数据库是要读写磁盘IO的,因此在高并发下是有性能瓶颈的

MQ补偿回滚方案

在实时同步、强一致性的场景中,系统中某个业务往往需要**顺序调用**多个服务并在调用结束得到调用结果,若中间一个服务失败,则整个执行流程都需要回滚。

1、某个业务操作构建一个软主体,比如订单则生成一个不可见订单,商品生成一个不可见商品,主要有生成一个基础的业务主体

2、根据软主体进行一系列的顺序服务调用,若中间一个服务失败,则向MQ广播发送**主体作废**的消息通知,各个服务接收消息根据实际情况进行回滚

本地消息表相比,该方案实时性比较高。本地消息表每次服务交互都需要通过消息,当前方案交互时是**实时服务调用**,仅在失败是通过MQ进行回滚补偿