一. 大纲
在谈论数据库架构和数据库优化的时候,我们经常会听到分库分表
、分区
、分片(Sharding)
等关键词,对于这些概念,相信大家应该都有听说过或是浏览过相关的文档,我们今天就来详细了解下这几个比较容易混淆的概念。
首先呢,我们需要知道上述的这些方案都是为了同一个目的
而产生,那就是为了突破单表/库过大
或单节点数据库服务器
的性能瓶颈,解决数据库的扩展性问题。
因为随着表数据量的增长,单机数据库的资源和处理能力有限,所以数据库非常容易成为应用系统的瓶颈。
二. 分库
垂直分库
在微服务
盛行的今天已经非常普及了,基本的思路就是按照业务模块
来划分出不同的数据库,而不是像早期一样将所有的数据表都放到同一个数据库中。
系统层面的服务化
拆分能够解决业务系统层面的耦合
与性能瓶颈
,有利于系统的扩展维护。
而数据库层面的数据拆分,道理其实也是相通的,可以更加便于数据库的管理、维护、监控、扩展
等。
垂直分库
可以在一定程度上解决数据库单机瓶颈,因为可以将不同的业务库分散在不同的服务器上,独享资源,互不干扰,但是一定要了解清楚拆分的原则与技巧,否则会遇到很多问题,例如:跨库JOIN
、分布式事务
等。
三. 分表
分表
就是将一张大表
拆分成N个子表
,根据切分规则,通常由如下两种模式:
-
垂直拆分:基于
列
拆分表,将表中的某些字段拆分出去放入到扩展表
中。 -
水平拆分:基于
行
拆分表,通过规则匹配分类后,将数据放到若干个子表
中。
3.1. 垂直分表
垂直分表
在日常开发和设计中比较常见,通俗的说法叫做大表拆小表
,拆分是基于关系型数据库中的列(字段)
进行的。
通常情况下,当某个表中的字段比较多时,可以新建一张扩展表
,将不经常使用或者长度较大的字段拆分出去。
垂直分表的作用比较有限,它只可以减少行数据大小,加快数据检索的速度,但是无法解决因为表数据量增长而导致的压力。
表字段拆分(垂直分表)
建议在数据库设计阶段就做好,如果是在发展过程中拆分,则需要改写以前的查询语句,会额外带来一定的成本和风险。
3.2. 水平分表
水平分表
也称为横向分表
,就是将表中不同的数据行按照一定规律分布到不同的数据库表中(这些表保存在同一个数据库中
),以此来降低单表数据量,优化查询性能。
这种同库下水平分表通常有以下几张方式:
-
冷热数据分离:
-
定期将使用较少的历史数据迁移到对应的历史表中,减少原表的数据量。
-
-
业务分表:
-
在代码层面实现分表,数据库中存在多张子表,但在应用上层表现为一张表。
-
-
分区表:
-
通过数据库自带的分区表功能实现分表,逻辑上表现为一张表,但是底层存储在不同的分区上。
-
水平分表
能够降低单表的数据量(分区表必须要带上分区条件才可以提前过滤
),一定程度上缓解查询性能瓶颈。但本质上这些表还是保存在同一个库,或者说同一个数据库实例、同一个DB服务器中,所以无法避免单机数据库性能瓶颈的问题。
四. 分片
分片
也叫做Sharding
,或是水平分库分表
,都是相同的概念,与上面所讲到的水平分表的概念类似,唯一不同点
就在于将这些拆分出来的子表保存在不同的数据库服务器
中。
常见分片策略
:
-
RANGE:按照范围划分,比如按照日期或者自增键进行范围划分。
-
HASH: 采用 hash+mod 的组合划分数据。
分片实现方式
:
-
客户端:
-
在客户端实现分片,如比较流行的
分库分表框架Sharding-JDBC
,就是在驱动层
实现了分片功能,对上层应用保持透明。
-
-
中间层:
-
通过在应用与DB之间接入
中间件(Proxy)
,如DBLE、MaxScale等开源中间件,用于实现后端数据分片与路由查询,对上层应用保持透明。
-
-
服务端:
-
使用某些
分布式数据库
,如TiDB、SequoiaDB等开源NewSQL,其在存储引擎层
实现了数据分片功能,对上层应用保持透明。
-
分片能够有效的缓解单机、单库、单表的性能瓶颈,突破IO、连接数、硬件资源等瓶颈。但是我们一旦将数据分片后,那么整个数据库架构就转换成了分布式架构
,也会引发诸多问题:
-
分布式事务
:-
数据分片后,原先的
本地事务
就演变成了分布式事务
了,一般需要通过二阶段或三阶段提交
来保证分布式事务的ACID特性,性能也因此会有一定损耗。
-
-
跨库join
:-
分库分表后,不同的表分布在不同的服务器上,所以无法直接通过sql join来进行查询,这时候通常会建立
冗余字段
或全局表
来实现跨库join。
-
-
SQL问题
:-
数据分片后,SQL的运行需要分发到各个节点计算,然后将结果合并后再返回,
性能会有所下降
,并且当SQL较为复杂时,结果准确性
也有可能出现问题。这个问题也是数据分片实现中最为关键的点,只能不断优化完善,无法完全解决。
-
-
自增主键
:-
数据分片后,我们将不能再依赖数据库自身的自增键生成机制,需要配置额外的
全局序列
来保证自增主键的全局唯一。
-
五. 小结
需要注意的是,分片(Sharding)
会为数据库维护和业务逻辑带来一系列复杂性和性能损耗,所以除非业务量大到万不得已,否则切莫过度设计
、过早优化
。
面对数据库性能问题,我们可以先尝试用以下方式来解决:
-
数据优化:优化SQL、索引及相关数据库参数配置。
-
硬件扩展:提升服务器CPU、内存、磁盘IOPS等硬件配置。
-
读写分离:通过读写分离架构提高数据库的整体性能。
-
数据拆分:通过垂直拆分库表,或水平分表来解决大表性能问题
如果上述方式仍未能奏效,才考虑最复杂的方案:数据分片