分库分表

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 分库分表

一、首先了解下Oracle和Mysql的区别:

1、Oracle查询效率优化为什么比Mysql要快呢。

2、mysql类似农村的新村一样,Oracle类似于高楼大厦(类似一栋楼房) 3、用Oracle的时候没有要求去多键数据库,当做项目的时候用Oracle的话,要求一个数据库就可以了,因为在Oracle中存在表空间,而这个表空间可以看作楼房的电梯。

举个例子:



比如说对于oracle中:

5楼的人家想要一碗面,然后送外卖的直接把一碗面通过梯的方式直接送到5楼。通过索引就可以进行相应的定位,比如定位到我们的表空间,表空间里面有索引空间和临时空间。

而在mysql是下面的样子:以前的农村的通讯是不太好的,基本都是靠吼的,然后有一户人家要一碗面,然后买面的人家也知道是这人家喊的:农村的邻舍的关系都挺好的,卖面的人家临近一家都给打个招呼

小节总结:

1、在mysql数据库之间完全是进行交互的,比如当你去访问一个数据库的时候,不是一来就访问这个数据库的,它会往下进行去搜索,相互之间是进行检索的,否则是找不到的。

2、而Oracle就不一样了,因为里面有表空间,因为它只有一个数据库,所以在操作oracle直接操作的是表空间中的表。可以把上面的图中的通道称为表空间,而一层一层的楼层称之为表。不想mysql那样,和每个数据库都会进行交互,所以效率是很快的。3、在我们开发中用Oracle还不是很多,因为如果用它的高级功能,Oracle是收费的,所以在项目中用的还是mysql比较多。

二、为什么要分库分表?

1、分库分表的原因:

数据库是有IO瓶颈的 数据库服务的CPU也是有限制的,单表超过了2000w的时候,查询效率大幅度降低

2、IO和CPU瓶颈是怎么形成的?

io瓶颈: ①、磁盘读的IO,热点数据太多,数据库缓存放不下,每次查询时产生大量IO,降低了查询速度。

②、网络IO,请求的数据太多了,带宽不够了。

cpu瓶颈:

①、sql语句包含了一些函数(集合操作,分组排序,group by,count,等等)这些就可以导致计算量大。非索引字段条件查询等等,增加cpu运算操作,都会导致cpu瓶颈。

②、单表数据量太大,查询时扫描的数据太多,自然sql执行效率底下,cpu瓶颈就出现了。如何让count函数达到最大的优化:

比如要统计分析的情况下:这时涉及到索引的切换:(innodb,myisam) myisam:这个索引是针对count统计量是事先统计好的,直接把统计的结果给返回出来,这个引擎不支持事务,不支持函数。针对于读多写小的:用这个索引myisam,在优化数据的时候是非常有很大的提高的。

三、怎么做=》数据切分

数据量太大的话,可以把数据切成几大块, 不管是数据的分表还是分库都是对数据进行分片。

1、水平分库


上面的图是按照一定的规则进行水平分库。水平分库不一定按照取模或者hash,也可以按照时间来。比如一个季度放1个库。

1、水平分库:是以”字段“为依据,按照一定的策略(hash,range,时间),将一个库中的数据拆分到多个库中。

2、每个数据库的结构都一样 3、所有数据库中的数据并集就是全量的数据。建议做数据迁移的时候做冷备,不要做热备,因为热备有源源不断的数据写入数据库,有很多服务器在维护的时候,都说某个点在维护,都有个维护时间。

4、分库完系统的并发量也上来了,多个库能够分担IO性能了。解决了单台的性能,提高数据库查询的性能,把一个库的承担负载承担给了多个库。

5、分库难以解决问题,因为只是根据表中的某个字段来分库的。

6、缺点:数据库增多了,维护起来不方便,也会引发事务的问题。

2、垂直分库

一个库里面有很多的表,表加起来的数据量很大,以表为依据,分成多个库 1、尽量避免联合的操作。

2、以表为拆分的依据,按照业务规则不同,将不同的表拆分到不同的库中。

3、每一个库的结构完全是不一样 4、每个库中的数据也不一样,没有交集。

5、不同的库中如果有表相关联的话,需要在业务层面来进行关联。

6、所有库的数据的并集就是全量数据。

3、水平分表


以”字段“为依据,按照一定的策略(hash,range,时间),将表中的数据拆分到多个表中。

1、每个表的结构完全一样 2、每个表中的数据完全不一样,并且没有交集 3、所有表的并集就是全量数据 场景:

用于单表数据量比较大,影响sql的执行效率,加重了cpu的负担,以至于出现瓶颈。

分析:

表的数据量成倍减少,单词执行SQL执行效率上升,自然减轻CPU的负载。系统的并发并没有提高,单表的数据量太多导致cpu的瓶颈就可以把大量数据的表给水平分表,效率提高起来了。

比如在做电商的时候,订单表,日志表,交易表,这些数据量都是很大的表。分表是对用户完全不知道的,在查询的时候只需要连数据库就可以了。

2、垂直分表

场景:表的记录不多,但是字段多,并且热点数据和非热点数据在一起,一行记录存储需要的空间比较大的情况下需要垂直分表。举个例子:微博:文章标题,作者,分类,创建时间 1、这些字段的数据变更的频率相当的低,基本上不怎么变,查询的次数多 (冷数据/非热点数)。可以用MyISAM的引擎 2、在微博的表中,回复数,点赞数,这两个字段的的数据变更的频率非常高,这样的数据称之为热数据,可以选用Innodb,因为这里的数据要保持有效性,和原子性的。

3、这时将这几个字段文章标题,作者,分类,创建时间放入一张表,把这两个字段回复数,点赞数放入一张表,因为当变更数据库中一条数据的时候,它会对这条记录进行加锁。

4、以字段为依据,按照字段的活跃性,将表中的字段拆分到不同的表(主表,从表) 5、每个表的结构是不一样的,每个表的数据也不一样的,每个表的字段至少有一个交集,一般是主键,关联数据 6、所有表的并集就是全量数据。

7、建议表的数据很重要的时候加上变更时间的字段。

总结:

1、水平拆分的优点:

水平分库:解决单表数据过多,主要是降低单库带来的压力 水平分表:解决单表数据过多,从而提高SQL执行查询性能 单库单表的数据保持在一定的量级,有助于提升性能。切分表的结构相同,应用层改造就少了,只需要增加路由规则就可以了。提高了系统的稳定性和负载能力(并放性能提高)。

2、垂直拆分的优点:

垂直分库:解决是数据库中表过多的问题 垂直分表:解决表中列过多(冷数据和热数据问题) 拆分后业务明确 系统进行整合和扩展就比较容易了。垂直分库类似于SOA的服务化。易于实现动静分离,冷热分离的数据库表的数据模式

垂直分库用的多,水平分库用的多。

数据切分策略:垂直拆分:用er分片来拆分的,以业务为依据 水平拆分:用一致性hash,范围,按照id进行切分,也可以按照日期切分。

二、分库分表带来的问题

分库分表能有效的缓解单机和单库带来的性能瓶颈和压力,突破网络IO,硬件资源,连接数的瓶颈,同时也带来了一些问题,具体问题如下:

1、事务的一致性问题

跨库来解决一个业务 举个例子:比如现在发起了一个订单的业务:

1、下订单,必定要减库存,生成订单信息,原本是一个数据库当中的,很好解决,用数据库的ACID来解决遇到的问题。

2、现在不是一个DB了:现在把这个库垂直分库了:这时就会涉及到事务的问题,如何在下订单的时候能够正确的扣减库存,这时就会出现脏数据。

3、为了避免这类的问题产生,这也是分布式事务的问题,如何这两个操作保证数据的一致性呢?要么全部成功,要么全部失败。

4、这种一致性:也是弱一致性(最终一致性),强一致性(两段提交来保证),在开发中用弱一致性比多的。最终一致性习惯用消息表(可以用activeMQ,也可以用RocketMQ)来解决,也可以用补偿性事务来实现,mycat中间件用的事务机制就是两段提交。

5、在分布式项目中是尽量用分布式事务来解决这样的问题,为了出现死锁的问题,一般是使用消息表以异步的方式来进行协调。

用redis也能解决分布式事务的问题:这也类似RocketMQ的回查机制 1、比如现在有两个节点,涉及到两个库,一个订单业务,只要这里存在分布式事务的话 2、会在redis存一个对应的key,存什么都行,存当前操作的商品的Id 3、在执行订单生成的时候,会去redis中查下商品的Id所对应的值存在与否 4、如果这个值为1的话,这个操作还存在事务当中,需要这个操作和另外一个操作的事务进行协调,需要等待这个事务处理完成 5、为了不让大流量的等地啊,这里可以设置一个超时,把redis中的key设置20s的超时时间,一般3-5秒 6、如果20s之内没有处理完,这时将这个key所对应的值给销毁掉。整体 的事务就失败了。

7、如果到一定时间,事务没有提交的话,这时可以判断商品Id的值,如果发现它为0,这时就回查当前操作执行事务的过程,就是事务的结果,可能还没有来的及更改,这时出现了异常但是事务已经提交了,因为已经改为了0,这时就执行提交。如果是1的的话就进行回滚。最终一致性:可以用MQ(消息表), RocketMQ[half message, 回查机制]

2、跨节点关联查询join问题

比如按照业务拆分,拆成3个库,这时就涉及到连表查询的问题 比如在查询订单的时候想要查询这个商品,想通过订单编号查询商品名称:

解决办法

1、原有的方式通过接口整合(RPC:dubbo,或者HTTP:微服务) 比如在订单的服务中去调用商品服务当中根据订单编号所提供的商品信息的方法,就可以达到接口的整合。

2 做字段冗余,在订单信息里面违反三反式:比如说在商品信息表中id,name,count,在订单信息表中有id ,number,pro_id,然后在订单表中添加一个商品的名称,就可以解决跨库join的问题。

3、使用全局表:每个库中都有一张相同的表,相同的数据,把商品表做为一个全局的一张表,全局表:就是把一些数据不经常变化的字段做成数据字典。全局表在微服务中称为基础服务(BaseServer)。也不会导致跨库join的问题,在数据库中称为数据基础字典,在mycat中可以进行设置全局表 4、ER分片:将同一个类型的表放入到一个库中:垂直分库。跨库join很消耗性能的操作。


3、跨节点分页,排序,函数问题

针对分页的问题:意味着每一页的数据来源于不同的库

1、有两个数据节点DN1,DN2 按照范围去存,DN1存的是1到500万的数据,DN2存的是501w到1000w的数据 现在想做一个分页,该怎么去分呢?

2、按照一定的方式去排序,比如说按照时间的先后顺序就行排序,排序完去取前10条的数据 3、查完之后,放入到内存,会在内存中生成一张临时的表。

4、通过mycat中间件 都取完进行合并,然后再进行一次上面的SQL语句的查询 跨节点分页查询对性能消耗 非常大,第二,三。。。页之后是不好查的 5、如果DN1和DN2存的数据量非常大的话,可以存储到hbase中,这时的效率就很高。然后再进行查询,再放入 内存,然后利用MyCat再进行查询。

解决函数的问题:上面的方式一样的解决方案:

4、全局主键重复问题



按照水平分表在做数据插入的时候,表结构是一样的,只是所对应的数据不一样, 要保证Id是唯一的, 1、在数据库中建个通用的序列表,类似于Oracle中的Sequence:序列 1-99999 2、在整个库当中在进行插入的时候,都从序列表中去读取下一个序列的值是什么 这样就可以保证唯一了。这种性能是不高的,因为数据库中的表是串行读取的方式,就会存在堵塞的问题。针对少量的操作是可以的。

第二种方式:uuid:64位。虽然解决了唯一性,但是缺点占的容量太大了。

第三种方式:使用推特,雪花算法:产生50万的id不到3秒。虽然也是64位,但是以数字的方式来存储的。

Twitter的snowflake算法解决了分布式系统生成全局ID的需求,生成64位的Long型数字,组成部分:

  • 第一位未使用
  • 接下来41位是毫秒级时间,41位的长度可以表示69年的时间
  • 5位datacenterId,5位workerId。10位的长度最多支持部署1024个节点
  • 最后12位是毫秒内的计数,12位的计数顺序号支持每个节点每毫秒产生4096个ID序列
    这样的好处是:毫秒数在高位,生成的ID整体上按时间趋势递增;不依赖第三方系统,稳定性和效率较高,理论上QPS约为409.6w/s(1000*2^12),并且整个分布式系统内不会产生ID碰撞;可根据自身业务灵活分配bit位。
    不足就在于:强依赖机器时钟,如果时钟回拨,则可能导致生成ID重复。

5、数据迁移,扩容问题(项目后期)

1、如果知道项目后期要迁移的话,可以用hash分,不是所有的数据在移动,只是部分数据在移动而已。

2、当业务高速发展,面临性能和存储的瓶颈时,才会考虑分片设计,此时就不可避免的需要考虑历史数据迁移的问题。一般做法是先读出历史数据,然后按指定的分片规则再将数据写入到各个分片节点中。(非常麻烦) 3、此外,还需要根据当前的数据量和QPS,以及业务发展的速度,进行容量规划,推算出大概需要多少分片(一般建议单个分片上的单表数据量不超过1000W【经验】)。

4、如果采用数值范围分片,只需要添加节点就可以进行扩容了,不需要对分片数据迁移。如果采用的是数值取模分片,则考虑后期的扩容问题就相对比较麻烦。

三、什么时候考虑切分

1、 能不切分尽量不要切分

1、并不是所有表都需要进行切分,主要还是看数据的增长速度。切分后会在某种程度上提升业务的复杂度,数据库除了承载数据的存储和查询外,协助业务更好的实现需求也是其重要工作之一。

2、不到万不得已不用轻易使用分库分表这个大招,避免"过度设计"和"过早优化"。分库分表之前,不要为分而分,先尽力去做力所能及的事情,例如:升级硬件、升级网络、读写分离、索引优化等等。当数据量达到单表的瓶颈时候,再考虑分库分表。

2、 数据量过大,正常运维影响业务访问

这里说的运维,指:

  • 对数据库备份,如果单表太大,备份时需要大量的磁盘IO和网络IO。例如1T的数据,网络传输占50MB时候,需要20000秒才能传输完毕,整个过程的风险都是比较高的;
  • 对一个很大的表进行DDL修改时,MySQL会锁住全表,这个时间会很长,这段时间业务不能访问此表,影响很大。如果使用pt-online-schema-change,使用过程中会创建触发器和影子表,也需要很长的时间。在此操作过程中,都算为风险时间。将数据表拆分,总量减少,有助于降低这个风险。
  • 大表会经常访问与更新,就更有可能出现锁等待。将数据切分,用空间换时间,变相降低访问压力。

3、 随业务发展需对某些字段垂直拆分

举个例子,

假如项目一开始设计的用户表如下:id bigint #用户的IDname varchar #用户的名字lastlogintime datetime #最近登录时间personalinfo text #私人信息…… #其他信息字段 在项目初始阶段,这种设计是满足简单的业务需求的,也方便快速迭代开发。而当业务快速发展时,用户量从10w激增到10亿,用户非常的活跃,每次登录会更新 lastlogintime 字段,使得 user 表被不断update,压力很大。而其他字段:id, name, personalinfo 是不变的或很少更新的,此时在业务角度,就要将 lastlogintime 拆分出去,新建一个 usertime 表。personalinfo 属性是更新和查询频率较低的,并且text字段占据了太多的空间。这时候,就要对此垂直拆分出 user_ext 表了。

4 、数据量快速增长

随着业务的快速发展,单表中的数据量会持续增长,当性能接近瓶颈时,就需要考虑水平切分,做分库分表了。此时一定要选择合适的切分规则,提前预估好数据容量。

5、 这里写错了,明天修改

随着业务的快速发展,单表中的数据量会持续增长,当性能接近瓶颈时,就需要考虑水平切分,做分库分表了。此时一定要选择合适的切分规则,提前预估好数据容量。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
6月前
|
缓存 关系型数据库 MySQL
分库分表知识总结(四)
分库分表知识总结(四)
76 1
|
6月前
|
SQL 存储 数据库连接
什么是分库分表,为什么要分库分表?
笔者经常将缓存、分库分表、消息队列定义为高并发三剑客。开发互联网应用系统时,分库分表是一个绕不开的技术点。 这篇文章,我们会探讨如下问题:
|
6月前
|
存储 关系型数据库 中间件
什么是分库分表
什么是分库分表
80 3
|
1月前
|
存储 Java 关系型数据库
分库分表专题
分库分表专题
|
3月前
|
中间件 数据库 运维
分库分表
【8月更文挑战第10天】
51 12
|
4月前
|
中间件 数据库
分库分表全局查询
【7月更文挑战第12天】
139 12
|
6月前
|
存储 算法 数据库连接
为什么要分库分表
为什么要分库分表
为什么要分库分表
|
6月前
|
缓存 监控 Java
分库分表带来的问题
分库分表带来的问题
|
SQL 缓存 关系型数据库
什么情况下需要考虑分库分表?
什么情况下需要考虑分库分表?
153 0
|
存储 负载均衡 Oracle
分库分表介绍
分库分表是一种用于解决大规模数据存储和查询性能问题的数据库架构设计技术。它将一个数据库拆分成多个独立的数据库实例(分库),并将每个数据库实例的表进一步拆分成多个子表(分表)。这样可以提高数据库的并发处理能力和查询性能。
249 0