终于明白为什么要“分库分表”了!

2021-01-29    分类: 网站建设

随着互联网产业的蓬勃发展,在互联网应用上产生的数据也是与日俱增。产生大量的交易记录和行为记录,它们的存放和分析是我们需要面对的问题。

用 ID 取模的分表方式分配记录

ID 分别为 01-04 的四条记录,如果分配到 3 个表中,那么对 3 取模得到的余数分别是:

  • ID:01 对 3 取模余数为 1 ,存到“表 1”。
  • ID:02 对 3 取模余数为 2 ,存到“表 2”。
  • ID:03 对 3 取模余数为 3 ,存到“表 3”。
  • ID:04 对 3 取模余数为 1 ,存到“表 1”。

当然这里只是一个例子,实际情况需要对 ID 做 Hash 之后再计算。同时还可以针对不同表所在的不同的数据库的资源来设置存储数据的多少。针对每个表所在的库的资源设置权值。

用这种方式存放数据以后,在访问具体数据的时候需要通过一个 Mapping Table 获取对应要响应的数据来自哪个数据表。目前比较流行的数据库中间件已经帮助我们实现了这部分的功能。

也就是说不用大家自己去建立这个 Mapping Table,在做查询的时候中间件帮助你实现了 Mapping Table 的功能。所以,我们这里只需要了解其实现原理就可以了。

按照时间做的数据分表

需要注意的是由于分表造成一系列记录级别的问题,例如 Join 和 ID 生成,事务处理,同时存在这些表需要跨数据库的可能性:

  • Join:需要做两次查询,把两次查询的结果在应用层做合并。这种做法是最简单的,在应用层设计的时候需要考虑。
  • ID:可以使用 UUID,或者用一张表来存放生成的 Sequence,不过效率都不算高。UUID 实现起来比较方便,但是占用的


    Snowflake 示意图

    排序/分页:数据分配到水平的几个表中的时候,做排序和分页或者一些集合操作是不容易的。

    这里根据经验介绍两种方法。对分表的数据先进行排序/分页/聚合,再进行合并。对分表的数据先进行合并再做排序/分页/聚合。

    事务:存在分布式事务的可能,需要考虑补偿事务或者用 TCC(Try Confirm Cancel)协助完成,这部分的内容我们下面会为大家介绍。

    数据分库

    说完了分表,再来谈谈分库。每个物理数据库支持数据都是有限的,每一次的数据库请求都会产生一次数据库链接,当一个库无法支持更多访问的时候,我们会把原来的单个数据库分成多个,帮助分担压力。

    这里有几类分库的原则,可以根据具体场景进行选择:

    • 根据业务不同分库,这种情况都会把主营业务和其他功能分开。例如可以分为订单数据库,核算数据库,评论数据库。
    • 根据冷热数据进行分库,用数据访问频率来划分,例如:近一个月的交易数据属于高频数据,2-6 个月的交易数据属于中频数据,大于 6 个月的数据属于低频数据。
    • 根据访问数据的地域/时间范围进行分库。

    单个表会分到不同的数据库中

    通常数据分库之后,每一个数据库包含多个数据表,多个数据库会组成一个 Cluster/Group,提高了数据库的可用性,并且可以把读写做分离。

    Master 库主要负责写操作,Slave 库主要负责读操作。在应用访问数据库的时候会通过一个负载均衡代理,通过判断读写操作把请求路由到对应的数据库。

    如果是读操作,也会根据数据库设置的权重或者平均分配请求。另外,还有数据库健康监控机制,定时发送心跳检测数据库的健康状况。

    如果 Slave 出现问题,会启动熔断机制停止对其的访问;如果 Master 出现问题,通过选举机制选择新的 Master 代替。

    两个集群中的两个主从,分别扩展成四个集群中的四个

    从两个集群扩展成四个集群

    双写数据库扩容

    在没有数据库主从配置的情况下的扩容,假设有数据库 M1 M2 如下图:

    扩展前的两个主库

    需要对目前的两个数据库做扩容,扩容之后是 4 个库如下图。新增的库是 M3,M4 路由的方式分别是 ID%2=0 和 ID%2=1。

    新增两个主库

    这个时候新的数据会同时进入 M1 M2 M3 M4 四个库中,而老数据的使用依旧从 M1 M2 中获取。

    与此同时,后台服务对 M1 M3,M2 M4 做数据同步,建议先做全量同步再做数据校验。

    老库给新库做数据同步

    当完成数据同步之后,四个库的数据保持一致了,修改负载均衡代理的配置为 ID%4 的模式。此时扩容就完成了,从原来的 2 个数据库扩展成 4 个数据库。

    当然会存在部分的数据冗余,需要像上面一个方案一样通过后台服务删除这些冗余数据,删除的过程不会影响业务。

    数据同步以后做 Hash 切分

    分布式事务原理

    架构设计的分表分库带来的结果是我们不得不考虑分布式事务,今天创新互联来看看分布式事务需要记住哪两个原理。

    CAP

    互联网应用大多会使用分表分库的操作,这个时候业务代码很可能会同时访问两个不同的数据库,做不同的操作。同时这两个操作有可能放在同一个事务中处理。

    这里引出分布式系统的 CAP 理论,他包括以下三个属性:

    一致性(Consistency):分布式系统中的所有数据,同一时刻有同样的值。

    业务代码往数据库 01 这个节点写入记录 A,数据库 01 把 A 记录同步到数据库 02,业务代码再从数据库 02 中读出的记录也是 A。那么两个数据库存放的数据就是一致的。

    一致性简图

    可用性(Availability):分布式系统中一部分节点出现故障,分布式系统仍旧可以响应用户的请求。

    假设数据库 01 和 02 同时存放记录 A,由于数据库 01 挂掉了,业务代码不能从中获取数据。

    那么业务代码可以从数据库 02 中获取记录 A。也就是在节点出现问题的时候,还保证数据的可用性。

    可用性简图

    分区容错性(Partition tolerance):假设两个数据库节点分别在两个区,而两个区的通讯发生了问题。就不能达成数据一致,这就是分区的情况,我就需要从 C 和 A 之间做出选择。

    是选择可用性(A),获取其中一个区的数据。还是选择一致性(C),等待两个区的数据同步了再去获取数据。

    这种情况的前提是两个节点的通讯失败了,写入数据库 01 记录的时候,需要锁住数据库 02 记录不让其他的业务代码修改,直到数据库 01 记录完成修改。因此 C 和 A 在此刻是矛盾的。两者不能兼得。

    BASE

    Base 原理广泛应用在数据量大,高并发的互联网场景。一起来看看都包含哪些:

    基本可用(Basically Available): 不会因为某个节点出现问题就影响用户的请求。

    即使在流量激增的情况下,也会考虑通过限流降级的办法保证用户的请求是可用的。

    比如,电商系统在流量激增的时候,资源会向核心业务倾斜,其他的业务降级处理。

    软状态( Soft State):一条数据如果存在多个副本,允许副本之间同步的延迟,在较短时间内能够容忍不一致。这个正在同步并且还没有完成同步的状态称为软状态。
    最终一致性( Eventual Consistency):最终一致性是相对于强一致性来说的,强一致性是要保证所有的数据都是一致的,是实时同步。

    而最终一致性会容忍一小段时间数据的不一致,但过了这段时间以后数据会保证一致。其包含以下几种“一致性”:

    ①因果一致性(Causal Consistency)

    如果有两个进程 1 和 2 都对变量 X 进行操作,“进程 1” 写入变量 X,“进程 2”需要读取变量 X,然后用这个 X 来计算 X+2。

    这里“进程 1”和“进程 2” 的操作就存在因果关系。“进程 2” 的计算依赖于进程 1 写入的 X,如果没有 X 的值,“进程 2”无法计算。


    ②读己之所写(Read Your Writes)

    “进程 1”写入变量 X 之后,该进程可以获取自己写入的这个值。
    进程写入的值的同时获取值

    ③会话一致性(Session Consistency)

    如果一个会话中实现来读己之所写。一旦数据更新,客户端只要在同一个会话中就可以看到这个更新的值。

    ④单调写一致性(Monotonic Write Consistency)

    “进程 1”如果有三个操作分别是 1,2,3。“进程 2”有两个操作分别是 1,2。当进程请求系统时,系统会保证按照进程中操作的先后顺序来执行。

    分布式事务方案

    说完了分布式的原理,再来提一下分布式的方案。由于所处场景不一样,所以方案也各有不同,这里介绍两种比较流行的方案,两段式和 TCC(Try,Confirm,Cancel)。

    两阶段提交

    顾名思义,事务会进行两次提交。这里需要介绍两个概念,一个是事务协调者,也叫事物管理器。

    它是用来协调事务的,所有事务什么时候准备好了,什么时候可以提交了,都由它来协调和管理。

    另一个是参与者,也叫资源管理器。它主要是负责处理具体事务的,管理者需要处理的资源。例如:订票业务,扣款业务。

    第一阶段(准备阶段):事务协调者(事务管理器)给每个参与者(资源管理器)发送 Prepare 消息,发这个消息的目的是问“大家是不是都准备好了,我们马上就要执行事务了”。

    参与者会根据自身业务和资源情况进行检查,然后给出反馈。这个检查过程根据业务内容不同而不同。

    例如:订票业务,就要检查是否有剩余票。扣款业务就要检查,余额是否足够。一旦检查通过了才能返回就绪(Ready)信息。

    否则,事务将终止,并且等待下次询问。由于这些检查需要做一些操作,这些操作可能再之后回滚时用到,所以需要写 redo 和 undo 日志,当事务失败重试,或者事务失败回滚的时候使用。

    第二阶段(提交阶段):如果协调者收到了参与者失败或者超时的消息,会给参与者发送回滚(rollback)消息;否则,发送提交(commit)消息。两种情况处理如下:

    情况 1,当所有参与者均反馈 yes,提交事务:

    • 协调者向所有参与者发出正式提交事务的请求(即 commit 请求)。
    • 参与者执行 commit 请求,并释放整个事务期间占用的资源。
    • 各参与者向协调者反馈 ack(应答)完成的消息。
    • 协调者收到所有参与者反馈的 ack 消息后,即完成事务提交。

    情况 2,当有一个参与者反馈 no,回滚事务:

    • 协调者向所有参与者发出回滚请求(即 rollback 请求)。
    • 参与者使用第一阶段中的 undo 信息执行回滚操作,并释放整个事务期间占用的资源。
    • 各参与者向协调者反馈 ack 完成的消息。
    • 协调者收到所有参与者反馈的 ack 消息后,即完成事务。

    TCC(Try,Confirm,Cancel)

    对于一些要求高一致性的分布式事务,例如:支付系统,交易系统,我们会采用 TCC。

    它包括,Try 尝试,Confirm 确认,Cancel 取消。看下面一个例子能否帮助大家理解。

    假设我们有一个转账服务,需要把“A 银行”“A 账户”中的钱分别转到“B银行”“B 账户”和“C 银行”“C 账户”中去。

    假设这三个银行都有各自的转账服务,那么这次转账事务就形成了一次分布式事务。

    我们来看看用 TCC 的方式如何解决:
    首先是 Try 阶段,主要检测资源是否可用,例如检查账户余额是否足够,缓存,数据库,队列是否可用等等。

    并不执行具体的逻辑。如上图,这里从“A 账户”转出之前要检查,账户的总金额是否大于 100,并且记录转出金额和剩余金额。

    对于“B 账户”和“C 账户”来说需要知道账户原有总金额和转入的金额,从而可以计算转入后的金额。

    这里的交易数据库设计除了有金额字段,还要有转出金额或者转入金额的字段,在 Cancel 回滚的时候使用。


    如果 Try 阶段成功,那么就进入 Confirm 阶段,也就是执行具体的业务逻辑。

    这里从“A 账户”转出 100 元成功,剩余总金额=220-100=120,把这个剩余金额写入到总金额中保存,并且把交易的状态设置为“转账成功”。

    “B 账户”和“C 账户”分别设置总金额为 80=50+30 和 130=60+70,也把交易状态设置为“转账成功”。则整个事务完成。

    如果 Try 阶段没有成功,那么服务 A B C 都要做回滚的操作。对于“A账户”来说需要把扣除的 100 元加回,所以总金额 220=120+100。

    那么“B 服务”和“C 服务”需要把入账的金额从总金额里面减去,也就是 50=80-30 和 60=130-70。

    TCC 接口实现

    这里需要注意的是,需要针对每个服务去实现 Try,Confirm,Cancel 三个阶段的代码。

    例如上面所说的检查资源,执行业务,回滚业务等操作。目前有很多开源的架构例如:ByteTCC、TCC-transaction 可以借鉴。

    TCC 可靠性

    TCC 通过记录事务处理日志来保证可靠性。一旦 Try,Confirm,Cancel 操作的时候服务挂掉或者出现异常,TCC 会提供重试机制。另外如果服务存在异步的情况可以采用消息队列的方式通信保持事务一致。

    分库表中间件介绍

    如果觉得分表分库之后,需要考虑的问题很多,可以使用市面上的现成的中间件帮我们实现。

    这里介绍几个比较常用的中间件:

    • 基于代理方式的有 MySQL Proxy 和 Amoeba。
    • 基于 Hibernate 框架的有 Hibernate Shards。
    • 基于 JDBC 的有当当 Sharding-JDBC。
    • 基于 MyBatis 的类似 Maven 插件式的蘑菇街 TSharding。

    另外着重介绍 Sharding-JDBC 的架构,它的构成和“服务注册中心”很像。

    Sharding-JDBC 会提供一个 Sharding-Proxy 做代理,他会连接一个注册中心(registry center),一旦数据库的节点挂接到系统中,会在这个中心注册,同时也会监控数据库的健康状况做心跳检测。

    而 Sharding-Proxy 本身在业务代码(Business Code)请求数据库的时候可以协助做负载均衡和路由。

    同时 Sharding-Proxy 本身也可以支持被 MySQL Cli 和 MySQL Workbench 查看。

    实际上如果我们理解了分表分库的原理之后,实现并不难,很多大厂都提供了产品。


    总结

    因为数据量的上升,为了提高性能会对系统进行分表分库。从分表来说,有水平分表和垂直分表两种方式。

    可以根据业务,冷热数据等来进行分库,分库以后通过主从库来实现读写分离。

    如果对分库之后数据库做扩容,有两种方式,主从数据库扩容和双写数据库扩容。

    分表分库会带来分布式事务,我们需要掌握 CAP 和 BASE 原理,同时介绍了两阶段提交和 TCC 两个分布式事务方案。最后,介绍了流行的分表分库中间件,以及其实现原理。

当前标题:终于明白为什么要“分库分表”了!
网站路径:https://www.cdcxhl.com/news17/98067.html

成都网站建设公司_创新互联,为您提供企业建站网站内链ChatGPT关键词优化App开发虚拟主机

广告

声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

成都网站建设公司