当前位置: 首页 > 后端技术 > Java

这么好的系统,为什么还要分库分表呢?

时间:2023-04-01 22:32:08 Java

大家好,我是小芙~今天说的是《分库分表 ShardingSphere 原理与实战》系列的开篇。之前写过几篇分库分表的文章,反响还不错。至今公众号:程序员小富后台一直在留言咨询分库分表。没想到大家对分库分表这个话题这么感兴趣。可能很多人的工作内容和业务量都比较小,很难接触到这方面的技能。.这个系列我脑子里想了很久,一直拖到现在他说什么都不做。其实网上有很多关于分库分表的文章,但是我还是坚持发表这个系列,主要是为了自己的学习和研究,顺便分享一下。对于一个知识,不同的人从不同的角度理解是不一样的。网上的资料似乎很多,但值得学习和慎重选择。很多时候,在筛选的过程中,一点都不高的学习热情就被逐渐磨灭了。抄袭和雷同的东西太多,知识点比较零散,详细原理和实战案例很少。对于新手来说,从入门到放弃是稳妥的,就算有系统的,看几篇就断了(希望不会!)。我不太喜欢堆砌名词和概念。熟悉我的朋友不难发现,我的文章在讲解完原理后,后面总是跟着一波实操。学习技术原理一定要结合实际操作,不然三天半也忘不掉,纯经验。上图是我对ShardingSphere的初步概述,在官网文档的基础上补充了很多基础知识。本系列将用数十篇文章详细梳理分库分表的基础理论,手把手实践ShardingSphere5.X框架的功能。以及源码解读,以及开发中容易踩到的点,每篇文章都附有代码案例demo,旨在让新手更容易理解。系列完成后,所有内容将整理成PDF分享给大家。对此期待!话不多说,进入正题~先别急着上手ShardingSphere框架。先回顾一下分库分表的基本概念。大多数技术术语都很难理解。不要记住最重要的事情。一层窗户纸,发现其实是一样的东西。什么是分库分表分库分表是在海量数据下,单一数据库和表的数据量大,导致数据库性能持续下降的问题下演化出来的技术方案。分库和分表由两个独立的概念组成:分库和分表,但通常对分库和分表的操作会同时进行,所以我们习惯性的称它们为分库-数据库和子表。通过一定的规则,将数据量大的数据库拆分成多个独立的数据库,将数据量大的表拆分成若干个数据表,使单个数据库和表的性能达到最佳效果(responseFast),从而提高数据库的整体性能。为什么分库和分库的存储容量和连接数有限,很容易成为系统的瓶颈。当单表数据量超过百万里时,我们还可以通过增加从库和优化索引来提升性能。一旦数据量向千万级以上增长,无论你如何优化数据库,很多操作的性能还是会严重下降。为了减轻数据库的负担,提高数据库的响应速度,缩短查询时间,这时候就需要分库分表了。为什么需要子库?容量我们分配给数据库实例的磁盘容量是固定的,数据量持续大幅增加。单机容量承载这么多数据用不了多久。解决方法简单粗暴,加大容量!连接数单机容量可以随意扩展,但是数据库的连接数是有限制的。在高并发场景下,多个业务同时操作一个数据库。正常访问。可以通过max_connections查看MySQL的最大连接数。showvariableslike'%max_connections%'将原来单一的数据库根据不同的业务拆分为订单库、物流库、信用库等,既能有效分担数据库读写压力,又能提高系统容错能力.为什么需要子表?做过报表业务的同学应该都有过SQL执行时间超过几十秒的场景。数据库查询慢的原因有很多。SQL不打索引,like扫描全表,使用函数计算。这些都可以通过优化的方式来解决,但是MySQL无法通过自身的优化来解决大数据量的问题。缓慢的根本原因是InnoDB存储引擎。聚簇索引结构的B+树层级越高,查询性能随着磁盘IO的增加而变慢。具体原理大家可以自行搜索,这里就不再赘述了。阿里的开发手册里面有建议。如果单表行数超过500万行或者单表容量超过2GB,建议分库分表。然而,理想与现实总是有差距的。阿里这种规模的公司,已经不错了。当然,钱也可以这么用。事实上,很多公司还是不选择分库分表,单表有几千万、几十亿的数据。什么时候分库分表技术群里经常有小伙伴问,什么情况下会用到分库分表?分库分表需要解决现有海量数据访问的性能瓶颈和不断增长的数据量的架构可预测性。分库分表的关键指标是数据量。我们以fire100.top网站的资源表t_resource为例。系统初期运行时,每天只上传几十个资源。这时候单库、单表的方式足以支撑系统的存储,在数据量较小的时候几乎没有数据库性能瓶颈。但有一天,一股神秘的流量开始进入,系统每天产生的资源数据量激增到几十万甚至上百万。此时资源表的数据量达到了千万级别,查询响应变慢了。瓶颈逐渐显现。以MySQL数据库为例,当单表数据量达到亿级别时,通过加索引、SQL调优等传统优化策略,性能提升仍然微乎其微,可以考虑分库分库桌子。既然MySQL在存储海量数据时会出现性能瓶颈,是否可以考虑换成其他方案呢?比如高性能的非关系型数据库MongoDB?是的,但这取决于存储的数据类型!现在互联网上大部分公司的核心数据几乎都存储在关系型数据库(MySQL、Oracle等)中,因为它们具有和NoSQL一样的稳定性和可靠性,成熟的产品,完整的生态系统,核心的事务功能特性是不具备的在其他存储工具中,对于评论、点赞等非核心数据,MongoDB还是可以考虑的。如何分库分表数据分库的核心是将数据分库分表,在不同的库分表中进行相对统一的路由,分库后快速定位数据,整合搜索结果。分库分表可以从两个纬度进行拆分:vertical(纵向)和horizo??ntal(水平)。我们以经典的订单业务为例,看看如何拆分。垂直分库1.垂直分库垂直分库一般按照业务和功能维度进行分库,将不同的业务数据放入不同的数据库中。核心概念专用于特殊数据库。数据按业务类型分离,拆分到多个数据库中。订单、支付、会员、积分等表放在对应的订单库、支付库、会员库、积分库中。不同业务禁止跨库直连,对方业务数据必须通过API接口进行交互,这也是微服务拆分的重要依据。垂直分片很大程度上取决于业务的划分,但是有时候业务之间的划分并不是那么清晰,比如:电商中订单数据的拆分,很多其他业务依赖于订单数据,有时候边界不是很清晰好分工。垂直分库将一个数据库的压力分散到多个数据库,提高了部分数据库的性能,但是并没有解决单表数据量大带来的性能问题,所以需要配合后续的分库表来解决。2.垂直分表垂直分表是针对业务字段较多的大表进行的。一般将业务范围表中相对独立的字段或不常用的字段拆分成单独的数据表。将表拆分成小表的方式。例如:t_order订单表有几十个字段,其中经常计算订单金额相关的字段。为了不影响订单表t_order的性能,可以将订单金额相关的字段分离出来维护一个单独的t_order_price_expansion扩展表,这样每张表只存储原表的部分字段,并且是关联订单号order_no,然后分表路由到不同的库。数据库以行为单位将数据加载到内存中。拆分后,核心表大部分是访问频率高的字段,字段长度也较短。因此,可以将更多数据加载到内存中,减少磁盘IO。提高索引查询的命中率,进一步提高数据库性能。将上层竖库竖表水平拆分后,仍然会出现单库、表数据过多的问题。当我们的应用无法再进行细粒度的垂直切分时,仍然会存在单库读写和存储性能瓶颈,这时候就需要配合水平分库和水平分表。1.水平分库水平分库是将同一张表按照一定的规则拆分到不同的数据库中。每个数据库可以位于不同的服务器上,实现水平扩展。它是提高数据库性能的常用方法。例如:db_orde_1和db_order_2在两个数据库中有完全相同的t_order表。我们在访问一个订单的时候,可以对订单的订单号和订单号mod2(数据库实例数)取模,指定订单应该在哪个数据库中操作。这种方案往往可以解决问题单库存储和性能瓶颈,但由于同一张表分配在不同的数据库中,数据访问需要额外的路由工作,因此也增加了系统的复杂度。2.水平分表水平分表是将一个数据量很大的表按照一定的规则分成同一个数据库中结构完全相同的多个表,每个表只存储原表的一部分数据。例如:一张t_order订单表有900万条数据,横向拆分三张表,t_order_1、t_order_2、t_order_3,每张表有300万条数据,以此类推。虽然表被水平拆分,但子表仍然在同一个数据库实例中。只是解决了单表数据量过大的问题,并没有把分表分布到不同的机器上。争夺同一台物理机的CPU、内存、网络IO等。为了进一步提高性能,需要将分表分散到不同的数据库中,达到分布式的效果。数据存储到哪个数据库的表中之后,就会出现一个问题。一个表会出现在多个数据库中。哪个表应该存储在哪个数据库中?我们在上面多次提到了某些规则。这个规则其实就是一种路由算法,决定一条数据应该存放在哪个数据库的哪个表中。常见的有取模算法、范围限制算法、范围+取模算法、预定义算法1、取模算法keyfieldmodulus(remainderhash(XXX)modNforthehashresult),其中N为数据库实例数或子表数)是最常见的路由方式。以t_order订单表为例,先对数据库进行编号从0到N-1,对t_order订单表中order_no订单号字段取模hash(order_no)modN,得到余数i。i=0存储第一个bank,i=1存储第二个bank,i=2存储第三个bank,依此类推。相同的订单数据会落在同库同表中。查询时,使用相同的规则,以t_order订单号作为查询条件,可以快速定位到数据。优点是实现简单,数据分布比较均匀,不容易把所有的请求都打到一个库上。缺点取模算法对集群的伸缩支持不是很友好。集群中有N个数据库。hash(user_id)modN,当某台机器宕机时,本该落在数据库中的请求无法处理。暂时失败的实例将被踢出集群。此时机器减号算法改变hash(user_id)modN-1,相同的用户数据落在不同的数据库中,当本机恢复时,以user_id为条件查询用户数据会变少。2.范围限制算法范围限制算法按照一定的范围字段进行拆分,比如时间或者ID。用户表t_user分为三个表:t_user_1、t_user_2和t_user_3。然后将user_id为1~1000w的用户数据放入t_user_1,1000~2000w放入t_user_2,2000~3000w放入t_user_3,以此类推。除以日期范围也是如此。优点是单表数据量可控,水平扩展简单。您只需要添加节点,不需要从其他分片迁移数据。缺点:由于连续分片,可能会出现数据热点。(双11等大促)订单激增,存储11月数据的表可能会被频繁读写,而存储在其他分片表的历史数据很少被查询,造成数据倾斜,数据库压力分布不均。3.范围+取模算法为了避免热点数据的问题,我们可以优化范围上限算法。这次我们先通过range算法定义每个库的用户表t_user只存储1000w数据,第一个db_order_1库存储userId从1~1000w,第二个库1000~2000w,第三个库2000~3000w,等等。在各个库中,将用户表t_user拆分为t_user_1、t_user_2、t_user_3等,并对userd进行建模路由到对应的表中。有效避免了数据分布不均的问题,数据库的横向扩展也简单。直接添加实例不需要迁移历史数据。4.地理位置分片地理分片其实是一个更大的范围,按城市或地区划分,比如华东和华北的数据存储在不同的分库和分表中。5、预定义算法预定义算法是预先知道分库分表的个数,可以直接将某类数据路由到指定的库或表中,查询时也是如此。分库分表产生的问题了解了上面分库分表的拆分方式后,不难发现,与拆分前的单库单表相比,系统数据的演进存储架构变得非常复杂。看几个有代表性的问题,比如:分页、排序、跨节点联合查询分页、排序、联合查询,这些看似是开发中经常用到的常见操作,但是分库分表后,就很迷惑了头痛。查询分散在不同数据库中的表中的数据,然后将所有结果汇总合并提供给用户。例如:我们要查询11月和12月的订单数据。如果两个月的数据分散到不同的数据库实例中,我们需要查询两个数据库相关的数据。合并、排序和分页数据的过程繁琐复杂。事务一致性分库分表分布在不同数据库后,必然会出现跨库事务问题。以后分布式事务会使用阿里的Seata和MySQL的XA协议来实现,比较一下各自的优缺点。全局唯一主键数据库分表后,数据库表的主键ID没有什么商业意义,因为无法识别出唯一的一条记录。比如多个表t_order_1和t_order_2的主键ID都会从1开始重复,这时候我们就需要主动给一条记录分配一个ID。这个全球唯一的ID称为分布式ID。颁发此ID的系统通常称为颁发者。多数据库的高效管理多数据库的高效管理以及数据库中大量的碎片表是非常有必要的,因为order表像某宝这样大促后可能分裂成几千块对于t_order_n表,如果有没有高效的管理方案,手动建表排查问题是一件很可怕的事情。历史数据迁移分库分表架构实现后,第一个问题是如何平滑迁移历史数据、增量数据和全量数据迁移。这又是一件比较麻烦的事情,后面会详细介绍。分库分表架构模式分库分表架构主要有两种模式:客户端模式和代理模式。分割后的SQL直接连接多个数据库进行操作,然后在本地进行数据的合并汇总等操作。代理模式代理模式将应用程序与MySQL数据库隔离开来。业务方的应用不再需要直接连接数据库,而是连接proxy代理服务。代理服务实现了MySQL协议。对于业务端来说,代理服务就是数据库,它会将SQL分发到具体的数据库中去执行并返回结果。服务有分库分表的配置,分表根据配置自动创建。如何选择客户端模式和代理模式如何选择,我们可以从以下几个方面做一个简单的比较。1.性能方面,client模式的性能稍好一些。直接连接MySQL执行命令;代理服务延伸了整个执行环节。Application->proxyservice->MySQL可能会造成一些性能损失,但是两者区别不是很大。2.复杂度client模式在开发使用中通常可以引入一个jar;proxy代理模式需要单独搭建服务,有一定的维护成本。既然是服务,就要考虑高可用。毕竟所有的SQL应用都要通过它转发给MySQL。3、升级client模式的分库分表一般依赖基础架构组的Jar包。一旦有版本升级或bug修改,所有申请的项目都必须进行相应的升级。一个小规模的团队,用更少的服务进行升级问题不大。如果大公司服务规模大,涉及多个部门,升级成本会比较高;代理模式在升级、发布新功能或修复bug方面优势明显。只要重新部署代理服务集群,业务方是无感知的,但在发布过程中必须保证服务的可用性。4.治理和监督。由于client模式嵌入到应用中,不方便统一处理应用集群部署;代理模式在SQL限流、读写权限控制、监控告警等服务管理方面更加优雅。结束语本文主要是复习一下分库分表的一些基本概念,以便大家在后续的ShardingSphere实践中有更好的理解。内容中的很多概念都只是简单提及,没有详细展开。以下几页将一一解释。下期预告《分库分表ShardingSphere的基础知识点梳理》欢迎关注公众号:程序员小付,我们下期再见!