数据库设计 - 扩展性

一、What

数据库模块可变更的能力叫做数据库扩展性。比如以下三个场景:

  1. 底层的表结构变更:比如新增两个字段。因为线上数据量一般都是比较大的,如果直接ALTER TABLE会锁表,线上服务会不可用。
  2. 水平分库的个数变化:比如两个水平分库变更为三个水平分库
  3. 底层存储介质变换:比如mongodb修改为mysql

二、Why

数据库扩展性是指随着产品的迭代,数据库可能也需要迭代变更。

三、How

方案一:离线迁移数据,数据库升级期间服务不可用。适用场景1、2、3。

  1. 发通告要升级服务
  2. 停止服务
  3. 数据变更
  4. 恢复服务

方案一劣势:
a. 只能支持可以支持长时间不可用的应用,比如游戏停服
b. 因为通告一般都会限定维护的时间段,对于开发,测试,运维有一定的压力,容易出错。如果万一出错,就需要回滚。

方案二:在线表结构变更,迁移期间最多有秒级的不可用。适用场景1。
pt-online-schema-change 或者叫online DDL,用于Mysql表结构变更;步骤如下:

  1. 新建一个新的临时表
  2. 旧表添加INSERT/UPDATE/DELETE触发器,数据修改同步到新表
  3. 把旧表数据迁移到新表
  4. 删除触发器,并把旧表迁移到别的地方
  5. 重命名新表为旧表名

Tips:

  1. 处理冲突,以触发器的最新数据为准,迁移数据一定要有主键
  2. 要新建触发器,所以要求原有表不要有很多触发器,否则影响性能,不过目前大部分互联网公司都不用触发器
  3. 要在低峰期迁移,因为触发器会影响性能

具体使用参考https://www.cnblogs.com/lkj371/p/11430265.html

方案三:追日志,适用场景2,3。
追日志方案,是一个高可用的平滑迁移方案。总共需要五大步骤:
数据库设计 - 扩展性

  1. 升级服务,添加日志写入:把所有修改数据的主键(增删改)记录下来,比如记录库+表+主键三个信息
  2. 数据迁移:旧库存量的数据同步到新库,比如旧库dump出来一个sql文件,然后在新库source
  3. 读取日志,插入到新的库:旧库一直对线上提供服务,数据是在变化的,一次静态的迁移肯定是不够的。需要开发一个data-replay-tool这样的小工具,来读取第一步的日志,从旧库读取最新的数据,同步到新库
  4. 检查一致性:需要开发data-check-tool,执行重放日志,我们还不能完全认为数据是一致的,需要做一致性检查。
  5. 流量迁移:完成上面四个步骤,我们便认为新库是可用的,可以用修改配置文件的方式,或者配置中心回调的方式来通知服务层有新的下游节点可以消费了

方案四:双写模式
双写模式,是另外一种高可用的平滑迁移方案。总共需要四个步骤:

  1. 升级服务,让服务同事操作新旧两库:
    这里并不是把流量迁移到新库,新库执行数据库操作,affect rows是多少没有关系,不会影响服务,真正服务的还是旧库。

  2. 数据迁移:
    data-migrate-tool,和追日志模式一样,同样需要进行数据迁移,但是不需要replay log。为什么不需要,因为提前进行了双写,所以数据是一致的,具体分析如下:
    a. 在迁移中旧库中的数据依然在修改,这些修改新库和旧库会进行双写
    b. insert操作,新旧都没有,那就是都insert成功
    c. delete操作,如果是已经迁移过去的数据,那么大家都是delete,如果还没迁移,等到需要迁移的时候在旧库中它已经不存在了
    d. update操作可以看做是delete和insert叠加

极端情况:在数据迁移的过程中,正在迁移的段发生了删除或修改,那么就会出现数据不一致,所以还是需要数据一致性的检查。

  1. 一致性检查
    以旧库为准,对新库进行检查,这一过程旧库依旧对线上提供服务,不影响。

  2. 流量迁移
    完成上面三个步骤,我们便认为新库是可用的,可以用修改配置文件的方式,或者配置中心回调的方式来通知服务层有新的下游节点可以消费了。