分库分表
什么是分库分表?
分库:分库是将一个大型数据库分成多个数据库实例的过程。每个数据库实例通常被称为一个"分片"或"数据库分区"。分库的目的是将数据均匀分布在多个数据库中,以降低单个数据库的负载。
分表:分表是将一个数据库表分成多个子表的过程。每个子表通常包含表中的一部分数据。这种方式可以减小单个表的数据量,提高查询性能。
水平切分:水平切分是将数据按照行的方式分布在多个数据库或表中。每个分片存储数据的一部分。这通常用于处理大量数据。
垂直切分:垂直切分是将数据按照列的方式分布在多个数据库或表中。每个分片存储表中的一部分列。这通常用于将数据分为逻辑上相关的部分。
分片键:分片键是用于确定数据存储位置的关键字段或属性。它通常是在数据分片时选择的,以确保数据均匀分布。
为什么要分库分表?
这个答案很简单:数据库性能出现瓶颈
对外表现有几个方面:
- 大量请求阻塞:在高并发场景下,大量请求都需要操作数据库,导致连接数不够了,请求处于阻塞状态;
- SQL操作变慢:如果一张表存在上亿条数据,一条SQL语句如果未命中索引会导致查询耗时非常久;
- 存储出现问题:业务量剧增,单库数据量越来越大,给存储造成巨大压力。
数据库优化方案
数据库优化方案包括软件层面和硬件层面
软件层面:SQL调优、表结构优化、读写分离、数据库集群、分库分表等
硬件层面:增加机器性能
SQL 调优
SQL调优的目的就是尽可能让SQL命中索引,让慢SQL变快。
SQL调优一般有以下几个步骤:
- 首先开启慢SQL记录,明确知道慢SQL是哪个;
- 使用explain解析SQL,查看SQL的执行计划;
返回有一列叫“type”,常见取值有:
ALL、index、range、 ref、eq_ref、const、system、NULL(从左到右,性能从差到好)
ALL 代表这条 SQL 语句全表扫描了,需要优化。一般来说需要达到range 级别及以上。
表结构优化
根据业务场景进行表结构设计,如果涉及到连表查询,可以将部分字段作为冗余字段加入到业务表中,可以减少连表查询;
但是冗余字段存在一个弊端,该冗余字段会涉及到多个表的更新,因此选择冗余字段的时候需要选择不常更新的字段。
架构优化
- 增加主从集群,主实例负责写,从实例负责读;
- 增加Redis,让请求优先从redis中获取数据,减少数据库压力;
分库
分库步骤:
- 单应用单数据库
- 多应用单数据库
- 多应用多数据库(根据业务将数据库进行拆分)
分表
如果系统处于高速发展阶段,拿商城系统来说,一天下单量可能几十万,那数据库中的订单表增长就特别快,增长到一定阶段数据库查询效率就会出现明显下降。
因此,当单表数据增量过快,业界流传是超过500万的数据量就要考虑分表了。当然500万只是一个经验值,大家可以根据实际情况做出决策。
那如何分表呢?
分表有几个维度,一是水平切分和垂直切分,二是单库内分表和多库内分表。
水平切分:表结构相同,数据不同
- 根据业务id进行水平拆分
- 根据日期进行拆分,例如每日表/每月表/历史表
垂直拆分:表结构不同
- 基于字段进行拆分,将部分不重要的信息作为额外信息表进行存储
分库分表实践
场景:目前有一张表存在上亿条数据,打算对该表进行分表,打算分为20个表,此时应该怎么做?横向分表
- 确定切分纬度(水平切分/垂直切分),根据业务id或其他字段进行表切分
- 数据备份,确保拆分过程数据不会丢失
- 使用分布式ID作为分表后的ID,并且与原表进行id对应
- 采用双写方式,确保老表与新分表的数据一致
- 数据对账,新表与老表的数据进行核对
- 查询路由,业务切流
分布分表带来的复杂性
既然分库分表这么好,那我们是不是在项目初期就应该采用这种方案呢?不要激动,冷静一下,分库分表的确解决了很多问题,但是也给系统带来了很多复杂性,下面简要说一说。
(1)跨库关联查询
在单库未拆分表之前,我们可以很方便使用 join 操作关联多张表查询数据,但是经过分库分表后两张表可能都不在一个数据库中,如何使用 join 呢?
有几种方案可以解决:
-
字段冗余:把需要关联的字段放入主表中,避免 join 操作;
-
数据抽象:通过ETL等将数据汇合聚集,生成新的表;
-
全局表:比如一些基础表可以在每个数据库中都放一份;
-
应用层组装:将基础数据查出来,通过应用程序计算组装;
(2)分布式事务
单数据库可以用本地事务搞定,使用多数据库就只能通过分布式事务解决了。
常用解决方案有:基于可靠消息(MQ)的解决方案、两阶段事务提交、柔性事务等。
(3)排序、分页、函数计算问题
在使用 SQL 时 order by, limit 等关键字需要特殊处理,一般来说采用分片的思路:
先在每个分片上执行相应的函数,然后将各个分片的结果集进行汇总和再次计算,最终得到结果。
(4)分布式 ID
如果使用 Mysql 数据库在单库单表可以使用 id 自增作为主键,分库分表了之后就不行了,会出现id 重复。
常用的分布式 ID 解决方案有:
-
UUID
-
基于数据库自增单独维护一张 ID表
-
号段模式
-
Redis 缓存
-
雪花算法(Snowflake)
-
百度uid-generator
-
美团Leaf
-
滴滴Tinyid
这些方案后面会写文章专门介绍,这里不再展开。
(5)多数据源
分库分表之后可能会面临从多个数据库或多个子表中获取数据,一般的解决思路有:客户端适配和代理层适配。
业界常用的中间件有:
-
shardingsphere(前身 sharding-jdbc)
-
Mycat
参考: 我们为什么要分库分表