什么时候需要分表分库?

在当今互联网时代,海量数据基本上是每一个成熟产品的共性,特别是在移动互联网产品中,几乎每天都在产生数据,例如,商城的订单表、支付系统的交易明细以及游戏中的战报等等。
对于一个日活用户在百万数量级的商城来说,每天产生的订单数量可能在百万级,特别在一些活动促销期间,甚至上千万。
假设我们基于单表来实现,每天产生上百万的数据量,不到一个月的时间就要承受上亿的数据,这时单表的性能将会严重下降。因为MySQL在InnoDB存储引擎下创建的索引都是基于B+树实现的,所以查询时的I/O次数很大程度取决于树的高度,随着B+树的树高增高,I/O次数增加,查询性能也就越差。
当我们面对一张海量数据的表时,通常有分区、NoSQL存储、分表分库等优化方案。

一 什么时候要分表分库?

在我看来,能不分表分库就不要分表分库。在单表的情况下,当业务正常时,我们使用单表即可,而当业务出现了性能瓶颈时,我们首先考虑用分区的方式来优化,如果分区优化之后仍然存在后遗症,此时我们再来考虑分表分库。
我们知道,如果在单表单库的情况下,当数据库表的数据量逐渐累积到一定的数量时(5000W行或100G以上),操作数据库的性能会出现明显下降,即使我们使用索引优化或读写库分离,性能依然存在瓶颈。此时,如果每日数据增长量非常大,我们就应该考虑分表,避免单表数据量过大,造成数据库操作性能下降。

二 如何分表分库?

通常,分表分库分为垂直切分和水平切分两种。
垂直分库是指根据业务来分库,不同的业务使用不同的数据库。例如,订单和消费券在抢购业务中都存在着高并发,如果同时使用一个库,会占用一定的连接数,所以我们可以将数据库分为订单库和促销活动库。
而垂直分表则是指根据一张表中的字段,将一张表划分为两张表,其规则就是将一些不经常使用的字段拆分到另一张表中。例如,一张订单详情表有一百多个字段,显然这张表的字段太多了,一方面不方便我们开发维护,另一方面还可能引起跨页问题。这时我们就可以拆分该表字段,解决上述两个问题。
水平分表则是将表中的某一列作为切分的条件,按照某种规则(Range或Hash取模)来切分为更小的表。
水平分表只是在一个库中,如果存在连接数、I/O读写以及网络吞吐等瓶颈,我们就需要考虑将水平切换的表分布到不同机器的库中,这就是水平分库分表了。

三 分表分库之后面临的问题

然而,分表分库虽然存在着各种各样的问题,但在一些海量数据、高并发的业务中,分表分库仍是最常用的优化手段。所以,我们应该充分考虑分表分库操作后所面临的一些问题,接下我们就一起看看都有哪些应对之策。
为了更容易理解这些问题,我们将对一个订单表进行分库分表,通过详细的业务来分析这些问题。
假设我们有一张订单表以及一张订单详情表,每天的数据增长量在60W单,平时还会有一些促销类活动,订单增长量在千万单。为了提高系统的并发能力,我们考虑将订单表和订单详情表做分库分表。除了分表,因为用户一般查询的是最近的订单信息,所以热点数据比较集中,我们还可以考虑用表分区来优化单表查询。
通常订单的分库分表要么基于订单号Hash取模实现,要么根据用户 ID Hash 取模实现。订单号Hash取模的好处是数据能均匀分布到各个表中,而缺陷则是一个用户查询所有订单时,需要去多个表中查询。
由于订单表用户查询比较多,此时我们应该考虑使用用户ID字段做Hash取模,对订单表进行水平分表。如果需要考虑高并发时的订单处理能力,我们可以考虑基于用户ID字段Hash取模实现分库分表。这也是大部分公司对订单表分库分表的处理方式。

1.分布式事务问题

在提交订单时,除了创建订单之外,我们还需要扣除相应的库存。而订单表和库存表由于垂直分库,位于不同的库中,这时我们需要通过分布式事务来保证提交订单时的事务完整性。
通常,我们解决分布式事务有两种通用的方式:两阶事务提交(2PC)以及补偿事务提交(TCC)。有关分布式事务的内容,我将在后面的讲解中详细介绍。

2.跨节点JOIN查询问题

用户在查询订单时,我们往往需要通过表连接获取到商品信息,而商品信息表可能在另外一个库中,这就涉及到了跨库JOIN查询。
通常,我们会冗余表或冗余字段来优化跨库JOIN查询。对于一些基础表,例如商品信息表,我们可以在每一个订单分库中复制一张基础表,避免跨库JOIN查询。而对于一两个字段的查询,我们也可以将少量字段冗余在表中,从而避免JOIN查询,也就避免了跨库JOIN查询。

3.跨节点分页查询问题

我们知道,当用户在订单列表中查询所有订单时,可以通过用户ID的Hash值来快速查询到订单信息,而运营人员在后台对订单表进行查询时,则是通过订单付款时间来进行查询的,这些数据都分布在不同的库以及表中,此时就存在一个跨节点分页查询的问题了。
通常一些中间件是通过在每个表中先查询出一定的数据,然后在缓存中排序后,获取到对应的分页数据。这种方式在越往后面的查询,就越消耗性能。

4.全局主键ID问题

在分库分表后,主键将无法使用自增长来实现了,在不同的表中我们需要统一全局主键ID。因此,我们需要单独设计全局主键,避免不同表和库中的主键重复问题。
使用UUID实现全局ID是最方便快捷的方式,即随机生成一个32位16进制数字,这种方式可以保证一个UUID的唯一性,水平扩展能力以及性能都比较高。但使用UUID最大的缺陷就是,它是一个比较长的字符串,连续性差,如果作为主键使用,性能相对来说会比较差。
我们也可以基于Redis分布式锁实现一个递增的主键ID,这种方式可以保证主键是一个整数且有一定的连续性,但分布式锁存在一定的性能消耗。
我们还可以基于Twitter开源的分布式ID生产算法——snowflake解决全局主键ID问题,snowflake是通过分别截取时间、机器标识、顺序计数的位数组成一个long类型的主键ID。这种算法可以满足每秒上万个全局ID生成,不仅性能好,而且低延时。

5.扩容问题

随着用户的订单量增加,根据用户 ID Hash 取模的分表中,数据量也在逐渐累积。此时,我们需要考虑动态增加表,一旦动态增加表了,就会涉及到数据迁移问题。
我们在最开始设计表数据量时,尽量使用2的倍数来设置表数量。当我们需要扩容时,也同样按照2的倍数来扩容,这种方式可以减少数据的迁移量。

四 总结

在业务开发之前,我们首先要根据自己的业务需求来设计表。考虑到一开始的业务发展比较平缓,且开发周期比较短,因此在开发时间比较紧的情况下,我们尽量不要考虑分表分库。但是我们可以将分表分库的业务接口预留,提前考虑后期分表分库的切分规则,把该冗余的字段提前冗余出来,避免后期分表分库的JOIN查询等。
当业务发展比较迅速的时候,我们就要评估分表分库的必要性了。一旦需要分表分库,就要结合业务提前规划切分规则,尽量避免消耗性能的跨表跨库JOIN查询、分页查询以及跨库事务等操作。

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
数据库设计经验中进行分表分库的主要原因是为了解决性能瓶颈和高并发的问题。当业务发展迅速,单个数据库成为了性能瓶颈时,分库分表可以有效地提高数据库的处理能力和性能。 分表是将一个大表按照一定的规则拆分成多个小表,每个小表只包含部分数据。这样可以减少单个表的数据量,提高查询效率,降低锁竞争,减轻数据库的负载压力。常见的分表规则有按照时间范围、按照哈希值等。 分库是将一个数据库分成多个独立的子数据库,每个子数据库可以运行在不同的机器上。这样可以将不同的业务数据分开存储,避免了单个数据库的性能瓶颈,提高了数据库的并发处理能力。 进行分表分库也会带来一些问题。比如,跨表查询和跨库查询可能会变得复杂,需要额外的处理。同时,数据一致性的维护也会变得更加复杂,需要考虑分布式事务的处理。此外,对于分表分库的中间件选择也需要谨慎考虑,以满足业务需求并保证系统的稳定性。 综上所述,在数据库设计中进行分表分库可以提高数据库的性能和并发处理能力,但也会引入一些额外的问题和挑战。因此,在设计和实施分表分库时,需要综合考虑业务需求、系统架构和数据库性能等因素,做出合理的决策。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [分布式 微服务 项目 我们为什么要分库分表?](https://blog.csdn.net/qq_44866828/article/details/124098306)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值