当单机数据库压力变大,常见的有两种处理方式,一是读写分离,类似于集群,一是分库,分表,即分布式。

读写分离的问题和对策

  • 数据复制问题
  • 应用对于数据源选择问题

主从数据同步,必然会在某一刻,数据的不一致,如果业务允许短时间的不一致,那只要保证最终一致即可。当然也可以采取强制读主库的方式。

对于读写多数据源,有两种形式,一种是通过 proxy 的形式,将 应用层 sql 发送给 proxy 机器,有 proxy 来判断给主库还是从库执行。另一种是在应用内配置两种数据源,根据 sql 的类型(读/写)来调用对应的数据库服务器。

单机到分布式的问题和对策

应用服务化后,每个微服务一个对应一个数据库,能很好的减轻数据库的压力,这也是垂直拆分,将较为内聚的业务拆到一个库中。

垂直拆分的问题:

  • 单机的 acid 无法保证
  • join 操作无法执行

对于分布式事务,强一致性会极大影响分布式系统的性能,因此提出 cap(一致性,可用性,分区容忍性) 理论,一般牺牲一致性来换取系统的可用性和分区容错性。不过牺牲一致性并不是完全放弃数据一致性,而是牺牲强一致性换取弱一致性,即 base 理论,Basic Available,Soft State,Eventual Consisstency。

分布式 join 的问题,一是将原有的 sql,分割为两条,在服务之间调用,二是分库建表时,冗余一些必要字段。

当然,除了垂直方向的分库,还有水平方向的分表,比如订单表,遇到活动,订单数激增,表迅速增大,此时就通过增加一张字段一样的表,来分摊单表的压力。

水平拆分新增的问题:

  • 主键问题
  • SQL 路由问题
  • 分页查询问题

主键问题:有两个要素,唯一性和连续性,这里推荐 Twitter 的 Snowflake 算法,该算法生成的是 64 位唯一 Id(由 41 位的 timestamp+ 10 位自定义的机器码 + 13 位累加计数器组成)

SQL 路由问题:数据分片的时候按照指定的 key,路由存储到不到的表中。

分页查询问题:

  • 不需要排序,可按等步长或等比例从多张表中取数据
  • 需要排序,每张表都需获取足够查询页数的数据,在应用内合并排序,当页数过大时,负担越重,尽量避免。

数据库中间件的设计

  1. SQL 解析
  • 对官方 SQL 的支持范围
  1. 规则处理
  • 根据某个字段按照一定规则确定一组库,比如 id % 2 = 0 为 group db 1,id % 2 = 1 为 group db 2.
  1. SQL 改写
  • 比如改写表名。水平拆分的表在同一个库中,表名不同,就需要在执行 SQL 时动态更换表名
  1. 数据源选择
  • 确定一组数据源中的具体的某一个数据源
  1. SQL 执行
  2. 结果集返回,合并处理

开源的数据库中间件

MyCAT
OneProxy
Atlas