1.基本思想Sharding的基本思想是将一个数据库分成多个部分,分别放在不同的数据库(服务器)上,从而缓解单个数据库的性能问题。不严格的说,对于一个海量数据的数据库,如果表很多,数据很多,这时候适合使用垂直切分,也就是把关系密切(比如同一个模块)的表划分出来,放到他们在服务器上。如果表不多,但是每个表的数据很多,这时候适合做水平切分,即将表的数据按照一定的规则(比如哈希)切分到多个数据库(服务器)按身份证)。当然,在现实中,这两种情况更多的是混合在一起。这时候就需要根据实际情况做出选择。也可以结合使用垂直和水平切分,将原始数据库分成相似的矩阵。***扩展数据库(服务器)阵列。下面详细介绍垂直切分和水平切分。垂直切分的最大特点是规则简单,实施起来也比较方便。特别适用于服务之间耦合度很低,相互影响很小,逻辑性很强的系统。在本系统中,很容易将不同业务模块使用的表拆分到不同的数据库中。按照不同的表拆分对应用的影响更小,拆分规则也会更简单明了。(这就是所谓的“什么都不分享”)。水平拆分相对于垂直拆分要复杂一些。因为需要将同一张表的不同数据拆分到不同的数据库中,对于应用来说,拆分规则本身比根据表名拆分要复杂,后期的数据维护也会更加复杂。我们从一般情况考虑数据切分:一方面,一个库中的所有表通常不可能被某张表串联起来。这句话的意思是,水平切分几乎都是针对一个小搓的紧密相关的表进行的(实际上是垂直切出的块),不可能对所有的表都进行。另一方面,对于一些负载非常高的系统,即使是单个表也无法通过单个数据库主机来承担其负载,这意味着仅靠垂直切分并不能完全解决问题。因此,大部分系统都会采用垂直切分和水平切分相结合的方式,先对系统进行垂直切分,然后针对每一种小表摩擦情况,选择性地进行水平切分。这样,整个数据库就被划分成了一个分布式矩阵。2.切分策略如上所述,切分是按照先垂直切分,再水平切分的步骤进行的。垂直分割的结果只是为水平分割铺平了道路。垂直切分的思想是分析表之间的聚合关系,将关系密切的表放在一起。在大多数情况下,它可能是同一个模块,或者同一个“聚合体”。这里的“聚合”正是领域驱动设计中所说的聚合。在垂直拆分表聚合中,找到“根元素”(这里的“根元素”是领域驱动设计中的“聚合根”),根据“根元素”进行水平切分,即从“根元素”Element,将所有与其直接和间接相关的数据都放到一个shard中。这样跨shard关联的可能性很小。应用程序不需要打破表之间已有的关联举个例子:对于社交网站来说,几乎所有的数据最终都会和某个用户相关联,基于用户的切分是最好的选择。再比如论坛系统,用户和论坛这两个模块应该分为两个垂直切分时的shards。对于forum模块来说,Forum显然是聚合根,所以水平切分是按照Forum进行的,所有的帖子和回复自然是和Forum一起放在一个shard中。对于sh数据,如果是只读的字典表,在每个分片中维护一个副本应该是个不错的选择,以免关系中断。如果是一般数据之间的跨节点关联,则必须打断。需要注意的是,当同时进行垂直和水平分割时,分割策略会发生一些细微的变化。例如:当只考虑垂直切分时,分在一起的表之间可以保持任意关联,所以可以按“功能模块”来分表,但是一旦引入水平切分,表之间的关联就会大大增加有限制的,通常只允许一个主表(通过表ID散列的表)和它的多个从表保留关联关系,也就是说:当同时进行纵横切分时,切分在垂直方向将不再以“功能模块”来划分,而是需要更细粒度的垂直切分,而这种粒度与领域驱动设计中“聚合”的概念不谋而合,甚至可以完全一致,主表每个分片的是聚合中的聚合根!这样拆分后会发现数据库分区过于分散(分片数量会多一些,但是分片中的表并不多),为了避免管理过多的数据源,充分利用每个数据库服务器的资源,可以考虑两个或多个业务相似,数据增长率相近(主表数据量在同一个数量级)的数据库。将多个分片放到同一个数据源中,每个分片仍然是独立的。他们有自己的主表,并使用各自的主表ID进行散列。唯一的区别是它们的哈希模数(即节点数)要求是一致的。1.事务问题事务问题目前有两种可行的解决方案:分布式事务和通过应用程序和数据库的联合控制来实现事务。下面对两种解决方案进行简要比较。方案一:使用分布式事务优点:由数据库管理,简单有效缺点:性能开销大,尤其是分片越来越多的时候方案二:应用程序和数据库共同控制单一数据库,每个小事务都由应用程序控制。优点:性能上的优势缺点:应用在事务控制上需要灵活设计。如果使用spring的事务管理,改起来会面临一定的困难。2、跨节点Join的问题只要进行了切分,就不可避免地会出现跨节点Join的问题。但良好的设计和细分可以减少此类情况的发生。解决这个问题的常用方法是在两个查询中实现它。在第一次查询的结果集中找到关联数据的id,根据这些id发起第二次请求获取关联数据。3.跨节点计数,orderby,groupby和聚合函数问题是一类问题,因为它们都需要基于整个数据集进行计算。大多数代理不会自动处理合并。解决方案:与解决跨节点join问题类似,在每个节点上得到结果后,在应用端合并结果。与join不同的是每个节点的查询可以并行执行,所以很多时候它的速度比单个大表要快很多。但是如果结果集很大,申请内存的消耗是个问题。关于VerticalSharding的粒度垂直分片的粒度是指在做垂直分片的时候,允许将多级关联表放在一个shard中。这个问题对应用和分片实现影响很大。中断越多,影响的join操作越多,应用做出的妥协就越大,但单表的路由会更简单,与业务的相关性越低,越容易使用统一的机制加工。这个方向的极端解决方案是:中断所有连接,每个表都配备路由规则,可以使用统一的机制或框架自动处理。比如amoeba这样的框架,它的路由可以也只能通过SQL特性(比如某个表的id)进行路由。反之,关联中断越少,对join操作的限制就会越小,应用需要做出的妥协也会越小,但表的路由会变得越复杂,越是与业务相关,越是难以使用统一的机制来处理。需要为每个数据请求分别实现路由。这个方向的极端解决方案是:所有表都在一个shard中,即没有垂直切分,这样关联就不会中断。当然,这是很极端的,除非整个数据库非常简单,表的数量很少。实际的粒度控制需要结合“业务紧密度”和“表数据量”两个因素综合考虑,一般来说:如果分组在一起的表关系密切,数据量不大,增长速度快很慢,放在一个shard里面比较合适,不需要再水平切分;如果表分组在一起,表数据量巨大,增长速度很快,那么需要在垂直切分的基础上进行水平切分。水平切分是指将原来的单个分片细分为多个更小的分片。分片有一个主表(也就是会用表ID哈希的表)和多个关联的关联表。总之,垂直细分的粒度呈现出优势与劣势在两个相反的方向上并存、相互竞争的局面。建筑师需要做的是结合项目的实际情况,在两者之间取得利益最大化的平衡。参考资料:《MySQL性能调优与架构设计》注:本文图片摘自本书《MySQL性能调优与架构设计》
