简介当前流行微服务和分布式,高并发、大数据量、分库分表等在中高级Java工程师的面试题中已经成为high-访谈中的频率词汇。明白面试通过率不会太高。您可能不知道如何使用它,但您必须知道这就是现状。大多数技术术语晦涩难懂。最重要的是不要死记硬背。戳开窗纸,你会发现其实是一样的。一、为什么要分库分表关系型数??据库以MySQL为例,单机的存储容量和连接数是有限的,很容易成为系统的瓶颈。当单表数据量超过百万里时,我们还可以通过增加从库和优化索引来提升性能。一旦数据量向千万级以上增长,无论你如何优化数据库,很多操作的性能还是会严重下降。为了减轻数据库的负担,提高数据库的响应速度,缩短查询时间,这时候就需要分库分表了。2、如何分库分表分库分表就是将大量的数据分散到多个数据库中,使得每个数据库中的数据量小,响应速度快,从而提高整体数据库的性能。核心思想是对数据进行分片,以及分片后如何快速定位和整合数据。对于数据切分的类型,大致可以分为垂直(纵向)切分和水平(水平)切分两种。1.垂直切分垂直切分又分为垂直分库和垂直表分库。垂直分库是基于业务分类的。和我们经常听到的微服务治理的概念很像。它有自己的数据库,需要通过接口调用不同业务的数据。垂直分库也是按照业务分类划分的,每个业务都有一个独立的数据库,更容易理解。垂直分表垂直分表是基于数据表的列。是一种将大表拆分成小表的模式。例如:一个订单表有很多字段,将长度大、访问不频繁的字段拆分出来,创建一个单独的扩展表work_extend进行存储。ordertable:idworkNopricedescribe...int(12)int(2)int(15)varchar(2000)拆分后的订单核心表:idworkNoprice...int(12)int(2)int(15)work_extendtable:idworkNodescribe…int(12)int(2)varchar(2000)数据库以行为单位加载数据到内存,所以拆分后,核心表大部分是访问频率高的字段,字段length也短,可以将更多的数据加载到内存中,提高查询的命中率,减少磁盘IO,从而提高数据库性能。优点:业务之间解耦,不同业务的数据独立维护、监控、扩容。在高并发场景下,一定程度上缓解了数据库的压力。缺点:增加了开发的复杂度。由于业务隔离,很多表无法直接访问,必须通过接口聚合数据。分布式事务管理的难度增加。数据库仍然存在单表数据量过大的问题,没有得到根本解决。需要配合水平切分2.水平切分。仍然存在单表数据过多的问题。当我们的应用已经无法做到垂直细粒度的时候,单库的读写和存储性能仍然存在瓶颈。这时候就需要配合横向切分。Horizo??ntalsharding将一个数据量较大的表切分为结构相同的多个表,每个表只占用原表中的一部分数据,然后根据不同的情况分布到多个数据库中。假设一张order表有2000万条数据,水平切分后生成四张表,order_1、order_2、order_3、order_4,每张表有500万条数据,依次类推。order_1表:idworkNopricedescribe...int(12)int(2)int(15)varchar(200order_2table:idworkNopricedescribe...int(12)int(2)int(15)varchar(200order_3表:idworkNopricedescribe...int(12)int(2)int(15)varchar(200order_4table:idworkNopricedescribe...int(12)int(2)int(15)varchar(200水平分割并分为库表和分库分表分库虽然分表,但是分表还是在同一个数据库实例中,只是解决了单表数据量过大的问题,并没有分表。分布在不同机器上的库还在争夺同一台物理机的CPU、内存、网络IO。表的数据量变小,达到分布式的效果。优点:解决高并发时单个数据库数据量过大的问题,提高系统稳定性和负载能力。业务系统改造的工作量不是很大。缺点:难以保证跨分片交易的一致性。跨库join关联查询性能比较低扩展和维护难度比较大,(拆分成几千个子表想想都吓人)3.数据应该存放在哪个数据库表?分库分表后,就会出现问题。一个表会出现在多个数据库中。它应该存储在哪个数据库表中?1、按照时间间隔或ID间隔按取值范围进行分段。例如:如果我们对user表进行分片,我们可以定义每个库的User表只存储10000条数据,第一库的userId从1~9999,第二库10000~20000,第三行20001~30000...等等。优点:单表数据量可控,横向扩展简单。只需要增加节点,不需要从其他分片迁移数据。可以快速定位到要查询的数据在哪个数据库中。缺点:由于连续分片,可能会出现数据热点。如果按时间字段分片,有的分片存储的是最近一个时间段的数据,可能会被频繁读写,而有的分片存储的是历史数据,很少被查询到2.Hashmodulohashmodulo(取hash的余数result(hash()modN))比较常见。以User表为例,将数据库编号从0到N-1,对User表中的userId字段取模得到余数i,i=0存入第一个库,i=1为存入第二个库,i=2存入第三个库……以此类推。这样,同一个用户的数据就会存储在同一个库中,使用userId作为条件查询是一个很好的定位。优点:数据分片比较均匀,不易并发访问某个库。缺点:但是该算法存在一些问题,当某台机器宕机时,本应落在数据库中的请求无法正确处理,而宕机的实例会被踢出集群。这时候算法就变成了hash(userId)modN-1,User信息可能已经不在同一个repository中了。4、分库分表后会有哪些坑?1、事务一致性问题由于表分布在不同的数据库中,必然会出现跨库事务问题。一般可以采用“XA协议”和“两阶段提交”进行处理,但这种方式性能较差,代码开发量也比较大。通常的做法是实现最终一致性方案,往往对系统的实时一致性要求不高,只要在允许的时间段内实现最终一致性,就可以使用事务补偿。2.分页排序的坑分页排序是日常开发中必备的功能,但是在查询多个数据库的时候限制分页排序真的很麻烦。分页需要根据指定的字段进行排序。如果排序字段恰好是分片字段,则很容易通过分片规则定位到分片位置;一旦排序字段不是分片字段,就需要在不同的分片节点对数据进行排序返回,然后将不同分片返回的结果集进行汇总重新排序,最后返回给用户。过程比较复杂。3.全局唯一主键问题。数据库分表后,表中的数据同时存在于多个数据库中,分区数据库的自增主键不再满足全局唯一性。因此,此时一个能够生成全球唯一ID的系统就非常有必要了。那么这个全球唯一的ID就叫做分布式ID。可以参考我之前写的这篇文章《一口气说出 9种 分布式ID生成方式,面试官有点懵了》5。分库分表的工具?自己开发分库分表工具工作量巨大。好在业界已经有很多成熟的分库分表中间件。我们可以花更多的时间在业务实施上。(蘑菇街)Atlas(Qihoo360)Cobar(Alibaba)MyCAT(基于Cobar)Oceanus(58.com)Vitess(Google)
