1、前言mysql
中大型项目中,一旦遇到数据量比较大,小伙伴应该都知道就应该对数据进行拆分了。有垂直和水平两种。算法
垂直拆分比较简单,也就是原本一个数据库,数据量大以后,从业务角度进行拆分多个库。以下图,独立的拆分出订单库和用户库。spring
水平拆分的概念,是同一个业务数据量大以后,进行水平拆分。sql
上图中订单数据达到了4000万,咱们也知道mysql单表存储量推荐是百万级,若是不进行处理,mysql单表数据太大,会致使性能变慢。使用方案能够参考数据进行水平拆分。把4000万数据拆分4张表或者更多。固然也能够分库,再分表;把压力从数据库层级分开。数据库
2、分库分表方案 缓存
在咱们设计系统以前,能够先预估一下大概这几年的订单量,如:4000万。每张表咱们能够容纳1000万,也咱们能够设计4张表进行存储。服务器
那具体如何路由存储的呢?hash的方案就是对指定的路由key(如:id)对分表总数进行取模,上图中,id=12的订单,对4进行取模,也就是会获得0,那此订单会放到0表中。id=13的订单,取模获得为1,就会放到1表中。为何对4取模,是由于分表总数是4。jvm
优势:分布式
订单数据能够均匀的放到那4张表中,这样此订单进行操做时,就不会有热点问题。函数
热点的含义:热点的意思就是对订单进行操做集中到1个表中,其余表的操做不多。
订单有个特色就是时间属性,通常用户操做订单数据,都会集中到这段时间产生的订单。若是这段时间产生的订单 都在同一张订单表中,那就会造成热点,那张表的压力会比较大。
缺点:
未来的数据迁移和扩容,会很难。
如:业务发展很好,订单量很大,超出了4000万的量,那咱们就须要增长分表数。若是咱们增长4个表
一旦咱们增长了分表的总数,取模的基数就会变成8,之前id=12的订单按照此方案就会到4表中查询,但以前的此订单时在0表的,这样就致使了数据查不到。就是由于取模的基数产生了变化。
遇到这个状况,咱们小伙伴想到的方案就是作数据迁移,把以前的4000万数据,从新作一个hash方案,放到新的规划分表中。也就是咱们要作数据迁移。这个是很痛苦的事情。有些小公司能够接受晚上停机迁移,但大公司是不容许停机作数据迁移的。
固然作数据迁移能够结合本身的公司的业务,作一个工具进行,不过也带来了不少工做量,每次扩容都要作数据迁移
那有没有不须要作数据迁移的方案呢,咱们看下面的方案
range方案也就是以范围进行拆分数据。
range方案比较简单,就是把必定范围内的订单,存放到一个表中;如上图id=12放到0表中,id=1300万的放到1表中。设计这个方案时就是前期把表的范围设计好。经过id进行路由存放。
优势
咱们小伙伴们想一下,此方案是否是有利于未来的扩容,不须要作数据迁移。即时再增长4张表,以前的4张表的范围不须要改变,id=12的仍是在0表,id=1300万的仍是在1表,新增的4张表他们的范围确定是 大于 4000万以后的范围划分的。
缺点
有热点问题,咱们想一下,由于id的值会一直递增变大,那这段时间的订单是否是会一直在某一张表中,如id=1000万 ~ id=2000万之间,这段时间产生的订单是否是都会集中到此张表中,这个就致使1表过热,压力过大,而其余的表没有什么压力。
hash取模方案:没有热点问题,但扩容迁移数据痛苦
range方案:不须要迁移数据,但有热点问题。
那有什么方案能够作到二者的优势结合呢?,即不须要迁移数据,又能解决数据热点的问题呢?
其实还有一个现实需求,可否根据服务器的性能以及存储高低,适当均匀调整存储呢?
3、方案思路
hash是能够解决数据均匀的问题,range能够解决数据迁移问题,那咱们能够不能够二者相结合呢?利用这二者的特性呢?
咱们考虑一下数据的扩容表明着,路由key(如id)的值变大了,这个是必定的,那咱们先保证数据变大的时候,首先用range方案让数据落地到一个范围里面。这样之后id再变大,那之前的数据是不须要迁移的。
但又要考虑到数据均匀,那是否是能够在必定的范围内数据均匀的呢?由于咱们每次的扩容确定会事先设计好此次扩容的范围大小,咱们只要保证此次的范围内的数据均匀是否是就ok了。
4、方案设计
咱们先定义一个group组概念,这组里面包含了一些分库以及分表,以下图
上图有几个关键点:
1)id=0~4000万确定落到group01组中
2)group01组有3个DB,那一个id如何路由到哪一个DB?
3)根据hash取模定位DB,那模数为多少?模数要为全部此group组DB中的表数,上图总表数为10。为何要去表的总数?而不是DB总数3呢?
4)如id=12,id%10=2;那值为2,落到哪一个DB库呢?这是设计是前期设定好的,那怎么设定的呢?
5)一旦设计定位哪一个DB后,就须要肯定落到DB中的哪张表呢?
5、核心主流程
按照上面的流程,咱们就能够根据此规则,定位一个id,咱们看看有没有避免热点问题。
咱们看一下,id在【0,1000万】范围内的,根据上面的流程设计,1000万之内的id都均匀的分配到DB_0,DB_1,DB_2三个数据库中的Table_0表中,为何能够均匀,由于咱们用了hash的方案,对10进行取模。
上面咱们也提了疑问,为何对表的总数10取模,而不是DB的总数3进行取模?咱们看一下为何DB_0是4张表,其余两个DB_1是3张表?
在咱们安排服务器时,有些服务器的性能高,存储高,就能够安排多存放些数据,有些性能低的就少放点数据。若是咱们取模是按照DB总数3,进行取模,那就表明着【0,4000万】的数据是平均分配到3个DB中的,那就不可以实现按照服务器能力适当分配了。
按照Table总数10就可以达到,看如何达到
上图中咱们对10进行取模,若是值为【0,1,2,3】就路由到DB_0,【4,5,6】路由到DB_1,【7,8,9】路由到DB_2。如今小伙伴们有没有理解,这样的设计就能够把多一点的数据放到DB_0中,其余2个DB数据量就能够少一点。DB_0承担了4/10的数据量,DB_1承担了3/10的数据量,DB_2也承担了3/10的数据量。整个Group01承担了【0,4000万】的数据量。
注意:小伙伴千万不要被DB_1或DB_2中table的范围也是0~4000万疑惑了,这个是范围区间,也就是id在哪些范围内,落地到哪一个表而已。
上面一大段的介绍,就解决了热点的问题,以及能够按照服务器指标,设计数据量的分配。
6、如何扩容
其实上面设计思路理解了,扩容就已经出来了;那就是扩容的时候再设计一个group02组,定义好此group的数据范围就ok了。
由于是新增的一个group01组,因此就没有什么数据迁移概念,彻底是新增的group组,并且这个group组照样就防止了热点,也就是【4000万,5500万】的数据,都均匀分配到三个DB的table_0表中,【5500万~7000万】数据均匀分配到table_1表中。
7、系统设计
思路肯定了,设计是比较简单的,就3张表,把group,DB,table之间创建好关联关系就好了。
group和DB的关系
table和db的关系
上面的表关联实际上是比较简单的,只要原理思路理顺了,就ok了。小伙伴们在开发的时候不要每次都去查询三张关联表,能够保存到缓存中(本地jvm缓存),这样不会影响性能。
一旦须要扩容,小伙伴是否是要增长一下group02关联关系,那应用服务须要从新启动吗?
简单点的话,就凌晨配置,重启应用服务就好了。但若是是大型公司,是不容许的,由于凌晨也有订单的。那怎么办呢?本地jvm缓存怎么更新呢?
其实方案也不少,可使用用zookeeper,也可使用分布式配置,这里是比较推荐使用分布式配置中心的,能够将这些数据配置到分布式配置中心去
到此为止,总体的方案介绍结束,但愿对小伙伴们有所帮助。谢谢!!!
这边隐含了一个关键点,那就是路由key(如:id)的值是很是关键的,要求必定是有序的,自增的,这个就涉及到分布式惟一id的方案.
出处:https://mp.weixin.qq.com/s/pGcDXG6kS3U86HZz7K5fgg
简略版分析分库分表:
分库分表,作到永不迁移数据和避免热点的方法: 基础: 1、数据拆分方式:垂直拆分,水平拆分。 2、垂直拆分:原来就一个数据库,数据量一大了,就拆分为多个数据库。 3、水平拆分:原来是t_order表,拆分红t_order_一、t_order_二、t_order_三、t_order_4。 4、Mysql单表存储推荐是百万级,尽可能别到千万级。 分库分表方案: 1、经常使用的方案:hash取模、range范围方案,分库分表方案最主要的就是路由算法,把路由的key按照指定的算法进行路由存放。 2、hash取模方案(无热点问题,扩容困难) (一)、首先预估一下总数据量m,设定每张表的最大数据量n,m/n=z 是表个数。 (二)、hash方案就是对指定的路由key(如:id)对z进行取模,"t_order_"+id%z 是表名字。 (三)、优缺点:优势是数据均匀的分布在每张表中,不会有热点问题;缺点是数据的迁移或者扩容会很麻烦。 3、range范围方案(不须要迁移数据,有热点问题) (一)、range方案是以范围进行数据拆分:id=50在0~1000的在t_order_0表、id=1050在1000~2000的t_order_1表等。 (二)、优缺点:优势是扩容方便,不须要数据迁移;缺点是id在0-1000时,t_order_0会很忙,t_order_1,t_order_2...t_order_n...都没有数据的访问,一段时间只有一个热点表。 4、range和hash组合方案 (一)、设计是比较简单的,就三张表,把group,db,table之间创建好关联关系。 group表字段:group_id,group_name,start_id,end_id db表字段:db_id,db_name,group_id,hash_value group和db的关系表字段:table_id,table_name,db_id,start_id,end_id 分库分表就会带来各类join组合条件的分页查询问题,怎样解决分页查询问题,颇有挑战性。 1.事务问题: (1)、分布式事务 (2)、经过应用程序与数据库共同控制实现事务 方案一:使用分布式事务 优势:交由数据库管理,简单有效 缺点:性能代价高,特别是shard(分片)愈来愈多时 方案二:由应用程序和数据库共同控制 原理:将一个跨多个数据库的分布式事务分拆成多个仅处 于单个数据库上面的小事务,并经过应用程序来总控 各个小事务。 优势:性能上有优点 缺点:须要应用程序在事务控制上作灵活设计。若是使用 了spring的事务管理,改动起来会面临必定的困难。 2.跨节点Join的问题 只要是进行切分,跨节点Join的问题是不可避免的。可是良好的设计和切分却能够减小此类状况的发生。解决这一问题的广泛作法是分两次查询实现。在第一次查询的结果集中找出关联数据的id,根据这些id发起第二次请求获得关联数据。 3.跨节点的count,order by,group by以及聚合函数问题 解决方案:与解决跨节点join问题的相似,分别在各个节点上获得结果后在应用程序端进行合并。和join不一样的是每一个结点的查询能够并行执行,所以不少时候它的速度要比单一大表快不少。但若是结果集很大,对应用程序内存的消耗是一个问题。