通过MySQLReplication功能所实现的扩展总是会受到数据库大小的限制。一旦数据库过于庞大,尤其是当写入过于频繁,非常难由一台主机支撑的时候,我们还是会面临到扩展瓶颈。这时候,我们就必须许找其它技术手段来解决这个瓶颈,那就是我们这一章所要介绍恶的数据切分技术。
可能非常多读者朋友在网上或者杂志上面都已经多次见到关于数据切分的相关文章了,仅仅只是在有些文章中称之为数据的Sharding。事实上无论是称之为数据的Sharding还是数据的切分,其概念都是一样的。
简单来说,就是指通过某种特定的条件,将我们存放在同一个数据库中的数据分散存放到多个数据库(主机)上面,以达到分散单台设备负载的效果。数据的切分同一时候还能够提高系统的总体可用性,由于单台设备Crash之后。仅仅有总体数据的某部分不可用,而不是全部的数据。
数据的切分(Sharding)依据其切分规则的类型。能够分为两种切分模式。
一种是依照不同的表(或者Schema)来切分到不同的数据库(主机)之上,这样的切能够称之为数据的垂直(纵向)切分。另外一种则是依据表中的数据的逻辑关系,将同一个表中的数据依照某种条件拆分到多台数据库(主机)上面。这样的切分称之为数据的水平(横向)切分。
垂直切分的最大特点就是规则简单,实施也更为方便,尤其适合各业务之间的耦合度非常低。相互影响非常小,业务逻辑非常清晰的系统。在这样的系统中,能够非常easy做到将不同业务模块所使用的表分拆到不同的数据库中。依据不同的表来进行拆分。对应用程序的影响也更小,拆分规则也会比較简单清晰。
水平切分于垂直切分相比。相对来说略微复杂一些。由于要将同一个表中的不同数据拆分到不同的数据库中,对于应用程序来说,拆分规则本身就较依据表名来拆分更为复杂,后期的数据维护也会更为复杂一些。
当我们某个(或者某些)表的数据量和訪问量特别的大,通过垂直切分将其放在独立的设备上后仍然无法满足性能要求,这时候我们就必须将垂直切分和水平切分相结合。先垂直切分,然后再水平切分。才干解决这样的超大型表的性能问题。
以下我们就针对垂直、水平以及组合切分这三种数据切分方式的架构实现及切分后数据的整合进行对应的分析。
我们先来看一下,数据的垂直切分究竟是怎样一个切分法的。数据的垂直切分。也能够称之为纵向切分。将数据库想象成为由非常多个一大块一大块的“数据块”(表)组成。我们垂直的将这些“数据块”切开,然后将他们分散到多台数据库主机上面。这样的切分方法就是一个垂直(纵向)的数据切分。
一个架构设计较好的应用系统。其总体功能肯定是由非常多个功能模块所组成的。而每一个功能模块所须要的数据对应到数据库中就是一个或者多个表。
而在架构设计中,各个功能模块相互之间的交互点越统一越少,系统的耦合度就越低,系统各个模块的维护性以及扩展性也就越好。这样的系统。实现数据的垂直切分也就越easy。
当我们的功能模块越清晰,耦合度越低,数据垂直切分的规则定义也就越easy。全然能够依据功能模块来进行数据的切分,不同功能模块的数据存放于不同的数据库主机中,能够非常easy就避免掉跨数据库的Join存在。同一时候系统架构也非常的清晰。
当然。非常难有系统能够做到全部功能模块所使用的表全然独立,全然不须要訪问对方的表或者须要两个模块的表进行Join操作。这样的情况下,我们就必须依据实际的应用场景进行评估权衡。决定是迁就应用程序将须要Join的表的相关某快都存放在同一个数据库中,还是让应用程序做很多其它的事情,也就是程序全然通过模块接口取得不同数据库中的数据,然后在程序中完毕Join操作。
一般来说。假设是一个负载相对不是非常大的系统,并且表关联又非常的频繁。那可能数据库让步。将几个相关模块合并在一起降低应用程序的工作的方案能够降低较多的工作量。是一个可行的方案。
当然。通过数据库的让步,让多个模块集中共用数据源,实际上也是简单介绍的默许了各模块架构耦合度增大的发展,可能会让以后的架构越来越恶化。尤其是当发展到一定阶段之后,发现数据库实在无法承担这些表所带来的压力。不得不面临再次切分的时候。所带来的架构改造成本可能会远远大于最初的时候。
所以。在数据库进行垂直切分的时候,怎样切分,切分到什么样的程度,是一个比較考验人的难题。仅仅能在实际的应用场景中通过平衡各方面的成本和收益。才干分析出一个真正适合自己的拆分方案。
比方在本书所使用演示样例系统的example数据库,我们简单的分析一下。然后再设计一个简单的切分规则,进行一次垂直垂直拆分。
系统功能能够基本分为四个功能模块:用户,群组消息,相冊以及事件。分别对应为例如以下这些表:
1. 用户模块表:user,user_profile,user_group,user_photo_album
2. 群组讨论表:groups,group_message,group_message_content,top_message
3. 相冊相关表:photo,photo_album,photo_album_relation,photo_comment
4. 事件信息表:event
初略一看,没有哪一个模块能够脱离其它模块独立存在,模块与模块之间都存在着关系。莫非无法切分?
当然不是,我们再略微深入分析一下,能够发现,尽管各个模块所使用的表之间都有关联,可是关联关系还算比較清晰,也比較简单。
◆ 群组讨论模块和用户模块之间主要存在通过用户或者是群组关系来进行关联。一般关联的时候都会是通过用户的id或者nick_name以及group的id来进行关联。通过模块之间的接口实现不会带来太多麻烦。
◆ 相冊模块仅仅与用户模块存在通过用户的关联。这两个模块之间的关联基本就有通过用户id关联的内容。简单清晰,接口明白;
◆ 事件模块与各个模块可能都有关联,可是都仅仅关注其各个模块中对象的ID信息,相同能够做到非常easy分拆。
所以。我们第一步能够将数据库依照功能模块相关的表进行一次垂直拆分。每一个模块所涉及的表单独到一个数据库中,模块与模块之间的表关联都在应用系统端通过藉口来处理。例如以下图所看到的:
★ mycat 数据整合:具体http://www.songwie.com/articlelist/11
★ 其它实现数据切分及整合的解决方式
除了上面介绍的几个数据切分及整合的总体解决方式之外,还存在非常多其它相同提供了数据切分与整合的解决方式。如基于MySQLProxy的基础上做了进一步扩展的HSCALE,通过Rails构建的SpockProxy。以及基于Pathon的Pyshards等等。
无论大家选择使用哪一种解决方式,总体设计思路基本上都不应该会有不论什么变化。那就是通过数据的垂直和水平切分,增强数据库的总体服务能力,让应用系统的总体扩展能力尽可能的提升。扩展方式尽可能的便捷。
仅仅要我们通过中间层Proxy应用程序较好的攻克了数据切分和数据源整合问题。那么数据库的线性扩展能力将非常easy做到像我们的应用程序一样方便。仅仅须要通过加入便宜的PCServerserver,就可以线性添加数据库集群的总体服务能力,让数据库不再轻易成为应用系统的性能瓶颈。
这里。大家应该对数据切分与整合的实施有了一定的认识了。也许非常多读者朋友都已经依据各种解决方式各自特性的优劣基本选定了适合于自己应用场景的方案,后面的工作主要就是实施准备了。
在实施数据切分方案之前,有些可能存在的问题我们还是须要做一些分析的。
一般来说,我们可能遇到的问题主要会有以下几点:
◆ 引入分布式事务的问题。
◆ 跨节点Join的问题;
◆ 跨节点合并排序分页问题。
1. 引入分布式事务的问题
一旦数据进行切分被分别存放在多个MySQLServer中之后,无论我们的切分规则设计的多么的完美(实际上并不存在完美的切分规则),都可能造成之前的某些事务所涉及到的数据已经不在同一个MySQLServer中了。
在这样的场景下,假设我们的应用程序仍然依照老的解决方式。那么势必须要引入分布式事务来解决。而在MySQL各个版本号中,仅仅有从MySQL5.0開始以后的各个版本号才開始对分布式事务提供支持,并且眼下仅有Innodb提供分布式事务支持。不仅如此。即使我们刚好使用了支持分布式事务的MySQL版本号。同一时候也是使用的Innodb存储引擎,分布式事务本身对于系统资源的消耗就是非常大的,性能本身也并非太高。并且引入分布式事务本身在异常处理方面就会带来较多比較难控制的因素。
怎么办?事实上我们能够能够通过一个变通的方法来解决这样的问题。首先须要考虑的一件事情就是:是否数据库是唯一一个能够解决事务的地方呢?事实上并非这样的,我们全然能够结合数据库以及应用程序两者来共同解决。各个数据库解决自己身上的事务。然后通过应用程序来控制多个数据库上面的事务。
也就是说。仅仅要我们愿意。全然能够将一个跨多个数据库的分布式事务分拆成多个仅处于单个数据库上面的小事务。并通过应用程序来总控各个小事务。
当然,这样作的要求就是我们的俄应用程序必须要有足够的健壮性。当然也会给应用程序带来一些技术难度。
2.跨节点Join的问题
上面介绍了可能引入分布式事务的问题,如今我们再看看须要跨节点Join的问题。
数据切分之后。可能会造成有些老的Join语句无法继续使用。由于Join使用的数据源可能被切分到多个MySQLServer中了。
怎么办?这个问题从MySQL数据库角度来看,假设非得在数据库端来直接解决的话,恐怕仅仅能通过MySQL一种特殊的存储引擎Federated来攻克了。Federated存储引擎是MySQL解决相似于Oracle的DBLink之类问题的解决方式。
和OracleDBLink的主要差别在于Federated会保存一份远端表结构的定义信息在本地。咋一看,Federated确实是解决跨节点Join非常好的解决方式。可是我们还应该清晰一点,那就似乎假设远端的表结构发生了变更,本地的表定义信息是不会跟着发生对应变化的。假设在更新远端表结构的时候并没有更新本地的Federated表定义信息。就非常可能造成Query执行出错,无法得到正确的结果。
对待这类问题,我还是推荐通过应用程序来进行处理,先在驱动表所在的MySQLServer中取出对应的驱动结果集。然后依据驱动结果集再到被驱动表所在的MySQLServer中取出对应的数据。可能非常多读者朋友会觉得这样做对性能会产生一定的影响,是的,确实是会对性能有一定的负面影响,可是除了此法,基本上没有太多其它更好的解决的方法了。
并且,由于数据库通过较好的扩展之后,每台MySQLServer的负载就能够得到较好的控制。单纯针对单条Query来说,其响应时间可能比不切分之前要提高一些,所以性能方面所带来的负面影响也并非太大。更何况。相似于这样的须要跨节点Join的需求也并非太多。相对于总体性能而言,可能也仅仅是非常小一部分而已。所以为了总体性能的考虑,偶尔牺牲那么一点点。事实上是值得的。毕竟系统优化本身就是存在非常多取舍和平衡的过程。
3. 跨节点合并排序分页问题
一旦进行了数据的水平切分之后,可能就并不仅仅仅仅有跨节点Join无法正常执行,有些排序分页的Query语句的数据源可能也会被切分到多个节点。这样造成的直接后果就是这些排序分页Query无法继续正常执行。事实上这和跨节点Join是一个道理。数据源存在于多个节点上,要通过一个Query来解决,就和跨节点Join是一样的操作。相同Federated也能够部分解决。当然存在的风险也一样。
还是相同的问题,怎么办?我相同仍然继续建议通过应用程序来解决。
怎样解决?解决的思路大体上和跨节点Join的解决相似,可是有一点和跨节点Join不太一样。Join非常多时候都有一个驱动与被驱动的关系。所以Join本身涉及到的多个表之间的数据读取一般都会存在一个顺序关系。可是排序分页就不太一样了,排序分页的数据源基本上能够说是一个表(或者一个结果集)。本身并不存在一个顺序关系,所以在从多个数据源取数据的过程是全然能够并行的。
这样。排序分页数据的取数效率我们能够做的比跨库Join更高。所以带来的性能损失相对的要更小,在有些情况下可能比在原来未进行数据切分的数据库中效率更高了。
当然,不论是跨节点Join还是跨节点排序分页。都会使我们的应用server消耗很多其它的资源,尤其是内存资源,由于我们在读取訪问以及合并结果集的这个过程须要比原来处理很多其它的数据。
分析到这里,可能非常多读者朋友会发现,上面全部的这些问题,我给出的建议基本上都是通过应用程序来解决。大家可能心里開始犯嘀咕了。是不是由于我是DBA,所以就非常多事情都扔给应用架构师和开发人员了?
事实上全然不是这样,首先应用程序由于其特殊性。能够非常easy做到非常好的扩展性,可是数据库就不一样。必须借助非常多其它的方式才干做到扩展。并且在这个扩展过程中,非常难避免带来有些原来在集中式数据库中能够解决但被切分开成一个数据库集群之后就成为一个难题的情况。
要想让系统总体得到最大限度的扩展,我们仅仅能让应用程序做很多其它的事情。来解决数据库集群无法较好解决的问题。
通过数据切分技术将一个大的MySQLServer切分成多个小的MySQLServer,既攻克了写入性能瓶颈问题,同一时候也再一次提升了整个数据库集群的扩展性。不论是通过垂直切分,还是水平切分。都能够让系统遇到瓶颈的可能性更小。尤其是当我们使用垂直和水平相结合的切分方法之后,理论上将不会再遇到扩展瓶颈了。