数据库分库分表策略的参考实现方案

目录

一、MySQL扩展方案具体的实现方式

二、单库分表实现策略

三、分库单表实现策略

四、分库分表实现策略

五、分库分表总结

六、本文总结


随着业务规模的不断扩大,需要选择合适的方案去应对数据规模的增长,以应对逐渐增长的访问压力和数据量。对于数据库的扩展方案,主要包括:业务拆分、主从复制,数据库分库与分表。这篇文章主要讲述数据库分库与分表。

一、MySQL扩展方案具体的实现方式

(1)业务拆分

业务起步初始,为了加快应用上线和快速迭代,很多应用都采用集中式的架构。随着业务系统的扩大,系统变得越来越复杂,越来越难以维护,开发效率变得越来越低,并且对资源的消耗也变得越来越大,通过硬件提高系统性能的方式带来的成本也越来越高。

因此,在选型初期,一个优良的架构设计是后期系统进行扩展的重要保障。

例如:电商平台,包含了用户、商品、评价、订单等几大模块,最简单的做法就是在一个数据库中分别创建users、shops、comment、order四张表。

 但是,随着业务规模的增大、访问量的变大,我们不得不对业务进行拆分。对于每一个模块,都使用单独的数据库来进行存储。不同的业务访问不同的数据库,将原本对一个数据库的依赖拆分为对4个数据库的依赖。这样,系统就有4个数据库同时承担压力,吞吐量自然就提高了。 

 

(2)主从复制

1、MySQL5.6 数据库主从(Master/Slave)同步安装与配置详解:

http://blog.csdn.net/xlgen157387/article/details/51331244

2、MySQL主从复制的常见拓扑、原理分析以及如何提高主从复制的效率总结:

http://blog.csdn.net/xlgen157387/article/details/52451613

3、使用mysqlreplicate命令快速搭建 Mysql 主从复制:

http://blog.csdn.net/xlgen157387/article/details/52452394

上述三篇文章,讲述了如何配置主从数据库,以及如何实现数据库的读写分离,这里不再赘述。有需要的同学,请自己点击查看。

下图是一张关于MySQL的Master和Slave之间数据同步的过程图。

该图主要讲述了MySQL主从复制的原理:首先,Slave从Master获取Binary log文件;然后,Slave在本地镜像地执行Binary日志中记录的操作。由于主从复制的过程是异步的,因此Slave和Master之间的数据有可能存在延迟的现象,其只能保证数据最终的一致性。 

(3)数据库分库与分表

我们知道,无论机器配置多么好,每台机器都有自身的物理上限。所以,当我们的应用已经能触及或远远超出单台机器的某个上限的时候,我们惟有寻找别的机器的帮助,或者继续升级的我们的硬件,但常见的方案还是通过添加更多的机器来共同承担压力。

当我们的业务逻辑不断增长,我们的机器能不能通过线性增长就能满足需求?使用数据库的分库分表,能够立竿见影的提升系统的性能。关于为什么要使用数据库的分库分表,这里不再赘述,主要讲解具体的实现策略。

二、单库分表实现策略

关键字:用户ID、表容量

大部分数据库的设计和业务操作,基本都与用户ID相关。因此,用户ID作为贯穿整个系统的重要字段,是最常用的分库分表的路由策略因子。因此,使用用户ID,不仅可以方便查询,还可以将数据平均分配到不同的数据库中。(当然,还可以根据类别等进行分表操作,分表的路由策略有很多方式)

接着上述电商平台假设,订单表order存放用户的订单数据,sql脚本如下(只是为了演示,省略部分细节):

CREATE TABLE `order` (
  `order_id` bigint(32) primary key auto_increment,
  `user_id` bigint(32),
   ...
) 

当数据量比较大的时候,对数据进行分表,首先要确定将数据平均分配到多少张表中,也就是:表容量。

这里假设单库有100张表进行存储,则我们在进行存储数据的时候,首先对用户ID进行取模操作,根据 user_id%100 获取对应的表进行存储和查询操作,示意图如下:

例如,user_id = 101, 我们在获取值的时候,可以通过下边的sql语句:

select * from order_1 where user_id= 101

其中,表名order_1的后缀数值是根据 101%100 计算所得。

在实际的开发中,如果我们使用MyBatis做持久层,MyBatis已经提供了很好地支持数据库分表的功能。例如,上述sql用MyBatis实现,应该是:

 接口定义:

/**
  * 获取用户相关的订单详细信息
  * @param tableNum 具体某一个表的编号
  * @param userId 用户ID
  * @return 订单列表
  */
public List<Order> getOrder(@Param("tableNum") int tableNum,@Param("userId") int userId);

xml配置映射文件:

<select id="getOrder" resultMap="BaseResultMap">
    select * from order_${tableNum}
    where user_id = #{userId}
</select>

其中${tableNum} 含义是直接让参数加入到sql中,这是MyBatis支持的特性。

注意:
另外,在实际的开发中,我们的用户ID更多的可能是通过UUID生成的。若是这样,我们可以首先对UUID进行hash,获取到整数值;然后,对该整数值进行取模操作。

三、分库单表实现策略

单个数据库分表,能够解决“单表在数据量很大时查询数据的效率问题”,但是无法给数据库的并发操作带来效率上的提高。单库分表还是在一个数据库上进行操作,很容易受数据库IO性能的限制。

如何平均分配数据库的IO性能?显然,对数据进行分库存储,可以很好地解决单台数据库的性能问题。

分库策略与分表策略的实现相似,最简单的都是可以通过取模的方式进行路由。

还是上例,对用户ID进行取模操作,可以获取到某一个具体的数据库,同理,这种方式对关键字为:用户ID、库容量

数据库路由的示意图如下:

 上图中,库总数为100。

同样,如果用户ID为UUID,那么:首先对用户ID进行hash取值;然后,对hash结果值进行取模。

四、分库分表实现策略

上述的配置中,单库分表可以解决“单表海量数据的查询性能问题”,分库单表可以解决“单台数据库的并发访问压力问题”。

在实际应用中,我们既需要分库操作,也需要分表操作,以便同时扩展系统的并发处理能力和提升单表的查询性能,这就是分库分表。

分库分表的策略相对复杂一些,一种常见的路由策略如下:


1、中间变量 = user_id%(库数量*每个库的表数量);
2、库序号 = 取整(中间变量/每个库的表数量);
3、表序号 = 中间变量%每个库的表数量;
 

例如:数据库有256 个,每一个库中有1024个数据表,用户的user_id=262145,按照上述的路由策略,可得:

1、中间变量 = 262145%(256*1024)= 1;
2、库序号 = 取整(1/1024)= 0;
3、表序号 = 1%1024 = 1;
 

这样,对于user_id=262145,将被路由到第0个数据库的第1个表中。

示意图如下:

五、分库分表总结

分库分表策略有很多种,根据用户ID进行分库分表是比较简单的一种。对于其他分库分表方式,比如使用号段、使用hash进行路由等,有兴趣的同学可以自行查找学习。

如果用户的ID是通过UUID方式生成的,那么:首先,我们需要单独进行一次hash操作;然后,对hash结果进行取模。其实hash本身就是一种分库分表的策略,使用hash作为路由策略的时候,我们需要知道hash路由策略的优缺点,优点是:数据分布均匀;缺点是:数据迁移的时候麻烦,不能按照机器性能分摊数据。

通过分库分表操作,系统的数据查询性能和并发能力都得到了提高。但是,还有一些需要注意的点:

(1)原本单库的事务变成了分布式事务;

(2)由于记录被切分到不同的数据库和数据表中,难以进行多表关联查询;

(3)对数据进行查询时,必须指定路由字段。

(4)分库分表之后,如果我们需要对数据库进行进一步的扩容(或者路由策略变更),将变得非常不方便,需要重新进行数据迁移。

自己开发分库分表工具的工作量是巨大的,好在业界已经有了很多比较成熟的分库分表中间件,我们可以将更多的时间放在业务实现上。

业界常用分库分表工具:

  • sharding-jdbc(当当)

  • TSharding(蘑菇街)

  • Atlas(奇虎360)

  • Cobar(阿里巴巴)

  • MyCAT(基于Cobar)

  • Oceanus(58同城) Vitess(谷歌)

六、本文总结

通过本文,我们学到了如何进行数据库分库分表。那么,是不是就可以实现一个可扩展、高性能、高并发的网站?很显然,还不可以!一个大型网站使用到的技术远不止这些,这些只是其中最基础的一个环节,还有很多具体的细节我们没有掌握到,比如:数据库的集群控制、集群的负载均衡、灾难恢复、故障自动切换、事务管理等。因此,还有很多需要去学习和研究的地方。

猜你喜欢

转载自blog.csdn.net/chinawangfei/article/details/121087681