面试的时候,聊到高并发或者大数据,很多时候会聊到数据库分库分表相关的问题,因为你的数据库单机抗不了多少并发量,而且用户量上来之后,数据库容纳的数据量也是有限的。
如果单表数据量过大,SQL稍微复杂点,查询就会很慢。而且,现在稍微大点的互联网公司,分库分表都成为了标配。如果你现在出去面试,面试官问你分库分表相关的问题,你说你没做过,人家立马会觉得你没有高并发的经验,做的都是比较简单的业务系统。
MySQL单表通常500w条数据以内比较合适,不建议超过1000w,如果超过1000w了建议要做分库分表了。
比如常见的分库分表面试题:
- 你们具体是如何对数据库进行进行垂直拆分或水平拆分的?
一般开发一个新业务系统,由于需要快速打样,尽快上线,所以一开始基本都是单库系统。可能业务发展迅猛,过了几个月,使用用户就达到了 1000 万!每天活跃用户数 100 万!每天单表数据量 10 万条!高峰期每秒最大请求达到 1000QPS!
现在大家感觉压力已经有点大了,为啥呢?因为每天多 10 万条数据,一个月就新增300 万条数据,现在咱们单表已经几百万数据了,马上就破千万了。目前用户量还在不断增长,每天新增的数据量也在不断变多,照目前这个势头,系统恐怕坚持不了多久。业务系统倒是可以很容易的增加一些机器。但大多数请求集中在20%的时间,80%的时间请求量还可以支撑。这20%时间段里,每秒并发量和在线用户量都达到峰值,对数据库的压力也是每天最大的时候。这时候,你可以在业务系统与MySQL数据库中间加一个MQ削峰,比如使用kafka,缓解一下过高的并发请求。假如高峰期每秒8000个请求,异步写系统每秒消费2000个请求。经过MQ削峰后,会在消息队列里缓存很多未执行的数据库操作,等待异步写系统慢慢的消费掉。业务系统能够增加机器扩容,没什么问题,MQ削峰也能撑一撑,但瓶颈在于MySQL。主要有3个问题:如果要让MySQL承担更高的并发,比如现在是8000请求/s,异步写系统也可以扩容到多台机器,MQ消费6000请求/s,这时候该怎么办?你首先得分库。假如现在分了3个库,每个库的表和表结构都是一模一样的,MQ分为3个partition,每个异步写系统都只消费一个partition。每个异步写系统,会根据每条数据的某个id分发到各个数据库里去,比如是userId,每个userId相同的数据分发到同一台机器上去。
分库前单库每天可能增加100w数据,现在每个库增加30多万条数据。数据库可以承受的并发增加了3倍,数据库的磁盘使用率大大降低,本来一个库磁盘很快就写满了,现在大大降低了,同时SQL语句执行性能也提高了。分库之后,每个表的数据库依旧很多,SQL语句执行起来性能依旧不高,所以还是要考虑分表,打造多库多表的系统。
比如你单表都几千万数据了,你确定你能扛住么?肯定不行,单表数据量太大,会极大影响你的SQL执行的性能,大量的连接卡在MySQL等待执行,不仅会把你MySQL数据库拖垮,还会产生连锁反应,把你的业务系拖垮。一般来说,单表到几百万的时候,性能就会越来越差,你就得分表了。就是把一个表的数据放到多个表中,然后查询的时候你就查一个表。比如按照用户 id 来分表,将一个用户的数据就放在一个表中。然后操作的时候你对一个用户就操作那个表就好了。这样可以控制每个表的数据量在可控的范围内,比如每个表就固定在500万以内。分库是啥意思?就经验而言,单库最多支撑到并发2000,一定要扩容了,而且一个健康的单库并发值最好保持在每秒1000以内,不要太大。那么你可以将一个库的数据拆分到多个库中,访问的时候就访问一个库好了。- ShardingSphere (Sharding-jdbc)
Sharding-Sphere是一套开源的分布式数据库中间件解决方案,属于client端方案,也就是你的业务系统只需要引用它的jar包,就可以使用了。Sharding-Sphere目前社区也还一直在开发和维护,还算是比较活跃,个人认为算是一个现在也可以选择的方案。mycat是基于Cobar改造的,属于 proxy 层方案,支持的功能非常完善,而且目前应该是非常火的而且不断流行的数据库中间件,社区很活跃,也有一些公司开始在用了。但是确实相比于 Sharding jdbc 来说,年轻一些,经历的锤炼少一些。Sharding-Sphere这种client端方案的优点在于不用部署,运维成本低,不需要代理层的二次转发请求,性能很高,但是如果遇到升级啥的需要各个业务系统都重新升级版本再发布,各个系统都需要耦合Sharding-Sphere即可。Mycat 这种proxy层方案的缺点在于需要单独部署,自己运维一套中间件,运维成本高,但是好处在于对于各个项目是透明的,如果需要升级只需要单独升级mycat就行了。通常来说,这两个方案其实都可以选用,但是个人建议中小型公司选用 Sharding-Sphere,client 层方案轻量级,而且维护成本低,不需要额外增派人手去维护,而且中小型公司系统复杂度会低一些,项目也没那么多。但是中大型公司最好还是选用Mycat这类proxy层方案,因为可能大公司系统和项目非常多,团队很大,人员充足,那么最好是专门弄个人来研究和维护Mycat,然后各个项目直接透明使用即可。
你们是如何对数据库进行垂直拆分或水平拆分的?
水平拆分的意思,就是把一个表的数据给弄到多个库的多个表里去,但是每个库的表结构都一样,只不过每个库表放的数据是不同的,所有库表的数据加起来就是全部数据。水平拆分的意义,就是将数据均匀放更多的库里,然后用多个库来扛更高的并发,还有就是用多个库的存储容量来进行扩容。垂直拆分,就是把一个有很多字段的表给拆分成多个表,或者是多个库上去。每个库表的结构都不一样,每个库表都包含部分字段。一般来说,会将访问频率很高的字段放到一个表里去,然后将访问频率很低的字段放到另外一个表里去。因为数据库是有缓存的,你访问频率高的行字段越少,就可以在缓存里缓存更多的行,性能就越好。这个一般在表层面做的较多一些。假如有600w数据,现在要分库分表,综合来看分库分表可能是这样的:常见的分库分表是,是根据某个id取模先定位到库,再定位到表的。可以根据userId和orderId取模。也可以根据数据的range去分库分表,比如根据数据的创建时间。引入了分库分表中间件,那么我们的系统就不用自己考虑每条数据路由到哪个库哪张表了。就可以直接将SQL丢给分库分表中间件,由它根据配置,路由到相应的库和表里去,此时MQ和异步写系统也可以去掉了,因为分库分表后,相当于每个业务系统承担的压力就大大减小了。
本文分享了分库分表的由来,业务不断发展的驱动下,改造系统分库分表是架构升级的必经之路。同时讲了业内常用的分库分表中间件ShardingSphere和Mycat以及他们的优缺点,最后分享了如何对数据库进行垂直拆分和水平拆分,以及具体分库分表数据路由方法。
有道无术,术可成;有术无道,止于术
欢迎大家关注Java之道公众号
好文章,我在看❤️