MYSQL 分库分表

公司现有业务不断发展,流量剧增,交易数量突破了千万订单,但是订单数据还是单表存储,主从分离后,虽然减少了缓解读请求的压力,但随着写入压力增加,数据库的查询和写入性能都在下降,这时你要怎么设计架构?

首先不能考虑主从分离了 因为他已经说了 目前数据结构还是单表结构 所以我们可以考虑分区 但是分区的性能优化时有上限的 突破千万级了  就不能采用这种方法了 我们就要考虑使用分库分表了

分库分表的概念

首先区分一下分表和分库

  • 何时分表

当数据量过大造成事务执行缓慢时,就要考虑分表,因为减少每次查询数据总量是解决数据查询缓慢的主要原因。你可能会问:“查询可以通过主从分离或缓存来解决,为什么还要分表?”但这里的查询是指事务中的查询和更新操作。

  • 何时分库

为了应对高并发,一个数据库实例撑不住,即单库的性能无法满足高并发的要求,就把并发请求分散到多个实例中去(这种应对高并发的思路我之前也说过)。

总的来说,分库分表使用的场景不一样: 分表是因为数据量比较大,导致事务执行缓慢;分库是因为单库的性能无法满足要求。

啊 那我要分库就行了啊 为什么还要分表呢 我们这里依然用“慷慨的代价”来回答这个问题 如果有一个更简单的方法解决这个问题就不要用复杂的方法解决

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

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

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

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

如何选择分片策略?

在明确分库分表的场景后,面试官一般会追问“怎么进行分片?”换句话说就是按照什么分片策略对数据库进行分片?

啊 这个问的是分库分表的策略

分库分表的策略

  • 垂直拆分

垂直拆分是根据数据的业务相关性进行拆分。比如一个数据库里面既存在商品数据,又存在订单数据,那么垂直拆分可以把商品数据放到商品库,把订单数据放到订单库。一般情况,垂直拆库常伴随着系统架构上的调整。

比如在对做系统“微服务”改造时,将原本一个单体系统拆分成多个子系统,每个系统提供单独的服务,那么随着应用层面的拆分带来的也有数据层面的拆分,将一个主库的数据表,拆分到多个独立的子库中去。

对数据库进行垂直拆分最常规,优缺点也很明显。

垂直拆分可以把不同的业务数据进行隔离,让系统和数据更为“纯粹”,更有助于架构上的扩展。但它依然不能解决某一个业务的数据大量膨胀的问题,一旦系统中的某一个业务库的数据量剧增,比如商品系统接入了一个大客户的供应链,对于商品数据的存储需求量暴增,在这个时候,就要把数据拆分到多个数据库和数据表中,也就是对数据做水平拆分。

 

  • 水平拆分

垂直拆分随架构改造而拆分,关注点在于业务领域,而水平拆分指的是把单一库表数据按照规则拆分到多个数据库和多个数据表中,比如把单表 1 亿的数据按 Hash 取模拆分到 10 个相同结构的表中,每个表 1 千万的数据。并且拆分出来的表,可以分别存放到不同的物理数据库中,关注点在于数据扩展。

 

  • Range(范围分片)

是按照某一个字段的区间来拆分,最好理解的就是按照时间字段分片,比如可以把一个月的数据放入一张表中,这样在查询时就可以根据时间先定位数据存储在哪个表里面,再按照查询条件来查询。还有根据关键字区分 比如商品型号

  • 垂直水平拆分

垂直水平拆分,是综合垂直和水平拆分方式的一种混合方式,垂直拆分把不同类型的数据存储到不同库中,再结合水平拆分,使单表数据量保持在合理范围内,提升性能。

 

看一个具体例子喵

垂直划分

将一张表的数据,根据场景切分成多张表,本质是由于前期抽象不足,需要将业务数据进一步拆分。(所以体现出了前期设计的重要性)

一种思路是将长度比较大、不常用的信息,移到扩展表,拿我们的user_score表来说,其数据量庞大,同时answer字段远大于其他字段,如果是查询用户的排名,按得分展示列表的场景,那么一般点击详情才会去看回答,因此,answer非常适合抽成扩展表。

水平划分呢?

那就把这个表分成多个

水平垂直划分呢?

先把解答内容拆出去 再拆成多个表

范围拆分呢?

那就得看业务需求了 你可以根据用户拆分数据 但不太现实 用户太多了 可以通过时间划分

一般有两种选择:①在公共包里实现;②在一个中间件服务实现。

公共包实现

本地依赖包,即将分表逻辑写在公共的代码库里,每个需要调用服务的客户方都集成该公共包,就接入了自动分表的能力。

优点在于简单,不引入新的组件,不增加运维难度。缺点是公共包更改后每个客户端都需要更新。在访问数据库的服务较少且完全可控时,可以选择该方案。

中间件实现

可以是服务级别的中间件,有自己独立的进程,通过该进程来调用数据库,这样分表逻辑就是中心化,完全可控的,代理服务就属于这类。

这种方式的优点是方便更改、耦合性低、架构清晰。缺点是增加了运维成本。

推荐中间件实现喵

分表分库面临的问题

分库分表过程中

在设计之初就考虑清楚,最好层层评审,一开始就设计一个长期稳定的分表策略,尽量不要对已有表进行拆分。如果实在不得已,需要对老项目进行拆分,通常要考虑如下几个问题:

1. 分表过程中,是否可以停服?

2. 如果不停服,怎么保证数据一致性?

针对不同场景,自然有不同的解决方案,这里只讲一种最复杂的情况,即在持续比较大的访问流量下,如何在不停服的情况下进行拆分?

通常来说, 可以按如下几个阶段操作:

1. 双写读老阶段:通过中间件,对write sql同时进行两次转发,也就是双写,保持新数据一致,同时开始历史数据拷贝。本阶段建议施行一周;

就是说 用户来了一条业务如果是 写入的操作 那就把他分别写入新表和旧表 读的话还得从旧表里面读(新表里面没多少数据呢)同时转移数据

2. 双写双读阶段:采用灰度策略,一部分流量读老表,一部分流量读新表,读新表的部分在一开始,还可以同时多读一次老表数据,进行比对检查,观察无误后,随着时间慢慢切量到新表。本阶段建议施行至少两周;

此时写操作仍然 对两个表 进行操作

这个时候呢 新库里面已经有一部数据了 我们可以尝试在新库中读取数据了 但是呢稳妥起见 还是和旧库的数据对比后再返还给用户 随着数据转移的进程 我们可以渐渐的把读重心 放在新表中 

同时数据转移不能停

 3.双写读新阶段此时基本已经稳定,可以只读新表,为了安全保证,建议还是多双写一段时间,防止有问题遗漏。本阶段建议周期一个月;

图片

写新读新阶段:此时已经完成了分表的迁移,老表数据可以做个冷备。

图片

1. 分布式事务问题

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

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

2.跨节点 JOIN 查询问题

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

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

3.跨节点分页查询问题

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

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

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

4.全局主键 ID 问题

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

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

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

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

 

5.扩容问题

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Kubernetes ConfigMap - Secret - 使用ConfigMap来配置 Redis

目录 ConfigMap : 参考文档:k8s -- ConfigMap - 简书 (jianshu.com) K8S ConfigMap使用 - 知乎 (zhihu.com) ConfigMap的作用类型: 可以作为卷的数据来源:使用 ConfigMap 来配置 Redis | Kubernetes 可以基于文件创建 Conf…

服务器介绍

本文章转载与b战up主谈三国圈,仅用于学习讨论,如有侵权,请联系博主 机架型服务器 堆出同时服务百万人次机组 刀型服务器 服务器炸了 比如用户访问量暴增 超过机组的峰值处理能力,进而导致卡顿或炸服, 适合企业的塔式…

idea下tomcat运行乱码问题解决方法

idea虚拟机选项添加-Dfile.encodingUTF-8

jdk1.7与jdk1.8的HashMap区别1-基本结构与属性对比

一、数据结构差别 1.7:数组链表 1.8:数组链表红黑树 当链表的长度大于8时,数组长度大于64,原来的链表数据结构变为红黑树 二、HashMap中的关键属性和方法区别 方法/变量/类 JDK7 JDK8 备注 DEFAULT_INITIAL_CAPACITY 16 16…

一个类似Office用户界面的WPF库

博主介绍: 🌈一个10年开发经验.Net老程序员,微软MVP、博客专家、CSDN/阿里云 .Net领域优质创作者,专注于.Net领域知识、开源项目分享!🌈 🛕文末获取,加入交流群🛕 &#…

HTML一些基础知识

1、Web标准:主要包含结构、表现、行为。结构用于对网页元素进行整理和分类,主要指HTML。表现用于设置网页元素的板式、颜色、大小等外观样式,主要指的是CSS。行为主要指的是网页模型的定义以及交互的编写,主要是js文件。 Html相当…

css定义超级链接a标签里面的title的样式

效果: 代码: 总结:此css 使用于任何元素,不仅仅是a标签!

时序预测 | MATLAB实现NARX-ANFIS时间序列预测

时序预测 | MATLAB实现NARX-ANFIS时间序列预测 目录 时序预测 | MATLAB实现NARX-ANFIS时间序列预测效果一览基本介绍研究内容程序设计参考资料效果一览

基于Open3D的点云处理13-分割

平面分割(基于RANSAC) 使用RANSAC算法从点云中拟合平面; 接口:segment_plane 测试:Plane-segmentation import open3d as o3dpcd_point_cloud o3d.data.PCDPointCloud() pcd o3d.io.read_point_cloud(pcd_point_cl…

安防监控视频汇聚EasyCVR修改录像计划等待时间较长,是什么原因?

安防监控视频EasyCVR视频融合汇聚平台基于云边端智能协同,支持海量视频的轻量化接入与汇聚、转码与处理、全网智能分发等。音视频流媒体视频平台EasyCVR拓展性强,视频能力丰富,具体可实现视频监控直播、视频轮播、视频录像、云存储、回放与检…

基于传统检测算法hog+svm实现图像多分类

直接上效果图: 代码仓库和视频演示b站视频005期: 到此一游7758258的个人空间-到此一游7758258个人主页-哔哩哔哩视频 代码展示: 数据集在datasets文件夹下 运行01train.py即可训练 训练结束后会保存模型在本地 运行02pyqt.py会有一个可视化…

【C语言】函数重难点之函数递归

大家好,我是深鱼~ 目录 一、函数递归知识讲解 1.什么是递归? 2.递归的两个必要条件 2.1练习1: 2.2练习2: 二、递归与迭代 2.1练习3 2.2练习4 一、函数递归知识讲解 1.什么是递归? 程序调用自身的编程技巧称为…

Github Copilot在JetBrains软件中登录Github失败的解决方案

背景 我在成功通过了Github Copilot的学生认证之后,在VS Code和PyCharm中安装了Github Copilot插件,但在PyCharm中插件出现了问题,在登录Github时会一直Retrieving Github Device Code,最终登录失败。 我尝试了网上修改DNS&…

ARM裸机-8

1、ARM的编程模式和工作模式 1.1、ARM的基本设定 ARM采用的是32位架构 ARM约定: - Byte:8 bits - Halfword :16 bits (2 byte) - Word:32 bits (4 byte) 大部分ARM core 提供: - ARM 指令集 (32-bit) - Thumb 指令集 …

微信小程序中使用echarts方法

小程序中使用echarts echarts是一个基于JS的数据可视化图标库,它提供了直观,生动,可交互,可个性定制的数据可视化图表。一般在vue中会使用到,并且官网也详细的说明了如何在vue中使用,但是今天我想来探讨的…

【解析excel】利用easyexcel解析excel

【解析excel】利用easyexcel解析excel POM监听类工具类测试类部分测试结果备注其他 EasyExcel Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题&…

iOS开发-自定义TabbarController与Tabbar按钮Badge角标

iOS开发-自定义Tabbar按钮Badge角标 Tabbar是放在APP底部的控件。UITabbarController是一个非常常见的一种展示模式了。比如微信、QQ都是使用tabbar来进行功能分类的管理。 一、实现自定义Tabbar 我这里Tabbar继承于系统的UITabBar,定义背景图、线条的颜色、tab…

AI编程常用工具 Jupyter Notebook

点击上方蓝色字体,选择“设为星标” 回复”云原生“获取基础架构实践 深度学习编程常用工具 我们先来看 4 个常用的编程工具:Sublime Text、Vim、Jupyter。虽然我介绍的是 Jupyter,但并不是要求你必须使用它,你也可以根据自己的喜…

VS Code环境配置问题

VS Code 环境配置问题 文章目录 VS Code 环境配置问题配置 C问题解决不乱码只显示结果避免闪退,中文乱码 配置 Java下载 JDKJDK 环境配置安装插件 配置 C 跟着官网教程(英文版)和其他博客配置了一遍,却遇到了很多小问题&#xff…

应用无线鼠标中的2.4GHz无线收发芯片

无线键盘和无线鼠标作为现代办公环境中常见的工具,为我们的工作带来了便利。无线键盘和无线鼠标的工作原理都是基于无线技术实现的,其中常见的是2.4GHz无线技术。让我们一起来详细了解一下它们的工作原理。 无线鼠标的原理非常简单,鼠标部分工作与传统鼠…