作者简介
瑞华,携程高级后端开发工程师,关注系统架构、分库分表、微服务、高可用等。
一、前言
随着国际火车票业务的高速发展,订单量快速增长,单数据库瓶颈层面的问题逐渐显露,常规的数据库优化已无法达到期望的效果。同时,原先的底层数据库设计,也存在一些历史遗留问题,比如存在部分无用字段、表通过自增主键关联和各个应用直连数据库等问题。
为此,经过讨论后,我们决定对订单库进行分库分表,同时对订单表进行重构,进而从根本上解决这些问题。
二、问题挑战
目标确定后,实践起来可不轻松,出现了很多的问题和挑战。这里列举一些典型问题,大致可以分为两大类:分库分表通用问题、具体业务关联问题。
分库分表通用问题
如何切分,垂直分还是水平分?分片的键,如何选取?
如何根据键值路由到对应库、对应表?
采用什么中间件,代理方式还是中间件的方式?
跨库操作等问题,如跨库事务和跨库关联?
数据扩容问题,后续如何进行扩容?
具体业务关联问题
各个应用直连数据如何解决?
如何进行平滑过渡?
历史数据如何恰当迁移?
三、方案选型
3.1 如何切分
切分方式,一般分为垂直分库、垂直分表、水平分库和水平分表四种,如何选择,一般是根据自己的业务需求决定。
我们的目标是要从根本上解决数据量大、单机性能问题等问题,垂直方式并不能满足需求,所以我们选取了水平分库+水平分表的切分方式。
3.2 分片键选取
一般是根据自己的实际业务,来选择字段来作为分片的键,同时可以结合考虑数据的热点问题 、分布问题。比如订单系统,不能根据国家字段进行分片,否则可能会出现某些国家很多的订单记录,某些国家几乎没有订单记录,进而数据分布不均。相对正确的方式,比如订单类系统,可以选择订单ID;会员系统,可以选择会员ID。
3.3 如何路由
选定了分片的键之后,接下来需要探讨的问题,就是如何路由到具体的数据库和具体的表。以分片键路由到具体某一个数据库为例,常见的路由方式如下:
映射路由
映射路由,即新增一个库,新建一个路由映射表,存储分片键值和对应的库之间的映射关系。比如,键值为 1001,映射到 db01 这个数据库,如下图所示:
映射方式,优点是映射方式可任意调整,扩容简单,但是存在一个比较严重的不足,就是映射库中的映射表的数据量异常巨大。我们本来的目标是要实现分库分表的功能,可是现在,映射库映射表相当于回到了分库分表之前的状态。所以,我们在实践中,没有采取这种方式。
分组路由
分组路由,即对分片的键值,进行分组,每组对应到一个具体的数据库。比如,键值为 1000到2000,则存储到 db01 这个数据库,如下图所示:
分组方式,优点是扩容简单,实现简单,但是也存在一个比较严重的不足,是数据分布热点问题,比如在某一个时间内,分片键值为2001,则在将来一段时间内,所有的数据流量,全部打到某一个库(db02)。这个问题,在互联网环境下,也比较严重,比如在一些促销活动中,订单量会有一个明显的飙升,这时候各个数据库不能达到分摊流量的效果,只有一个库在接收流量,会回到分库分表之前的状态。所以,我们也没有采取这种方式。
哈希路由
哈希路由,即对分片的键值,进行哈希,然后根据哈希结果,对应到一个具体的数据库。比如,键值为 1000,对其取哈希的结果为 01,则存储到 db01 这个