随着业务的不断增长,数据库中的数据也会越来越多,数据库的压力会越来越大,我们会发现,在业务繁忙的时候,数据库的性能会直线下降,这时为了保证良好的性能,需要想办法分担数据库的压力。分担数据库的读负载可以使用主从复制的方式,增加只读从数据库,通过读写分离的方式把数据库的读负载分担到不同的从数据库中,这时在一段时间内已经可以解决问题了。随着业务的发展,会发现,单一的主数据库已经无法承担写负载了,那么这时就需要对单一的主数据库进行拆分了,通常来说,对主数据库的拆分有下面几种方式:

(1)把一个实例中的多个数据库拆分到不同的实例;
(2)把一个库中的表分离到不同的数据库中;


1. 数据库分片

为了解决上面的问题,我们需要对一个库中的相关表进行水平拆分到不同实例的数据库中,即需要进行分片处理,我们通常所说的分库分表,大多数情况下指的就是这种方式。

数据库分片后,数据通常是存放在不同的物理节点上,数据库的分片并不像我们前面所说的第二种分库分表的方式容易实现,要对原来一个独立的数据库进行分片,我们需要考虑很多问题,通常来说,不是万不得已,不建议对数据库进行分片。分片后数据库会变得难以维护。

例如对订单表进行分片,会将订单表分成多个相同表结构的订单表,多个订单表可能会分布在不同的物理节点中:


2、数据库分片前的准备:

在进行数据库分片前,最重要的一项工作就是如何选择分区键。分区键决定了我们如何对数据库进行分片,以及分片后如何查询数据。分区键选择的是否合适直接决定了分区后数据库的性能,对于分区键的选择,我们应该做到以下几点:

分区键要能尽量避免跨分片查询的发生;
分区键要能尽量使各个分片中的数据平均。
(1)如何存储无需分片的表?

方案一:每个分片中存储一份相同(冗余)的数据。这种方式可以更好的提高查询效率。如果使用这种方式,对于维护每个分片中相同表的一致性就显得非常重要了,一般可以采用多写的方式维护数据;
方案二:使用额外的节点统一存储。好处是不存在数据冗余问题。如果分片的表与统一存储的表需要关联查询时,就只能由程序分别查询后进行合并操作了,查询效率比方案一要差一些。

(2)如何在节点上部署分片?

方案一:每个分片使用单一数据库,并且数据库名也相同;
方案二:将多个分片表存储在一个数据库中, 由于数据的表不能重名,所以需要在表名上加入分片号后缀;
方案三:在一个节点中部署多个数据库,每个数据库包含一个分片。


(3)如何分配分片中的数据?

方案一:按分区键的 Hash 值取模来分配分片数据。优点是可以相对平均的,在各个分片中分配数据,缺点是很难人为的控制什么样的数据分配到哪个分片中;
方案二:按分区键的范围来分配分片数据。常用于分区键是日期类型或数值类型的情况,优点是可以很清楚的知道某条数据分配到了哪个分片中,缺点是很容易产生数据分配不平均和数据访问量不平均的情况;
方案三:利用分区键和分片的映射表来分配分片数据,这张映射表需要使用缓存的方式进行缓存,否则这张映射表就可能会成为系统的瓶颈。


(4)如何生成全局唯一ID?

方案一:使用 auto_increment_increment 和 auto_increment_offset 参数;
方案二:使用全局节点来生成ID;
方案三:使用 Redis 等缓存服务器中创建全局 ID。