35 - 什么时候需要分表分库?

在当今互联网时代,海量数据基本上是每一个成熟产品的共性,特别是在移动互联网产品中,几乎每天都在产生数据,例如,商城的订单表、支付系统的交易明细以及游戏中的战报等等。

对于一个日活用户在百万数量级的商城来说,每天产生的订单数量可能在百万级,特别在一些活动促销期间,甚至上千万。

假设我们基于单表来实现,每天产生上百万的数据量,不到一个月的时间就要承受上亿的数据,这时单表的性能将会严重下降。因为 MySQL 在 InnoDB 存储引擎下创建的索引都是基于 B+ 树实现的,所以查询时的 I/O 次数很大程度取决于树的高度,随着 B+ 树的树高增高,I/O 次数增加,查询性能也就越差。

当我们面对一张海量数据的表时,通常有分区、NoSQL 存储、分表分库等优化方案。

分区的底层虽然也是基于分表的原理实现的,即有多个底层表实现,但分区依然是在单库下进行的,在一些需要提高并发的场景中的优化空间非常有限,且一个表最多只能支持 1024 个分区。面对日益增长的海量数据,优化存储能力有限。不过在一些非海量数据的大表中,我们可以考虑使用分区来优化表性能。

分区表是由多个相关的底层表实现的,这些底层表也是由句柄对象表示,所以我们也可以直接访问各个分区,存储引擎管理分区的各个底层表和管理普通表一样(所有的底层表都必须使用相同的存储引擎),分区表的索引只是在各个底层表上各自加上一个相同的索引,从存储引擎的角度来看,底层表和一个普通表没有任何不同,存储引擎也无须知道这是一个普通表,还是一个分区表的一部分。

而 NoSQL 存储是基于键值对存储,虽然查询性能非常高,但在一些方面仍然存在短板。例如,不是关系型数据库,不支持事务以及稳定性方面相对 RDBMS 差一些。虽然有些 NoSQL 数据库也实现了事务,宣传具有可靠的稳定性,但目前 NoSQL 还是主要用作辅助存储。

1、什么时候要分表分库?

分析完了分区、NoSQL 存储优化的应用,接下来我们就看看这讲的重头戏——分表分库。

在我看来,能不分表分库就不要分表分库。在单表的情况下,当业务正常时,我们使用单表即可,而当业务出现了性能瓶颈时,我们首先考虑用分区的方式来优化,如果分区优化之后仍然存在后遗症,此时我们再来考虑分表分库。

我们知道,如果在单表单库的情况下,当数据库表的数据量逐渐累积到一定的数量时(5000W 行或 100G 以上),操作数据库的性能会出现明显下降,即使我们使用索引优化或读写库分离,性能依然存在瓶颈。此时,如果每日数据增长量非常大,我们就应该考虑分表,避免单表数据量过大,造成数据库操作性能下降。

面对海量数据,除了单表的性能比较差以外,我们在单表单库的情况下,数据库连接数、磁盘 I/O 以及网络吞吐等资源都是有限的,并发能力也是有限的。所以,在一些大数据量且高并发的业务场景中,我们就需要考虑分表分库来提升数据库的并发处理能力,从而提升应用的整体性能。

2、如何分表分库?

通常,分表分库分为垂直切分和水平切分两种。

垂直分库是指根据业务来分库,不同的业务使用不同的数据库。例如,订单和消费券在抢购业务中都存在着高并发,如果同时使用一个库,会占用一定的连接数,所以我们可以将数据库分为订单库和促销活动库。

而垂直分表则是指根据一张表中的字段,将一张表划分为两张表,其规则就是将一些不经常使用的字段拆分到另一张表中。例如,一张订单详情表有一百多个字段,显然这张表的字段太多了,一方面不方便我们开发维护,另一方面还可能引起跨页问题。这时我们就可以拆分该表字段,解决上述两个问题。

水平分表则是将表中的某一列作为切分的条件,按照某种规则(Range 或 Hash 取模)来切分为更小的表。

水平分表只是在一个库中,如果存在连接数、I/O 读写以及网络吞吐等瓶颈,我们就需要考虑将水平切换的表分布到不同机器的库中,这就是水平分库分表了。

结合以上垂直切分和水平切分,我们一般可以将数据库分为:单库单表 - 单库多表 - 多库多表。在平时的业务开发中,我们应该优先考虑单库单表;如果数据量比较大,且热点数据比较集中、历史数据很少访问,我们可以考虑表分区;如果访问热点数据分散,基本上所有的数据都会访问到,我们可以考虑单库多表;如果并发量比较高、海量数据以及每日新增数据量巨大,我们可以考虑多库多表。

这里还需要注意一点,我刚刚强调过,能不分表分库,就不要分表分库。这是因为一旦分表,我们可能会涉及到多表的分页查询、多表的 JOIN 查询,从而增加业务的复杂度。而一旦分库了,除了跨库分页查询、跨库 JOIN 查询,还会存在跨库事务的问题。这些问题无疑会增加我们系统开发的复杂度。

3、分表分库之后面临的问题

然而,分表分库虽然存在着各种各样的问题,但在一些海量数据、高并发的业务中,分表分库仍是最常用的优化手段。所以,我们应该充分考虑分表分库操作后所面临的一些问题,接下我们就一起看看都有哪些应对之策。

为了更容易理解这些问题,我们将对一个订单表进行分库分表,通过详细的业务来分析这些问题。

假设我们有一张订单表以及一张订单详情表,每天的数据增长量在 60W 单,平时还会有一些促销类活动,订单增长量在千万单。为了提高系统的并发能力,我们考虑将订单表和订单详情表做分库分表。除了分表,因为用户一般查询的是最近的订单信息,所以热点数据比较集中,我们还可以考虑用表分区来优化单表查询。

通常订单的分库分表要么基于订单号 Hash 取模实现,要么根据用户 ID Hash 取模实现。订单号 Hash 取模的好处是数据能均匀分布到各个表中,而缺陷则是一个用户查询所有订单时,需要去多个表中查询。

由于订单表用户查询比较多,此时我们应该考虑使用用户 ID 字段做 Hash 取模,对订单表进行水平分表。如果需要考虑高并发时的订单处理能力,我们可以考虑基于用户 ID 字段 Hash 取模实现分库分表。这也是大部分公司对订单表分库分表的处理方式。

3.1、分布式事务问题

在提交订单时,除了创建订单之外,我们还需要扣除相应的库存。而订单表和库存表由于垂直分库,位于不同的库中,这时我们需要通过分布式事务来保证提交订单时的事务完整性。

通常,我们解决分布式事务有两种通用的方式:两阶事务提交(2PC)以及补偿事务提交(TCC)。有关分布式事务的内容,我将在第 41 讲中详细介绍。

通常有一些中间件已经帮我们封装好了这两种方式的实现,例如 Spring 实现的 JTA,目前阿里开源的分布式事务中间件 Fescar,就很好地实现了与 Dubbo 的兼容。

3.2、跨节点 JOIN 查询问题

用户在查询订单时,我们往往需要通过表连接获取到商品信息,而商品信息表可能在另外一个库中,这就涉及到了跨库 JOIN 查询。

通常,我们会冗余表或冗余字段来优化跨库 JOIN 查询。对于一些基础表,例如商品信息表,我们可以在每一个订单分库中复制一张基础表,避免跨库 JOIN 查询。而对于一两个字段的查询,我们也可以将少量字段冗余在表中,从而避免 JOIN 查询,也就避免了跨库 JOIN 查询。

3.3、跨节点分页查询问题

我们知道,当用户在订单列表中查询所有订单时,可以通过用户 ID 的 Hash 值来快速查询到订单信息,而运营人员在后台对订单表进行查询时,则是通过订单付款时间来进行查询的,这些数据都分布在不同的库以及表中,此时就存在一个跨节点分页查询的问题了。

通常一些中间件是通过在每个表中先查询出一定的数据,然后在缓存中排序后,获取到对应的分页数据。这种方式在越往后面的查询,就越消耗性能。

通常我们建议使用两套数据来解决跨节点分页查询问题,一套是基于分库分表的用户单条或多条查询数据,一套则是基于 Elasticsearch、Solr 存储的订单数据,主要用于运营人员根据其它字段进行分页查询。为了不影响提交订单的业务性能,我们一般使用异步消息来实现 Elasticsearch、Solr 订单数据的新增和修改。

3.4、全局主键 ID 问题

在分库分表后,主键将无法使用自增长来实现了,在不同的表中我们需要统一全局主键 ID。因此,我们需要单独设计全局主键,避免不同表和库中的主键重复问题。

使用 UUID 实现全局 ID 是最方便快捷的方式,即随机生成一个 32 位 16 进制数字,这种方式可以保证一个 UUID 的唯一性,水平扩展能力以及性能都比较高。但使用 UUID 最大的缺陷就是,它是一个比较长的字符串,连续性差,如果作为主键使用,性能相对来说会比较差。

我们也可以基于 Redis 分布式锁实现一个递增的主键 ID,这种方式可以保证主键是一个整数且有一定的连续性,但分布式锁存在一定的性能消耗。

我们还可以基于 Twitter 开源的分布式 ID 生产算法——snowflake 解决全局主键 ID 问题,snowflake 是通过分别截取时间、机器标识、顺序计数的位数组成一个 long 类型的主键 ID。这种算法可以满足每秒上万个全局 ID 生成,不仅性能好,而且低延时。

3.5、扩容问题

随着用户的订单量增加,根据用户 ID Hash 取模的分表中,数据量也在逐渐累积。此时,我们需要考虑动态增加表,一旦动态增加表了,就会涉及到数据迁移问题。

我们在最开始设计表数据量时,尽量使用 2 的倍数来设置表数量。当我们需要扩容时,也同样按照 2 的倍数来扩容,这种方式可以减少数据的迁移量。

4、总结

在业务开发之前,我们首先要根据自己的业务需求来设计表。考虑到一开始的业务发展比较平缓,且开发周期比较短,因此在开发时间比较紧的情况下,我们尽量不要考虑分表分库。但是我们可以将分表分库的业务接口预留,提前考虑后期分表分库的切分规则,把该冗余的字段提前冗余出来,避免后期分表分库的 JOIN 查询等。

当业务发展比较迅速的时候,我们就要评估分表分库的必要性了。一旦需要分表分库,就要结合业务提前规划切分规则,尽量避免消耗性能的跨表跨库 JOIN 查询、分页查询以及跨库事务等操作。

5、思考题

你使用过哪些分库分表中间件呢?欢迎分享其中的实现原理以及优缺点。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/180352.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LeetCode 5 最长回文子串

题目描述 最长回文子串 给你一个字符串 s,找到 s 中最长的回文子串。 如果字符串的反序与原始字符串相同,则该字符串称为回文字符串。 示例 1: 输入:s "babad" 输出:"bab" 解释:…

这个变量要不要用volatile修饰呢?

正文 大家好,又见面了,我是bug菌~ 在嵌入式软件开发过程中,如果对volatile不熟,那可以你应该是个"假嵌入式程序员",因为一个变量需不需要使用volatile考虑的场景挺多的,如果在某些场景下乱用&…

读像火箭科学家一样思考笔记12_实践与测试(下)

1. 舆论的火箭科学 1.1. 如果苹果违反了“即飞即测”原则,那苹果的iPhone就不会问世了 1.1.1. iPhone在其上市前的民意调查中相当失败 1.1.1.1. iPhone不可能获得太大市场份额,不可能。 1.1.1.1.1. 微软前CEO史蒂夫鲍尔默(Steve Ballmer&…

第15关 K8s HPA:自动水平伸缩Pod,实现弹性扩展和资源优化

------> 课程视频同步分享在今日头条和B站 大家好,我是博哥爱运维,这节课带来k8s的HPA 自动水平伸缩pod( 视频后面有彩蛋 : ) )。 我们知道,初始Pod的数量是可以设置的,同时业务也分流量高峰和低峰&a…

2023-简单点-机器学习中的数值计算问题

上溢和下溢: 上溢:指数函数或对数函数的输入值过大,导致计算结果超出了计算机可以表示的最大值。例如,在softmax函数中,当输入的数值很大时,指数运算的结果可能非常大,导致上溢。 下溢&#x…

Deep Image Prior

深度图像先验 论文链接:https://sites.skoltech.ru/app/data/uploads/sites/25/2018/04/deep_image_prior.pdf 项目链接:https://github.com/DmitryUlyanov/deep-image-prior Abstract 深度卷积网络已经成为一种流行的图像生成和恢复工具。一般来说&a…

如何选择一款安全稳定的跨境浏览器?

选择适合自己的跨境浏览器是进行跨境电商和跨境交流的关键一步。本文将为您介绍如何客观地选择一款安全稳定的跨境浏览器,以便更好地进行跨境业务。 在选择跨境浏览器时,以下几个因素是需要考虑的: 网络速度:跨境业务需要稳定而高…

第十四届蓝桥杯大赛国赛模拟题C++卷1

第十四届蓝桥杯大赛国赛模拟题C++卷1 一、选择题 1、在数组中,数组名表示( ) A.数组第1个元素的首地址 B.数组第2个元素的首地址 C.数组所有元素的首地址 D.数组最后1个元素的首地址答案:A.数组名是一个地址,指向第一个元素 2、下列叙述中正确的是( ) A.顺序存储结构的…

每日一练2023.11.28———N个数求和【PTA】

题目链接: L1-009 N个数求和 题目要求: 本题的要求很简单,就是求N个数字的和。麻烦的是,这些数字是以有理数分子/分母的形式给出的,你输出的和也必须是有理数的形式。 输入格式: 输入第一行给出一个正整…

三 STM32F4使用Sys_Tick 实现微秒定时器和延时

更多细节参考这篇 1. 什么是时钟以及作用 1.1 什么是时钟 时钟是由电路产生的周期性的脉冲信号,相当于单片机的心脏 1.2 时钟对于STM32的作用 指令同步:cpu和内核外设使用时钟信号来进行指令同步数据传输控制: 时钟信号控制数据在内部总…

【C数据(一)】数据类型和变量你真的理解了吗?来看看这篇

🌈write in front :🔍个人主页 : 啊森要自信的主页 ✏️真正相信奇迹的家伙,本身和奇迹一样了不起啊! 欢迎大家关注🔍点赞👍收藏⭐️留言📝>希望看完我的文章对你有小小的帮助&am…

Linux的Sysfs 接口

一、sysfs接口 在linux系统中,用户空间访问驱动程序一般是以“设备文件”的方式通过“read/write/ioctl”访问,还有一种方式,可以通过echo的方式来直接控制硬件或者修改驱动,也能为底层驱动提供一个接口便于应用层调用&#xff0c…

app上架一直显示审核中状态要怎么处理?

当你提交一个应用到App Store上时,它会经历一个审核过程。在这个过程中,苹果的审核人员会检查你的应用是否符合苹果的规定和标准。这个过程通常需要几天的时间,但是如果你的应用一直显示“审核中”状态,那么可能会有一些原因。 1…

广州华锐互动:VR虚拟现实内容创作工具带来全新的应用场景

随着科技的不断发展,低代码编辑工具已经成为了一种越来越受欢迎的开发方式。它可以帮助开发人员快速构建应用程序,降低开发成本,提高开发效率,而VR虚拟现实内容创作工具带来了全新的应用场景。 VR虚拟现实内容创作工具是广州华锐互…

AlphaFold的原理及解读

1、背景 蛋白质是生物体内一类重要的生物大分子,其结构复杂多样,蛋白质的结构对于理解其功能和参与的生物学过程具有重要意义。从生物学角度上看,蛋白质的结构可以分为四个层次:初级结构、二级结构、三级结构和四级结构。 初级结…

编程语言发展史:量子计算编程语言的应用和前景

一、引言 量子计算是一种基于量子力学原理的计算方式,它可以在某些情况下比传统计算机更快地处理某些问题。随着量子计算技术的发展,量子计算编程语言也逐渐成为了一个热门的研究方向。本文将介绍量子计算编程语言的发展历程、应用场景以及未来前景。 …

模拟实现offsetof宏(详解)

我们在以前学过这个offsetof函数,知道它的功能是求指针相较于起始位置的偏移量,我们今天要来写出一个宏,计算结构体中某成员变量相对于起始位置的偏移。 目录 1.offsetof函数 1.1offsetof函数介绍 1.2offsetof函数代码实现 2.offsetof函数…

python实现rpc的几种方式(SimpleXMLRPCServer 自带的、第三方ZeroRPC)、连接linux远程开发分布式锁、分布式id

1 python实现rpc的几种方式 1.1 SimpleXMLRPCServer 自带的 1.2 第三方ZeroRPC 2 连接linux远程开发 3 分布式锁 4 分布式id 1 python实现rpc的几种方式 # 远程过程调用-1 借助于rabbitmq,可以跨语言-2 SimpleXMLRPCServer 自带的-3 ZeroRPC-4 GRPC:跨语言的 htt…

dart多线程双向通信的案例----【小学4年级课程】

下面是运行后的打印顺序 I/flutter (20170): 上班 I/flutter (20170): 这里是校长室:main I/flutter (20170): 这里是饭堂:fantang1 I/flutter (20170): 这里是收发室--检查小孩发回去给他妈妈的信息是:我是秘书的儿子,我来到在校长室了。校长今晚想吃羊…

【Web】SWPUCTF 2022 新生赛 个人复现

目录 ①webdog1__start ②ez_rce ③ez_sql ④ez_1zpop ⑤file_maste ⑥Power! 挑了部分题,太简单的就没选进来(但选进来≠有难度) ①webdog1__start 进来没啥东西,右键查看源码 对于0e215962017,md5后也是以…