数据库(六) 分库分表
1、分库分表作用
- 分库分表主要是为了解决由于数据量过大而导致数据库性能降低的问题。将原来独立的数据库拆分成若干个数据库,将原来的数据大表拆分成若干个数据表,使得单个数据库、数据表的数据量变小,从而提升数据库性能。
- 微服务架构中,每个服务都分配一个独立的数据库,这就是分库。而对一些业务日志表,按月拆分成不同的表,这就是分表。
- 一般单表数据量超过500W或者2G的情况下,读写性能变得很差,而常规的优化手段已经不起作用,比如:SQL调优、添加索引、主从复制、读写分离,这时候就需要进行分库分表。
如何判断项目需要分库还是分表?
- 当数据库QPS过高,数据库连接数不足的时候,就需要分库。
- 当单表数据量过大,严重影响读写性能,就需要分表。
- 当上述两种情况都存在时,就需要分库分表了。
先分库还是先分表?
- 建议先分表,如果分表能解决问题,就不需要分库了,毕竟需要单独服务器资源,成本高
2、分库分表拆分方案
分库分表有垂直拆分和水平拆分。垂直拆分又有垂直分库、垂直分表。
垂直分库:不同的业务拆分到不同的数据库
垂直分表:把长度较大或者访问频次较低的字段,拆分到扩展表中
水平分表:单表数据量过大时,按照订单ID拆分到多张表中
3、分库分表缺点
分库分表带来了低耦合、高性能的优点,缺点也有一大堆
垂直分库:
不同数据库多表之间无法join关联查询,只能通过接口聚合,复杂度直线上升;
横跨多个数据库导致无法使用本地事务,更别说数据强一致性了,只能引入更为复杂的分布式事务,勉强实现数据的最终一致性,可用性直线下降。
垂直分表:
本来一张表能查出来的数据,现在需要多张表join关联查询,效率降低
水平分表:
多张表关联查询时,无法实现limit分页、order by排序功能
4、分库分表带来很多问题,相应的解决方案
跨库查询问题:
采用字段冗余方案,需要要求冗余字段要很少变动,就算变动后,也能容忍返回旧数据
多表分页查询问题:
这个处理起来就很需要技术含量了。
想要实现用户订单分页查询,可以采用按照用户ID分片,(user_id % 128),这样同一个用户的订单只会存储在一张表中,咋分页展示都行。
商户想要分页查看自己店铺的订单怎么办?那就把订单再冗余存储一份,按照店铺ID分片,(shop_id % 128)。不过由于商户数量较少,可以搞个异步线程往商户订单分片表同步。
订单按照用户ID分片后,发生数据倾斜怎么办?
因为不同用户的订单量是不同的,一个爱好购物的小姐姐的订单量抵得上几十个老爷们。导致一张表数据几百条,另一张表数据量千万级,这该咋整?
做冷热数据分离,基础库只存储3个月内的订单,其他的移动到历史订单库。这个要跟产品商量好,3个月前的订单需要单独的查询页面。
跨库事务问题:
只能使用分布式事务,分布式事务的实现非常复杂,常用的几种解决方案:
二阶段提交
TCC
本地消息表
MQ事务消息
分布式事务中间件
订单表分片后,肯定不能使用数据库自增主键做订单ID,因为无法实现全局唯一,如何解决?
使用雪花算法
5、分库分表中间件
编码层:
if else判断,spring AbstractRoutingDataSource动态切换数据源
优点:快速实现分库
缺点:需要编写大量代码,项目迭代,难以维护
框架层:
修改ORM框架代码,增加SQL自定义原语或hint,适合有统一的ORM框架,Mybatis的Interceptor接口
优点:效果很好
缺点:需要对框架源码很了解,并修改框架代码
驱动层:
重新编写一个JDBC的驱动,在内存中维护一个路由列表,然后将请求转发到真正的数据库连接,像TDDL、ShardingJDBC
优点:适合开发语言固定,多种数据库类型,集中式管理,运维负担小
缺点:占用较多的数据库连接
代理层:
代理层的数据库中间件,将自己伪装成一个数据库,接受业务端的链接,然后负载业务端的请求,解析或者转发到真正的数据库中,像MySQL Router、MyCat
优点:适合一种关系型数据库,多种开发语言
缺点:需要维护数据库连接数量有限,需要考虑高可用,运维负担大
实现层:
SQL特殊版本支持,如Mysql cluster本身就支持各种特性