数据的拆分.docx
《数据的拆分.docx》由会员分享,可在线阅读,更多相关《数据的拆分.docx(25页珍藏版)》请在冰豆网上搜索。
![数据的拆分.docx](https://file1.bdocx.com/fileroot1/2023-7/13/fb65734a-f6ea-4379-9fcb-79da1a95ea0f/fb65734a-f6ea-4379-9fcb-79da1a95ea0f1.gif)
数据的拆分
何谓数据切分
就是指通过某种特定的条件,将我们存放在同一个
数据库中的数据分散存放到多个数据库(主机)上面,以达到分散单台设备负载的效果。
数据的切分同时还可以提高系统的总体可用性,因为单台设备Crash之后,只有总体数据的
某部分不可用,而不是所有的数据。
数据的切分(Sharding)根据其切分规则的类型,可以分为两种切分模式。
一种是按照
不同的表(或者Schema)来切分到不同的数据库(主机)之上,这种切可以称之为数据的
垂直(纵向)切分;另外一种则是根据表中的数据的逻辑关系,将同一个表中的数据按照某
种条件拆分到多台数据库(主机)上面,这种切分称之为数据的水平(横向)切分。
垂直切分的最大特点就是规则简单,实施也更为方便,尤其适合各业务之间的耦合度非
常低,相互影响很小,业务逻辑非常清晰的系统。
在这种系统中,可以很容易做到将不同业
务模块所使用的表分拆到不同的数据库中。
根据不同的表来进行拆分,对应用程序的影响也
更小,拆分规则也会比较简单清晰。
水平切分于垂直切分相比,相对来说稍微复杂一些。
因为要将同一个表中的不同数据拆
分到不同的数据库中,对于应用程序来说,拆分规则本身就较根据表名来拆分更为复杂,后
期的数据维护也会更为复杂一些。
当我们某个(或者某些)表的数据量和访问量特别的大,通过垂直切分将其放在独立的
设备上后仍然无法满足性能要求,这时候我们就必须将垂直切分和水平切分相结合,先垂直
切分,然后再水平切分,才能解决这种超大型表的性能问题。
下面我们就针对垂直、水平以及组合切分这三种数据切分方式的架构实现及切分后数据
的整合进行相应的分析
数据的垂直切分
数据的垂直切分,也可以称之为纵向切分。
将数据库想象成为由很多个一大块一大块的“数据块”(表)组成,我们垂直的将这些“数据块”切开,然后将他们分散到多台数据库主机上面。
这样的切分方法就是一个垂直(纵向)的数据切分。
一个架构设计较好的应用系统,其总体功能肯定是由很多个功能模块所组成的,而每一
个功能模块所需要的数据对应到数据库中就是一个或者多个表。
而在架构设计中,各个功能
模块相互之间的交互点越统一越少,系统的耦合度就越低,系统各个模块的维护性以及扩展
性也就越好。
这样的系统,实现数据的垂直切分也就越容易。
当我们的功能模块越清晰,耦合度越低,数据垂直切分的规则定义也就越容易。
完全可
以根据功能模块来进行数据的切分,不同功能模块的数据存放于不同的数据库主机中,可以
很容易就避免掉跨数据库的Join存在,同时系统架构也非常的清晰。
所以,在数据库进行垂直切分的时候,如何切分,切分到什么样的程度,是一个比较考
验人的难题。
只能在实际的应用场景中通过平衡各方面的成本和收益,才能分析出一个真正
适合自己的拆分方案
如下图所示:
通过这样的垂直切分之后,之前只能通过一个数据库来提供的服务,就被分拆成四个数
据库来提供服务,服务能力自然是增加几倍了。
垂直切分的优点:
◆数据库的拆分简单明了,拆分规则明确;
◆应用程序模块清晰明确,整合容易;
◆数据维护方便易行,容易定位;
垂直切分的缺点:
◆部分表关联无法在数据库级别完成,需要在程序中完成;
◆对于访问极其频繁且数据量超大的表仍然存在性能平静,不一定能满足要求;
◆事务处理相对更为复杂;
◆切分达到一定程度之后,扩展性会遇到限制;
◆过读切分可能会带来系统过渡复杂而难以维护。
对于垂直切分可能遇到数据切分及事务问题,在数据库层面实在是很难找到一个较好
的处理方案。
实际应用案例中,数据库的垂直切分大多是与应用系统的模块相对应,同一个
模块的数据源存放于同一个数据库中,可以解决模块内部的数据关联问题。
而模块与模块之
间,则通过应用程序以服务接口方式来相互提供所需要的数据。
虽然这样做在数据库的总体
操作次数方面确实会有所增加,但是在系统整体扩展性以及架构模块化方面,都是有益的。
可能在某些操作的单次响应时间会稍有增加,但是系统的整体性能很可能反而会有一定的提
升。
而扩展瓶颈问题,就只能依靠数据水平切分架构来解决了。
数据的水平切分
数据的垂直切分基本上可以简单的理解为按照表按照模块来切分数据,而水平切分就不再是按照表或者是功能模块来切分了。
一般来说,简单的水平切分主要是将某个访问极其平凡的表再按照某个字段的某种规则来分散到多个表之中,每个表中包含一部分数据。
简单来说,我们可以将数据的水平切分理解为是按照数据行的切分,就是将表中的某些
行切分到一个数据库,而另外的某些行又切分到其他的数据库中。
当然,为了能够比较容易
的判定各行数据被切分到哪个数据库中了,切分总是都需要按照某种特定的规则来进行的。
如根据某个数字类型字段基于特定数目取模,某个时间类型字段的范围,或者是某个字符类
型字段的hash值。
如果整个系统中大部分核心表都可以通过某个字段来进行关联,那这个
字段自然是一个进行水平分区的上上之选了,当然,非常特殊无法使用就只能另选其他了。
一般来说,像现在互联网非常火爆的Web2.0类型的网站,基本上大部分数据都能够通
过会员用户信息关联上,可能很多核心表都非常适合通过会员ID来进行数据的水平切分。
而像论坛社区讨论系统,就更容易切分了,非常容易按照论坛编号来进行数据的水平切分。
切分之后基本上不会出现各个库之间的交互。
如我们的示例系统,所有数据都是和用户关联的,那么我们就可以根据用户来进行水平
拆分,将不同用户的数据切分到不同的数据库中。
当然,唯一有点区别的是用户模块中的
groups表和用户没有直接关系,所以groups不能根据用户来进行水平拆分。
对于这种特
殊情况下的表,我们完全可以独立出来,单独放在一个独立的数据库中。
其实这个做法可以
“数据的垂直切分”方法,后续介绍这
种垂直切分与水平切分同时使用的联合切分方法。
所以,对于我们的示例数据库来说,大部分的表都可以根据用户ID来进行水平的切分。
不同用户相关的数据进行切分之后存放在不同的数据库中。
如将所有用户ID通过2取模
然后分别存放于两个不同的数据库中。
每个和用户ID关联上的表都可以这样切分。
这样,
基本上每个用户相关的数据,都在同一个数据库中,即使是需要关联,也可以非常简单的关
联上。
我们可以通过下图来更为直观的展示水平切分相关信息:
水平切分的优点
◆表关联基本能够在数据库端全部完成;
◆不会存在某些超大型数据量和高负载的表遇到瓶颈的问题;
◆应用程序端整体架构改动相对较少;
◆事务处理相对简单;
◆只要切分规则能够定义好,基本上较难遇到扩展性限制;
水平切分的缺点
◆切分规则相对更为复杂,很难抽象出一个能够满足整个数据库的切分规则;
◆后期数据的维护难度有所增加,人为手工定位数据更困难;
◆应用系统各模块耦合度较高,可能会对后面数据的迁移拆分造成一定的困难。
垂直与水平联合切分的使用
了解了“垂直”和“水平”这两种切分方式的实现以及切分之后的架构信息,同时也分析了两种架构各自的优缺点。
但是在实际的应用场景中,除了那些负载并不是太大,业务逻辑也相对较简单的系统可以通过上面两种切分方法之一来解决,扩展性问题之外,恐怕其他大部分业务逻辑稍微复杂一点,系统负载大一些的系统,都无法通过上面任何一种数据的切分方法来实现较好的扩展性,而需要将上述两种切分方法结合使用,不同的场景使用不同的切分方法。
一般来说,我们数据库中的所有表很难通过某一个(或少数几个)字段全部关联起来,
所以很难简单的仅仅通过数据的水平切分来解决所有问题。
而垂直切分也只能解决部分问
题,对于那些负载非常高的系统,即使仅仅只是单个表都无法通过单台数据库主机来承担其
负载。
我们必须结合“垂直”和“水平”两种切分方式同时使用,充分利用两者的优点,避
开其缺点。
每一个应用系统的负载都是一步一步增长上来的,在开始遇到性能瓶颈的时候,大多数
架构师和DBA都会选择先进行数据的垂直拆分,因为这样的成本最先,最符合这个时期所追求的最大投入产出比。
然而,随着业务的不断扩张,系统负载的持续增长,在系统稳定一段时期之后,经过了垂直拆分之后的数据库集群可能又再一次不堪重负,遇到了性能瓶颈。
这时候我们该如何抉择?
是再次进一步细分模块呢,还是寻求其他的办法来解决?
如果
我们再一次像最开始那样继续细分模块,进行数据的垂直切分,那我们可能在不久的将来,
又会遇到现在所面对的同样的问题。
而且随着模块的不断的细化,应用系统的架构也会越来
越复杂,整个系统很可能会出现失控的局面
这时候我们就必须要通过数据的水平切分的优势,来解决这里所遇到的问题。
而且,我
们完全不必要在使用数据水平切分的时候,推倒之前进行数据垂直切分的成果,而是在其基
础上利用水平切分的优势来避开垂直切分的弊端,解决系统复杂性不断扩大的问题。
而水平
拆分的弊端(规则难以统一)也已经被之前的垂直切分解决掉了,让水平拆分可以进行的得
心应手。
对于我们的示例数据库,假设在最开始,我们进行了数据的垂直切分,然而随着业务的
不断增长,数据库系统遇到了瓶颈,我们选择重构数据库集群的架构。
如何重构?
考虑到之
前已经做好了数据的垂直切分,而且模块结构清晰明确。
而业务增长的势头越来越猛,即使
现在进一步再次拆分模块,也坚持不了太久。
我们选择了在垂直切分的基础上再进行水平拆
分。
在经历过垂直拆分后的各个数据库集群中的每一个都只有一个功能模块,而每个功能模
块中的所有表基本上都会与某个字段进行关联。
如用户模块全部都可以通过用户ID进行切
分,群组讨论模块则都通过群组ID来切分,相册模块则根据相册ID来进切分,最后的事
件通知信息表考虑到数据的时限性(仅仅只会访问最近某个事件段的信息),则考虑按时间
来切分。
下图展示了切分后的整个架构:
实际上,在很多大型的应用系统中,垂直切分和水平切这两种数据的切分方法基本上都
是并存的,而且经常在不断的交替进行,以不断的增加系统的扩展能力。
我们在应对不同的
应用场景的时候,也需要充分考虑到这两种切分方法各自的局限,以及各自的优势,在不同
的时期(负载压力)使用不同的结合方式。
联合切分的优点
◆可以充分利用垂直切分和水平切分各自的优势而避免各自的缺陷;
◆让系统扩展性得到最大化提升;
联合切分的缺点
◆数据库系统架构比较复杂,维护难度更大;
◆应用程序架构也相对更复杂;
数据切分及整合方案
数据库中的数据在经过垂直和(或)水平切分被存放在不同的数据库主机之后,
应用系统面临的最大问题就是如何来让这些数据源得到较好的整合,
分析可以使用的各种可以帮助我们实现数据切分以及数据整合的整体解决方案。
数据的整合很难依靠数据库本身来达到这个效果,虽然MySQL存在Federated存储引
擎,可以解决部分类似的问题,但是在实际应用场景中却很难较好的运用。
那我们该如何来
整合这些分散在各个MySQL主机上面的数据源呢?
总的来说,存在两种解决思路:
1.在每个应用程序模块中配置管理自己需要的一个(或者多个)数据源,直接访问各
个数据库,在模块内完成数据的整合;
2.通过中间代理层来统一管理所有的数据源,后端数据库集群对前端应用程序透明;
可能90%以上的人在面对上面这两种解决思路的时候都会倾向于选择第二种,尤其是系
统不断变得庞大复杂的时候。
确实,这是一个非常正确的选择,虽然短期内需要付出的成本
可能会相对更大一些,但是对整个系统的扩展性来说,是非常有帮助的。
所以,对于第一种解决思路我这里就不准备过多的分析,下面我重点分析一下在第二种
解决思路中的一些解决方案。
自行开发中间代理层
在决定选择通过数据库的中间代理层来解决数据源整合的架构方向之后,有不少公司
(或者企业)选择了通过自行开发符合自身应用特定场景的代理层应用程序。
通过自行开发中间代理层可以最大程度的应对自身应用的特定,最大化的定制很多个性
化需求,在面对变化的时候也可以灵活的应对。
这应该说是自行开发代理层最大的优势了。
当然,选择自行开发,享受让个性化定制最大化的乐趣的同时,自然也需要投入更多的
成本来进行前期研发以及后期的持续升级改进工作,而且本身的技术门槛可能也比简单的
Web应用要更高一些。
所以,在决定选择自行开发之前,还是需要进行比较全面的评估为好。
由于自行开发更多时候考虑的是如何更好的适应自身应用系统,应对自身的业务场景,
所以这里也不好分析太多。
后面我们主要分析一下当前比较流行的几种数据源整合解决方
利用MySQLProxy实现数据切分及整合
MySQLProxy是MySQL官方提供的一个数据库代理层产品,和MySQLServer一样,
同样是一个基于GPL开源协议的开源产品。
可用来监视、分析或者传输他们之间的通讯信
息。
他的灵活性允许你最大限度的使用它,目前具备的功能主要有连接路由,Query分析,
Query过滤和修改,负载均衡,以及基本的HA机制等。
实际上,MySQLProxy本身并不具有上述所有的这些功能,而是提供了实现上述功能的
基础。
要实现这些功能,还需要通过我们自行编写LUA脚本来实现。
MySQLProxy实际上是在客户端请求与MySQLServer之间建立了一个连接池。
所有客
户端请求都是发向MySQLProxy,然后经由MySQLProxy进行相应的分析,判断出是读操
作还是写操作,分发至对应的MySQLServer上。
对于多节点Slave集群,也可以起做到
负载均衡的效果。
以下是MySQLProxy的基本架构图:
通过上面的架构简图,我们可以很清晰的看出MySQLProxy在实际应用中所处的位置,
以及能做的基本事情。
关于MySQLProxy更为详细的实施细则在MySQL官方文档中有非常
详细的介绍和示例,感兴趣的读者朋友可以直接从MySQL官方网站免费下载或者在线阅读,
利用Amoeba实现数据切分及整合
Amoeba是一个基于Java开发的,专注于解决分布式数据库数据源整合Proxy程序的
开源框架,基于GPL3开源协议。
目前,Amoeba已经具有Query路由,Query过滤,读写
分离,负载均衡以及HA机制等相关内容。
Amoeba主要解决的以下几个问题:
1.数据切分后复杂数据源整合;
2.提供数据切分规则并降低数据切分规则给数据库带来的影响;
3.降低数据库与客户端的连接数;
4.读写分离路由;
我们可以看出,Amoeba所做的事情,正好就是我们通过数据切分来提升数据库的扩展
性所需要的。
Amoeba并不是一个代理层的Proxy程序,而是一个开发数据库代理层Proxy程序的开发框架,目前基于Amoeba所开发的Proxy程序有AmoebaForMySQL和AmoebaForAladin两个。
AmoebaForMySQL主要是专门针对MySQL数据库的解决方案,前端应用程序请求的协议以及后端连接的数据源数据库都必须是MySQL。
对于客户端的任何应用程序来说,AmoebaForMySQL和一个MySQL数据库没有什么区别,任何使用MySQL协议的客户端请求,都可以被AmoebaForMySQL解析并进行相应的处理。
下如可以告诉我们AmoebaForMySQL的架构信息(出自Amoeba开发者博客):
AmoebaForAladin则是一个适用更为广泛,功能更为强大的Proxy程序。
他可以同
时连接不同数据库的数据源为前端应用程序提供服务,但是仅仅接受符合MySQL协议的客
户端应用程序请求。
也就是说,只要前端应用程序通过MySQL协议连接上来之后,Amoeba
ForAladin会自动分析Query语句,根据Query语句中所请求的数据来自动识别出该所
Query的数据源是在什么类型数据库的哪一个物理主机上面。
下图展示了AmoebaFor
Aladin的架构细节(出自Amoeba开发者博客):
ProtocalAdapter处理之后,根据分析结果判断出数据源数据库,然后选择特定的JDBC
驱动和相应协议连接后端数据库。
其实通过上面两个架构图大家可能也已经发现了Amoeba的特点了,他仅仅只是一个开
发框架,我们除了选择他已经提供的ForMySQL和ForAladin这两款产品之外,还可以
基于自身的需求进行相应的二次开发,得到更适应我们自己应用特点的Proxy程序。
当对于使用MySQL数据库来说,不论是AmoebaForMySQL还是AmoebaForAladin
都可以很好的使用。
当然,考虑到任何一个系统越是复杂,其性能肯定就会有一定的损失,
维护成本自然也会相对更高一些。
所以,对于仅仅需要使用MySQL数据库的时候,我还是
建议使用AmoebaForMySQL。
AmoebaForMySQL的使用非常简单,所有的配置文件都是标准的XML文件,总共有四
个配置文件。
分别为:
◆amoeba.xml:
主配置文件,配置所有数据源以及Amoeba自身的参数设置;
◆rule.xml:
配置所有Query路由规则的信息;
◆functionMap.xml:
配置用于解析Query中的函数所对应的Java实现类;
◆rullFunctionMap.xml:
配置路由规则中需要使用到的特定函数的实现类;
如果您的规则不是太复杂,基本上仅需要使用到上面四个配置文件中的前面两个就可完
成所有工作。
Proxy程序常用的功能如读写分离,负载均衡等配置都在amoeba.xml中进行。
此外,Amoeba已经支持了实现数据的垂直切分和水平切分的自动路由,路由规则可以在
rule.xml进行设置。
目前Amoeba少有欠缺的主要就是其在线管理功能以及对事务的支持了,曾经在与相关
开发者的沟通过程中提出过相关的建议,希望能够提供一个可以进行在线维护管理的命令行
管理工具,方便在线维护使用,得到的反馈是管理专门的管理模块已经纳入开发日程了。
另
外在事务支持方面暂时还是Amoeba无法做到的,即使客户端应用在提交给Amoeba的请求是包含事务信息的,Amoeba也会忽略事务相关信息。
当然,在经过不断完善之后,我相信事务支持肯定是Amoeba重点考虑增加的feature。
关于Amoeba更为详细的使用方法读者朋友可以通过Amoeba开发者博客
()上面提供的使用手册获取,这里就不再细述了。
利用HiveDB实现数据切分及整合
和前面的MySQLProxy以及Amoeba一样,HiveDB同样是一个基于Java针对MySQL
数据库的提供数据切分及整合的开源框架,只是目前的HiveDB仅仅支持数据的水平切分。
主要解决大数据量下数据库的扩展性及数据的高性能访问问题,同时支持数据的冗余及基本
的HA机制。
HiveDB的实现机制与MySQLProxy和Amoeba有一定的差异,他并不是借助MySQL
的Replication功能来实现数据的冗余,而是自行实现了数据冗余机制,而其底层主要是
基于HibernateShards来实现的数据切分工作。
在HiveDB中,通过用户自定义的各种Partitionkeys(其实就是制定数据切分规则),
将数据分散到多个MySQLServer中。
在访问的时候,在运行Query请求的时候,会自动
分析过滤条件,并行从多个MySQLServer中读取数据,并合并结果集返回给客户端应用程
序。
单纯从功能方面来讲,HiveDB可能并不如MySQLProxy和Amoeba那样强大,但是其
数据切分的思路与前面二者并无本质差异。
此外,HiveDB并不仅仅只是一个开源爱好者所
共享的内容,而是存在商业公司支持的开源项目。
下面是HiveDB官方网站上面一章图片,描述了HiveDB如何来组织数据的基本信息,
虽然不能详细的表现出太多架构方面的信息,但是也基本可以展示出其在数据切分方面独特
的一面了。
其他实现数据切分及整合的解决方案
除了上面介绍的几个数据切分及整合的整体解决方案之外,还存在很多其他同样提供了
数据切分与整合的解决方案。
如基于MySQLProxy的基础上做了进一步扩展的HSCALE,通
过Rails构建的SpockProxy,以及基于Pathon的Pyshards等等
不管大家选择使用哪一种解决方案,总体设计思路基本上都不应该会有任何变化,那就
是通过数据的垂直和水平切分,增强数据库的整体服务能力,让应用系统的整体扩展能力尽
可能的提升,扩展方式尽可能的便捷
只要我们通过中间层Proxy应用程序较好的解决了数据切分和数据源整合问题,那么
数据库的线性扩展能力将很容易做到像我们的应用程序一样方便,只需要通过添加廉价的
PCServer服务器,即可线性增加数据库集群的整体服务能力,让数据库不再轻易成为应用
系统的性能瓶颈。
数据切分与整合中可能存在的问题
这里,大家应该对数据切分与整合的实施有了一定的认识了,或许很多读者朋友都已经
根据各种解决方案各自特性的优劣基本选定了适合于自己应用场景的方案,后面的工作主要
就是实施准备了。
在实施数据切分方案之前,有些可能存在的问题我们还是需要做一些分析的。
一般来说
我们可能遇到的问题主要会有以下几点:
◆引入分布式事务的问题;
◆跨节点Join的问题;
◆跨节点合并排序分页问题;
1.引入分布式事务的问题
一旦数据进行切分被分别存放在多个MySQLServer中之后,不管我们的切分规则设计
的多么的完美(实际上并不存在完美的切分规则),都可能造成之前的某些事务所涉及
到的数据已经不在同一个MySQLServer中了。
在这样的场景下,如果我们的应用程序仍然按照老的解决方案,那么势必需要引入分布
式事务来解决。
而在MySQL各个版本中,只有从MySQL5.0开始以后的各个版本才开
始对分布式事务提供支持,而且目前仅有Innodb提供分布式事务支持。
不仅如此,即使我
们刚好使用了支持分布式事务的MySQL版本,同时也是使用的Innodb存储引擎,分布式事
务本身对于系统资源的消耗就是很大的,性能本身也并不是太高。
而且引入分布式事务本
身在异常处理方面就会带来较多比较难控制的因素。
怎么办?
其实我们可以可以通过一个变通的方法来解决这种问题,首先需要考虑的一件
事情就是:
是否数据库是唯一一个能够解决事务的地方呢?
其实并不是这样的,我们完
全可以结合数据库以及应用程序两者来共同解决。
各个数据库解决自己身上的事务,然后通
过应用程序来控制多个数据库上面的事务。
也就是说,只要我们愿意,完全可以将一个跨多个数据库的分布式事务分拆成多个仅处
于单个数据库上面的小事务,并通过应用程序来总控各个小事务。
当然,这样作的要求
就是我们的俄应用程序必须要有足够的健壮性,当然也会给应用程序带来一些技术难度。
2.跨节点Join的问题
上面介绍了可能引入分布式事务的问题,现在我们再看看需要跨节点Join的问题。
数
据切分之后,可能会造成有些老的Join语句无法继续使用,因为Join使用的数据源
可能被切分到多个My