当前位置: 首页 > 科技观察

为什么要分库分表?

时间:2023-03-16 22:31:13 科技观察

文章开头先问几个问题:(1)什么时候需要分库分表?我们的判断标准是什么?(2)只有当一张表存储了多少数据才需要考虑分库分表?(3)数据增长速度非常快。每天产生多少数据才需要考虑分库分表?这些问题你想清楚了吗?相信看完这篇文章,你会有答案。为什么要分库分表?首先回答一下为什么要分库分表。答案很简单:数据库存在性能瓶颈。用大白话来说,数据库快被压垮了。数据库的性能瓶颈有几个外在表现:大量请求被阻塞在高并发场景下,大量请求需要操作数据库,导致连接数不足,请求被阻塞。SQL操作变慢。如果数据库中有一张上亿条数据的表,如果一条SQL没有命中索引,就会扫描全表。此查询将花费很长时间。存储问题业务量激增,单个数据库数据量越来越大,给存储带来巨大压力。从机器的角度来看,性能瓶颈无非就是CPU、内存、磁盘、网络等,解决性能瓶颈最简单粗暴的方法就是提高机器的性能,但是成本收益投入比这种方法的成本通常太高。性价比高,所以重点还是要从软件的角度入手。数据库相关优化方案数据库优化方案有很多,主要分为软件层面和硬件层面两大类。软件层面包括:SQL调优、表结构优化、读写分离、数据库集群、分库分表等;硬件层面主要是增加机器性能。SQL调优SQL调优通常是解决数据库问题的第一步,通常少量的努力可以产生更大的收益。SQL调优的主要目的是让慢的SQL尽可能快,方法其实很简单就是让SQL执行尽可能命中索引。启用慢速SQL日志记录如果您使用的是Mysql,则需要在Mysql配置文件中配置一些参数。slow_query_log=onlong_query_time=1slow_query_log_file=/path/to/log调优工具经常使用explain命令来查看SQL语句的执行计划。通过观察执行结果,很容易知道SQL语句是否是全表扫描。命中指数。selectid,age,genderfromuserwherename='LaughingArchitect';返回一个名为“type”的列,常用值有:ALL、index、range、ref、eq_ref、const、system、NULL(从左到右,性能从Bad到good)ALL表示这条SQL语句扫描整个表,需要优化。一般来说,你需要达到范围级别及以上。表结构优化以一个场景为例:“user”表有user_id、nickname等字段,“order”表有order_id、user_id等字段。想获取用户的昵称怎么办?一般是通过join关联表进行操作,在查询订单表时,关联查询用户表,获取导入的用户昵称。但是,随着业务量的增加,订单表和用户表也必须急剧增加。这时候通过两张表关联数据就比较费力了。为了得到一个昵称字段,我们不得不关联查询几千万张用户表。其速度可想而知。这时候可以尝试在订单表中加入nickname字段(order_id,user_id,nickname)。这种做法通常称为数据库表中的冗余字段。这样做的好处是在显示订单列表时,不需要关联查询用户表。冗余字段的做法也有一个缺点。如果该字段的更新会涉及同时更新多个表,在选择冗余字段时,尽量选择不经常更新的字段。架构优化当单个数据库实例无法处理时,我们可以添加实例组成集群对外服务。当发现读请求明显多于写请求时,我们可以让主实例负责写,并提供从实例读的能力;如果读实例压力还是很大,我们可以在数据库前面加一个缓存比如redis,这样请求优先从缓存中取数据减少数据库访问。缓存分担了一部分压力后,数据库仍然是瓶颈。这时候可以考虑分库分表的方案,后面会详细介绍。硬件优化硬件成本非常高。一般来说,遇到数据库性能瓶颈是不可能升级硬件的。当前期业务量比较小的时候,可以通过升级硬件来大幅提升硬件数据库的性能;但是到了后期,通过升级硬件获得的收益就没有那么明显了。分库分表详解下面我们以一个商城系统为例,一步步讲解数据库是如何一步步进化的。单应用单数据库,创业初期想搭建一个商城系统。基本上一个系统包含多个基本功能模块,最后打包成一个war包进行部署。这是一个典型的单一架构应用程序。商城项目使用单个数据库,如上图所示。商城系统包括首页Portal模板、用户模块、订单模块、库存模块等,所有模块共享一个数据库,数据库中通常有很多表。因为用户量不大,这样的结构在前期是完全适用的,开发者可以用demo到处找(骗)投资人。一旦收到投资人的钱,业务就必须大规模推广,系统架构也必须与业务的快速发展相匹配。为了抢占早期多应用单库市场,这套系统不断迭代更新,代码量越来越大,架构也越来越臃肿。现在随着系统接入压力逐渐增大,系统拆分已经迫在眉睫。为了保证业务的顺利进行,系统架构改造也分几个阶段进行。第一阶段,将商城系统的单体架构按照功能模块拆分成子服务,如:门户服务、用户服务、订单服务、库存服务等。单个数据库的多个应用如图所示上图。多个服务共享一个数据库。这样做的目的是保持底层数据库访问逻辑不受影响,并将影响降到最低。多应用多数据库随着业务推广的增多,数据库终于成为了瓶颈。这时候,多个服务共享一个数据库基本是行不通的。我们需要把每个服务相关的表分开,单独建一个数据库,其实就是一个“分库”。单个数据库支持的并发量是有限的。拆分成多个数据库可以消除服务之间的竞争,提高服务性能。多应用多数据库如上图所示,一个大数据分离多个小数据库,每个服务对应一个数据库。这是系统发展到一定阶段所必需的“分库”操作。当前非常流行的微服务架构也是如此。如果只拆分应用,不拆分数据库,根本解决不了问题,整个系统很容易出现瓶颈。说完分库,什么时候分表呢?如果系统处于快速发展阶段,以商城系统为例,一天的订单量可能有几十万,数据库中的订单表会增长非常快,增长到一定阶段数据库查询效率会大幅下降。因此,当单表数据增量过快时,业界传言数据量超过500万,就要考虑表了。当然,500万只是经验值,大家可以根据实际情况做决定。那么如何分表呢?分表有几个维度,一种是水平切分和垂直切分,一种是单库内表切分和多库内表切分。水平拆分和垂直拆分以用户表(user)为例,表中有7个字段:id、name、age、sex、nickname、description,如果nickname和description不常用,我们可以拆分成另一张表:用户详细信息表,这样一张用户表被拆分为用户基本信息表+用户详细信息表,两张表结构不同,相互独立。但是从这个角度来看,垂直拆分并不能从根本上解决单表数据量过大的问题,所以我们还是要做一个水平拆分。还有另一种拆分表的方法。比如表中有10000条数据,我们将它们拆分成id为奇数的两张表:1,3,5,7...,放在id为偶数的user1中:2,4,6,8...放到user2中,这种拆分方式就是水平拆分。水平拆分表格的方法也有很多。除了上面说的按照id进行分表,还可以按照时间维度进行分表。比如订单表可以按照日、月等进行拆分。日表:只存储当天的数据。月表:可以启动定时任务,将前一天的所有数据迁移到当月表。历史表:也可以通过定时任务将30天以上的数据迁移到历史表中。总结水平拆分和垂直拆分的特点:垂直拆分:基于表或字段的划分,表结构不同。横向切分:基于数据的切分,表结构相同,但数据不同。单库拆分和多库拆分以水平拆分为例,每个表被拆分成多个子表,多个子表存在于同一个数据库中。比如下面的用户表被拆分成了用户1表和用户2表。单库拆分在一个数据库中将一张表拆分成若干个子表,可以在一定程度上解决单表查询性能的问题,但是也遇到了一个问题:单库存储瓶颈。因此,业界更常用的是将分表拆分到多个数据库中。比如下图中,user表被拆分成了两个子表,两个子表分别存在于不同的数据库中。多库拆分一句话总结:分表主要是为了减小单表的大小,解决单表数据量大带来的性能问题。分库分表带来的复杂性既然分库分表这么好,那么在项目初期是否应该采用这种方案呢?不要激动,冷静点,分库分表确实解决了很多问题,但是也给系统带来了很多复杂度,下面简单说一下。(1)跨库关联查询在单库分表之前,我们可以很方便的使用join操作关联多张表来查询数据,但是分库分表后,两个表可能不在同一个数据库。如何使用join什么?有几种解决方案:字段冗余:将需要关联的字段放到主表中,避免join操作;数据抽象:通过ETL对数据进行汇聚聚合,生成新表;全局表:比如一些基本表的副本,可以放在每个数据库中;应用层组装:找出基础数据,通过应用计算组装;(2)分布式事务单库可以用本地事务来完成,多库只能通过分布式事务来解决。常用的解决方案包括:基于可靠消息(MQ)的解决方案、两阶段事务提交、灵活事务等。(3)排序、分页、函数计算问题使用SQL时,orderby、limit等关键字需要特殊处理.一般来说,采用分片的思路:先在每个分片上执行相应的函数,然后对每个分片的结果集进行聚合,重新计算,最终得到结果。(4)分布式ID如果在单库单表中使用Mysql数据库,可以使用id自增作为主键。数据库分表后就不行了,id会重复。常用的分布式ID方案有:UUID基于数据库自增维护单独的ID表号段方式Redis缓存雪花算法(Snowflake)百度uid-generatorMeituanLeafDidiTinyid这些方案后面会写在一篇文章中,这里不再展开。(5)多数据源分库分表后,可能会从多个数据库或多个分表中获取数据。一般的解决方案包括:客户端适配和代理层适配。业界常用的中间件有:shardingsphere(原sharding-jdbc)Mycat总结如果数据库有问题,不要着急分库分表,先看看能不能用常规手段解决。分库分表会给系统带来巨大的复杂性,除非万不得已,不建议不要提前使用。作为系统架构师,你可以让系统具有灵活性和可扩展性,但不要过度设计和过度设计。在这一点上,架构师必须要有前瞻性,提前做出预测。你学会了吗?