一架梯子,一头程序猿,仰望星空!
MySQL性能优化面试题 > 内容正文

分库分表会带来哪些问题?如何解决?


问题简答

虽然分库分表可以提高数据库的扩展性和性能,但是带来下面问题:
1、SQL变复杂,需要解决垮库查询、范围查询、没有分片健如何查询等问题
2、数据一致性问题,涉及多个数据库的分布式事务问题
3、开发和运维成本都比单库高出很多

问题详解:

1.SQL变复杂

1.1.跨库查询问题

在分库分表的环境下,原本简单的SQL可能需要拆分成多个SQL,并且在应用层进行数据组装,这会增加开发难度和维护成本。

解决方案:

  1. 在业务代码层面把查询需求,拆分成多个SQL,分别发送到不同的数据库执行查询,然后再业务代码层面合并数据。这种方法简单直接,但需要考虑到网络传输的开销,可能会影响查询性能。
  2. 借助中间件,例如:ShardingSphere、MyCat,在中间件完成跨库查询,中间件完成跨库查询原理跟方案1差不多,区别是不用开发写代码,中间件会自动分析join语句,把SQL发送到不同的数据库执行查询。

1.2.范围查询问题

根据不同的数据分片规则,对范围查询的影响不一样。

例子:
订单表,根据订单id使用hash分片

查询场景1:
根据订单id可以快速定位订单数据存储在那个数据库,查询很快,效率没问题。

查询场景2:
如果要查询 订单id > 1 and 订单id < 1000的数据,根据hash分片规则,可能要查询N个数据库才能凑齐数据。

解决方案:

  • 本质上也是跨库查询问题,参考跨库查询解决方案。

1.3.SQL条件没有分片字段(分片健)如何查询?

还是前面订单表的例子,根据订单id使用hash分片, 如果要查询user_id=100的订单数据,因为订单是根据订单id均匀分布在N个数据库,你怎么知道user_id=100的订单保存在那个数据库?

解决方案:

  1. 借助全局表,例如: 订单表虽然存储在十几个数据库,但是elasticsearch保存了一份全量的订单数据,可以到elasticsearch查询任意订单数据,无需分片字段,es相当于一个订单数据的全局表,借助全局表是主流的解决方案。
  2. 借助属性映射,例如:用户表根据id分片,如果想通过username查询账号,是无法直接查询的,那么可以存储一份username -> id的映射关系,先通过username查询到id,然后根据id查询,适合少量属性映射,映射字段多了成本也高。
  3. 借助分库分表中间件,一般来说中间件如果发现sql语句的查询条件,没有携带分片字段,会把sql广播到所有的数据库去查询数据,虽然能查到数据,但是效率很低,一般业务是不允许的。

1.4.读写分离架构下,从库数据同步延时怎么办?

在分库分表的情况下,如果每个物理数据库还做了主从读写分离架构,由于数据从主库同步到从库是需要时间的,即使同步速度再快,那也是有延时的,如果查询从库,数据没有同步好,查不到数据怎么办?

解决方案:

  1. 对于数据实时性要求较高的场景,强制读主库,避开读从库。

2.数据一致性问题

在分库分表的环境下,数据一致性问题是一个很有挑战的问题,主要是由于数据被拆分到不同的数据库和表中,而这些数据库和表又可能分布在不同的物理服务器上,传统单机数据库事务无法使用,你怎么确保事务完整性?

例如: 订单支付的场景,用户A的订单数据在数据库DB1,用户A账号数据在数据库DB2,怎么确保扣款和更新订单状态的事务一致性?

解决方案:

  • 借助分布式事务解决,常见的分布式事务方案有XA、TCC、SAGA等,不同的开发语言分布式事务框架有所区别。
  • 基于消息队列实现最终一致性。
  • 借助分库分表中间件,有些中间件支持分布式事务,开发成本最低。