1 为什么要分库分表

物理服务机的CPU、内存、存储设备、连接数等资源有限,某个时段大量连接同时执行操作,会导致数据库在处理上遇到性能瓶颈。为了解决这个问题,行业先驱门充分发扬了分而治之的思想,对大库表进行分割,
然后实施更好的控制和管理,同时使用多台机器的CPU、内存、存储,提供更好的性能。而分治有两种实现方式:垂直拆分和水平拆分。

2 垂直拆分(Scale Up 纵向扩展)

垂直拆分分为垂直分库和垂直分表,主要按功能模块拆分,以解决各个库或者各个表之间的资源竞争。比如分为订单库、商品库、用户库...这种方式,多个数据库之间的表结构是不同的。

2.1 垂直分库

先说说垂直分库。垂直分库其实是一种简单逻辑分割。比如我们的数据库中有商品表Products、还有对订单表Orders,还有积分表Scores。接下来我们就可以创建三个数据库,一个数据库存放商品,一个数据库存放订单,一个数据库存放积分。
垂直分库有一个优点,他能够根据业务场景进行孵化,比如某一单一场景只用到某2-3张表,基本上应用和数据库可以拆分出来做成相应的服务。拆分方式如下图所示:
 
 

2.2 垂直分表

再来说说垂直分表,比较适用于那种字段比较多的表,假设我们一张表有100个字段,我们分析了一下当前业务执行的SQL语句,有20个字段是经常使用的,而另外80个字段使用比较少。
这样我们就可以把20个字段放在主表里面,我们再创建一个辅助表,存放另外80个字段。当然主表和辅助表都是有主键的,他们通过主键进行关联合并,就可以组合成100个字段的表。拆分方式如下图所示。
除了这种访问频率的冷热拆分之外,还可以按照字段类型结构来拆分,比如大文本字段单独放在一个表中,与基础字段隔离,提高基础字段的访问效率。
也可以将字段按照功能用途来拆分,比如采购的物料表可以按照基本属性、销售属性、采购属性、生产制造属性、财务会计属性等用途垂直拆分。
 
总体来说:垂直拆分有以下优点:
  • 跟随业务进行分割,类似微服务的分治理念,方便解耦之后的管理及扩展。
  • 高并发的场景下,垂直拆分使用多台服务器的CPU、I/O、内存能提升性能,同时对单机数据库连接数、一些资源限制也得到了提升,能实现冷热数据的分离。
垂直拆分的缺点:
  • 部分业务表无法join,应用层需要很大的改造,只能通过聚合的方式来实现。增加了开发的难度。
  • 单表数据量膨胀的问题依然没有得到有效的解决。分布式事务也是一个难题。

3 水平拆分(Scale Out 横向扩展)

水平拆分又分为库内分表和分库分表,来解决单表中数据量增长出现的压力,这些数据库中的表结构完全相同。

3.1 库内分表

先说说库内分表。假设当我们的Orders表达到了5000万行记录的时候,非常影响数据库的读写效率,怎么办呢?
我们可以考虑按照订单编号的order_id进行rang分区,就是把订单编号在1-1000万的放在order1表中,将编号在1000万-2000万的放在order2中,以此类推,每个表中存放1000万数据。
 

关于水平分表的时机,业内的标准不是很统一,阿里的Java 开发手册的标准是当单表行数超过 500万行或者单表容量超过 2 GB时,才推荐进行分库分表。百度的则是1000 W行的进行分表,这个是百度的DBA经过测试推算出的结果。

但是这边忽略了单表的字段数和字段类型,如果字段数很多,超过50列,对性能影响也是不小的,我们曾经有个业务,表字段是随着业务的增长而自动扩增的,到了后期,字段越来越多,查询性能也越来越慢。

所以个人觉得不必拘泥于500W 还是1000W,开发人员在使用过程中,如果压测发现因为数据基数变大而导致执行效率慢下来,就可以开始考虑分表了。

3.2 库内分表的实现策略

目前在MySql中支持四种表分区的方式,分别为HASH、RANGE、LIST及KEY,当然在其它的类型数据库中,分区的实现方式略有不同,但是分区的思想原理是相同,具体如下:

3.2.1 HASH(哈希)

HASH分区主要用来确保数据在预先确定数目的分区中平均分布,而在RANGE和LIST分区中,必须明确指定一个给定的列值或列值集合应该保存在哪个分区中,而在HASH分区中,MySQL自动完成这些工作,

你所要做的只是基于将要被哈希的列值指定一个列值或表达式,以及指定被分区的表将要被分割成的分区数量。 示例如下:

 1 drop table if EXISTS  `t_userinfo`;
2 CREATE TABLE `t_userinfo` (
3 `id` int(10) unsigned NOT NULL,
4 `personcode` varchar(20) DEFAULT NULL,
5 `personname` varchar(100) DEFAULT NULL,
6 `depcode` varchar(100) DEFAULT NULL,
7 `depname` varchar(500) DEFAULT NULL,
8 `gwcode` int(11) DEFAULT NULL,
9 `gwname` varchar(200) DEFAULT NULL,
10 `gravalue` varchar(20) DEFAULT NULL,
11 `createtime` DateTime NOT NULL
12 ) ENGINE=InnoDB DEFAULT CHARSET=utf8
13 PARTITION BY HASH(YEAR(createtime))
14 PARTITIONS 10; 

上面的例子,使用HASH函数对createtime日期进行HASH运算,并根据这个日期来分区数据,这里共分为10个分区。

建表语句上添加一个“PARTITION BY HASH (expr)”子句,其中“expr”是一个返回整数的表达式,它可以是字段类型为MySQL 整型的一列的名字,也可以是返回非负数的表达式。

另外,可能需要在后面再添加一个“PARTITIONS num”子句,其中num 是一个非负的整数,它表示表将要被分割成分区的数量。

3.2.2 RANGE(范围)

基于属于一个给定连续区间的列值,把多行分配给同一个分区,这些区间要连续且不能相互重叠,使用VALUES LESS THAN操作符来进行定义。示例如下:

 1 drop table if EXISTS  `t_userinfo`;
2 CREATE TABLE `t_userinfo` (
3 `id` int(10) unsigned NOT NULL,
4 `personcode` varchar(20) DEFAULT NULL,
5 `personname` varchar(100) DEFAULT NULL,
6 `depcode` varchar(100) DEFAULT NULL,
7 `depname` varchar(500) DEFAULT NULL,
8 `gwcode` int(11) DEFAULT NULL,
9 `gwname` varchar(200) DEFAULT NULL,
10 `gravalue` varchar(20) DEFAULT NULL,
11 `createtime` DateTime NOT NULL
12 ) ENGINE=InnoDB DEFAULT CHARSET=utf8
13 PARTITION BY RANGE(gwcode) (
14 PARTITION P0 VALUES LESS THAN(101) ,
15 PARTITION P1 VALUES LESS THAN(201) ,
16 PARTITION P2 VALUES LESS THAN(301) ,
17 PARTITION P3 VALUES LESS THAN MAXVALUE
18 );

上面的示例,使用了范围RANGE函数对岗位编号进行分区,共分为4个分区,

岗位编号为1~100 的对应在分区P0中,101~200的编号在分区P1中,依次类推即可。那么类别编号大于300,可以使用MAXVALUE来将大于300的数据统一存放在分区P3中即可。

3.2.3 LIST(预定义列表)

类似于按RANGE分区,区别在于LIST分区是基于列值匹配一个离散值集合中的某个值来进行选择分区的。LIST分区通过使用“PARTITION BY LIST(expr)”来实现,其中“expr” 是某列值或一个基于某个列值、并返回一个整数值的表达式,

然后通过“VALUES IN (value_list)”的方式来定义每个分区,其中“value_list”是一个通过逗号分隔的整数列表。 示例如下:

 1 drop table if EXISTS  `t_userinfo`;
2 CREATE TABLE `t_userinfo` (
3 `id` int(10) unsigned NOT NULL,
4 `personcode` varchar(20) DEFAULT NULL,
5 `personname` varchar(100) DEFAULT NULL,
6 `depcode` varchar(100) DEFAULT NULL,
7 `depname` varchar(500) DEFAULT NULL,
8 `gwcode` int(11) DEFAULT NULL,
9 `gwname` varchar(200) DEFAULT NULL,
10 `gravalue` varchar(20) DEFAULT NULL,
11 `createtime` DateTime NOT NULL
12 ) ENGINE=InnoDB DEFAULT CHARSET=utf8
13 PARTITION BY LIST(`gwcode`) (
14 PARTITION P0 VALUES IN (46,77,89) ,
15 PARTITION P1 VALUES IN (106,125,177) ,
16 PARTITION P2 VALUES IN (205,219,289) ,
17 PARTITION P3 VALUES IN (302,317,458,509,610)
18 );

上面的例子,使用了列表匹配LIST函数对员工岗位编号进行分区,共分为4个分区,编号为46,77,89的对应在分区P0中,106,125,177类别在分区P1中,依次类推即可。

不同于RANGE的是,LIST分区的数据必须匹配列表中的岗位编号才能进行分区,所以这种方式只是适合比较区间值确定并少量的情况。

3.2.4 KEY(键值)

类似于按HASH分区,区别在于KEY分区只支持计算一列或多列,且MySQL 服务器提供其自身的哈希函数。必须有一列或多列包含整数值。 示例如下:

 1 drop table if EXISTS  `t_userinfo`;
2 CREATE TABLE `t_userinfo` (
3 `id` int(10) unsigned NOT NULL,
4 `personcode` varchar(20) DEFAULT NULL,
5 `personname` varchar(100) DEFAULT NULL,
6 `depcode` varchar(100) DEFAULT NULL,
7 `depname` varchar(500) DEFAULT NULL,
8 `gwcode` int(11) DEFAULT NULL,
9 `gwname` varchar(200) DEFAULT NULL,
10 `gravalue` varchar(20) DEFAULT NULL,
11 `createtime` DateTime NOT NULL
12 ) ENGINE=InnoDB DEFAULT CHARSET=utf8
13 PARTITION BY KEY(gwcode)
14 PARTITIONS 10; 

注意:此种分区算法目前使用的比较少,使用服务器提供的哈希函数有不确定性,对于后期数据统计、整理存在会更复杂,所以我们更倾向于使用由我们定义表达式的Hash,大家知道其存在和怎么使用即可。

3.2.5 Composite(复合模式)

Composite是上面几种模式的组合使用,比如你在Range的基础上,再进行Hash 哈希分区。

3.3 分库分表

库内分表解决了单表数据量过大的瓶颈问题,但使用还是同一主机的CPU、IO、内存,另外单库的连接数也有限制,并不能完全的降低系统的压力。
此时,我们就要考虑另外一种技术叫分库分表。分库分表在库内分表的基础上,将分的表挪动到不同的主机和数据库上。可以充分的使用其他主机的CPU、内存和IO资源。 拆分方式进一步演进到下面:
 
  

4 分库分表存在的问题

4.1 事务问题

在执行分库分表之后,由于数据存储到了不同的库上,数据库事务管理出现了困难。如果依赖数据库本身的分布式事务管理功能去执行事务,将付出高昂的性能代价;如果由应用程序去协助控制,形成程序逻辑上的事务,又会造成编程方面的负担。

4.2 跨库跨表的join问题

在执行了分库分表之后,难以避免会将原本逻辑关联性很强的数据划分到不同的表、不同的库上,这时,表的关联操作将受到限制,我们无法join位于不同分库的表,也无法join分表粒度不同的表,结果原本一次查询能够完成的业务,可能需要多次查询才能完成。

4.3 额外的数据管理负担和数据运算压力

额外的数据管理负担,最显而易见的就是数据的定位问题和数据的增删改查的重复执行问题,这些都可以通过应用程序解决,但必然引起额外的逻辑运算,例如,对于一个记录用户成绩的用户数据表userTable,业务要求查出成绩最好的100位,在进行分表之前,

只需一个order by语句就可以搞定,但是在进行分表之后,将需要n个order by语句,分别查出每一个分表的前100名用户数据,然后再对这些数据进行合并计算,才能得出结果。

 
 

MySQL全面瓦解28:分库分表的更多相关文章

  1. MYSQL性能优化分享(分库分表)

    1.分库分表 很明显,一个主表(也就是很重要的表,例如用户表)无限制的增长势必严重影响性能,分库与分表是一个很不错的解决途径,也就是性能优化途径,现在的案例是我们有一个1000多万条记录的用户表mem ...

  2. Mysql系列七:分库分表技术难题之分布式全局唯一id解决方案

    一.前言 在前面的文章Mysql系列四:数据库分库分表基础理论中,已经说过分库分表需要应对的技术难题有如下几个: 1. 分布式全局唯一id 2. 分片规则和策略 3. 跨分片技术问题 4. 跨分片事物 ...

  3. Mycat数据库中间件对Mysql读写分离和分库分表配置

    Mycat是一个开源的分布式数据库系统,不同于oracle和mysql,Mycat并没有存储引擎,但是Mycat实现了mysql协议,前段用户可以把它当做一个Proxy.其核心功能是分表分库,即将一个 ...

  4. 3.Mysql集群------Mycat分库分表

    前言: 分库分表,在本节里是水平切分,就是多个数据库里包含的表是一模一样的. 只是把字段散列的分到不同的库中. 实践: 1.修改schema.xml 这里是在同一台服务器上建立了4个数据库db1,db ...

  5. 【MySQL】数据库(分库分表)中间件对比

    分区:对业务透明,分区只不过把存放数据的文件分成了许多小块,例如mysql中的一张表对应三个文件.MYD,MYI,frm. 根据一定的规则把数据文件(MYD)和索引文件(MYI)进行了分割,分区后的表 ...

  6. MySQL系列(八)--数据库分库分表

    在互联网公司或者一些并发量比较大的项目,虽然有各种项目架构设计.NoSQL.MQ.ES等解决比较高的并发访问,但是对于数据库来说,压力 还是太大,这时候即使数据库架构.表结构.索引等都设计的很好了,但 ...

  7. MySQL:互联网公司常用分库分表方案汇总!

    转载别人 一.数据库瓶颈 不管是IO瓶颈,还是CPU瓶颈,最终都会导致数据库的活跃连接数增加,进而逼近甚至达到数据库可承载活跃连接数的阈值.在业务Service来看就是,可用数据库连接少甚至无连接可用 ...

  8. MySQL:互联网公司常用分库分表方案汇总!

    一.数据库瓶颈 不管是IO瓶颈,还是CPU瓶颈,最终都会导致数据库的活跃连接数增加,进而逼近甚至达到数据库可承载活跃连接数的阈值.在业务Service来看就是,可用数据库连接少甚至无连接可用.接下来就 ...

  9. 数据字符集mysql主从数据库,分库分表等笔记

    文章结束给大家来个程序员笑话:[M] 1.mysql的目录:在rpm或者yum安装时:/var/lib/mysql  在编译安装时默许目录:/usr/local/mysql 2.用rpm包安装的MyS ...

  10. MySQL主从复制&读写分离&分库分表

    MySQL主从复制 MySQL的主从复制只能保证主机对外提供服务,从机是不提供服务的,只是在后台为主机进行备份数据 首先我们说说主从复制的原理,这个是必须要理解的玩意儿: 理解: MySQL之间的数据 ...

随机推荐

  1. AIbee 笔试

    CSS选择器 div+p 选择紧接在div元素之后的所有< p >元素 C++删除数组最后一个元素. 例如[1 2 3 4] 最后变为 [1 2 3] 用splice的删除,增加和替换 a ...

  2. 浅尝装饰器-@staticmethod 和@classmethod

    [写在前面] 本帖归属于装饰器单元的学习,可以点击关键词'装饰器'查看其他博文讲解 [正文部分] 说到装饰器一开始我觉得很陌生,看了一下别人的博客讲解,原来以前学习遇到的静态方法@staticmeth ...

  3. 如何知道当前使用的python的安装路径

    电脑里多处安装了python,那么如何得知当前使用python的安装路径呢? 方法一 运行python指令: import sys print(sys.executable) 方法二 对于终端和Win ...

  4. 敏捷 Scrum Master 的難點

    什麼是 Scrum Master? Scrum master 是一個團隊角色,負責確保團隊遵守敏捷方法和原則並符合團隊的流程和實踐. Scrum Master 促進敏捷開發團隊成員之間的協作.Scru ...

  5. Beta实际开发与初始计划的比较

    零.说明 本篇博客为Beta阶段开始十天后,实际开发工作与初始计划的比较 截止至本篇博客发布为止,团队所有成员已完成计网考试,将在本周日进行充分的接口测试 一.比较 1.与初始计划对比 初始计划 实际 ...

  6. Charles的简单用法

    Charles的简单用法 一.抓电脑上 http 包 二.显示请求的 Request 和 Response 三.抓取电脑上 https 包 1.安装根证书 2.在钥匙串中启用根证书 3.配置哪些需要抓 ...

  7. springboot整合rabbitmq实现生产者消息确认、死信交换器、未路由到队列的消息

    在上篇文章  springboot 整合 rabbitmq 中,我们实现了springboot 和rabbitmq的简单整合,这篇文章主要是对上篇文章功能的增强,主要完成如下功能. 需求: 生产者在启 ...

  8. RabbitMQ处理未被路由的消息

    我们经常使用消息队列进行系统之间的解耦,日志记录等等.但是有时候我们在使用 RabbitMQ时,由于exchange.bindKey.routingKey没有设置正确,导致我们发送给交换器(excha ...

  9. (六)、Docker 之 Dockerfile

    1.什么是Dockerfile Dockerfile是用来构建Docker镜像的构建文件,是由一系列命令和参数构成的脚本. 2.Dockerfile解析过程 前提认知: 每条保留字指令都必须为大写字母 ...

  10. Noip模拟13 2021.7.13:再刚题,就剁手&&生日祭

    T1 工业题 这波行列看反就非常尴尬.....口糊出所有正解想到的唯独行列看反全盘炸列(因为和T1斗智斗勇两个半小时...) 这题就是肯定是个O(n+m)的,那就往哪里想,a,b和前面的系数分开求,前 ...