从分表分库说起

Database and Ruby, Python, History


单库单表

业务开始阶段,一个数据库,加上几个业务表,就够用。代码也写在一起,一个service就搞定了。

单库分表

但某个表到达千万级别甚至上亿级别,日增长在2%左右,这个时候,可以预见这个表将来会变得异样庞大。这个时候需要分表。操作上可以用分区函数,把一个表分到多个分区文件上。也可以真实地拆为多个实体表,不过这就意味着业务代码需要做相应的改动,或者需要引入中间件来获取所有的数据。

分表以后,只解决了单表的压力,整个数据库的压力实际上还是没有解决。

分库

这个时候,我们需要分库,把表拆分到多个数据库中去。

垂直拆分

垂直分库

个人见过的方式,将业务相关的代码拆成一个单独的service,与之对应的表也迁移到新的数据库。

垂直分表

按列拆分,把频繁更新的保存到一个表,剩余的放在另外一个表。反第三范式。不过数仓反的还少么?不必教条。

水平拆分

和分表类型,通过函数将表的数据拆分到多个库中去,这些库里面的表结构都是一致的。可以通过Hash,按照日期,ID范围来拆分。

分库分表之后

跨库的join关联性查询很差。解决方案可能是通过数仓简历自身产品的数据集市Data Mart。

关系型数据库

ACID

关系型数据库的ACID模型,原子性,一致性,隔离性,持久性。 原子性,即所有提交要么全部提交,要么全部回滚,不存在部分提交的情况。整个事务不可再分割。 一致性,即操作前后系统都处于一致的状态,不会出现某条数据违背约定的情况。 隔离性,即多个事务互相隔离,一个事务不影响另一个事务。 持久性,即数据保存下来,即使服务器重启数据都依旧存在。

隔离级别

一般隔离级别有四种,在四种基础上,会有其他的衍生隔离级别。Read Uncommitted, Read Committed, Repeatable Read, Seriablize.

一致性

关系型数据库的一致性和分布式数据库的一致性意义不一样。分布式数据库的更加注重节点。

强一致性

强制一致性,即任何一次读取任一节点都可以读到最新的改动。

弱一致性

数据更新后,允许后续的访问能够访问到部分或者全部访问不到。

最终一致性

不保证任一时刻任一节点上的数据是一致的,但随着时间的迁移,不同节点上的数据总是趋向同一方向变化。

分布式数据库

CAP原理

  1. Consistency 一致性 要求所有节点的数据更新是一致的
  2. Availablity 可用性 好的响应性能
  3. Partition tolerance 分区容错性 可靠性,某个节点坏掉后依旧可以使用

你只能够牺牲其中一个来保证其他两个,不能够同时拥有CAP。

BASE理论

基于CAP的AP,要求基本可用,软状态,最终一致性。

柔性事务

在CAP和BASE理论下,出现的柔性事务,相对于关系型数据库的刚性事务。一般通过日志记录+正向/反向补偿,消息,或者无锁设计(版本控制)。

分布式事务的解决方案

两阶段提交

一个全局事务管理器调节多个节点的事务管理器。分为prepare和commit/rollback阶段。

但是有弊端,比如同步阻塞,需要等待所有节点都准备好;单点阻塞,如果一个节点不available,就造成所有的阻塞了;可能会导致部分节点上面的数据不一致。

TCC

Try-Confirm-Cancel,是对两阶段提交的补充方案。主要是引入超时来解决同步阻塞,以及补偿机制。

本地消息表

通过消息日志的方式来异步执行。消息日志可以存储到本地文本、数据库或消息队列,再通过业务规则自动或人工发起重试。

MQ事务

系统A往MQ、Kafka里面丢消息,系统B去消费消息。系统B接收到消息以后返回ACK。如果成功,系统B记录改动。否则,系统B向系统A发出回滚请求。

总结

能不分表分库就不要分表分库,像我遇到的产品最多也就是主从同步,不是个个都能够做成天猫,京东。

MQ事务不就是以前J2EE里面就有的东西么?多个Service之间异步通信,靠MQ就可以实现了。现在只是把MQ应用到多个数据库节点中去。

如何保证多个节点中的自增ID唯一呢?保留最后1、2位,结尾为01的就是node1生成的id,上限支持99个节点。结尾为00一般用来给数据修复用。

此外,还有最近几年新起的NewSQL,能够既提高性能,又支持关系模型。What’s really new in NewSQL?

Reference

  1. https://xiaomi-info.github.io/2020/01/02/distributed-transaction/
  2. https://zhuanlan.zhihu.com/p/99396275