dbaplus社群 2024年12月30日
MySQL大数据表处理策略,原来一直都用错了……
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文深入探讨了MySQL大数据表处理的三大方案:数据表分区、分库分表以及冷热数据归档。文章首先分析了大数据量导致查询效率降低的根本原因,即索引B+树层级过高,磁盘IO增多。随后,详细介绍了每种方案的原理、优缺点及适用场景,如表分区适用于区间查询,分库分表解决单表数据量过大问题,冷热归档则针对数据访问频率差异进行优化。文章还对比了分区和分表的区别与联系,并指出了分库分表可能带来的事务、跨库Join等问题。最后,为读者提供了选型建议,帮助根据自身业务场景选择合适的解决方案。

🗂️ 表容量评估:主要从表的记录数、平均长度、增长量、读写量和总大小量进行评估。通常,OLTP表建议不超过2000万行数据量和15GB大小。当数据量过大时,传统的`select count(*)`查询可能会超时,应使用`show table status`命令获取更详细的表信息。

🗄️ 磁盘空间与实例容量:数据量建议占磁盘使用率的70%以内。MySQL是基于线程的服务模型,高并发场景下单实例可能无法充分利用CPU资源,需考虑实例模式。单表数据量过大导致查询效率下降的根本原因是维护索引的B+树结构层级变高,查询时磁盘IO增多。

🔨 三大解决方案:包括数据表分区、分库分表和冷热数据归档。表分区将数据按照条件分布到不同文件,但仍指向同一张表,适用于区间查询;分库分表通过水平或垂直拆分降低单表数据量,提高查询效率;冷热数据归档将不常用的冷数据迁移至其他库表,优化热数据操作效率。

💡 分区与分表:分区是在物理上对表文件进行拆分,表名不变;分表则是将一张表拆分成多张独立的表。分区主要提高磁盘读写能力,而分表则提高MySQL并发能力。两者可以结合使用,对于访问量大且数据多的表,可以同时采用分表和分区。

2024-12-30 07:15 广东

详解三大方案及优缺点,MySQL大数据表处理一次性说明白!

场景


当我们业务数据库表中的数据越来越多,如果你也和我遇到了以下类似场景,那让我们一起来解决这个问题。



评估表数据体量


我们可以从表容量/磁盘空间/实例容量三方面评估数据体量,接下来让我们分别展开来看看。


1.表容量


表容量主要从表的记录数、平均长度、增长量、读写量、总大小量进行评估。一般对于OLTP的表,建议单表不要超过2000W行数据量,总大小15G以内。


访问量:单表读写量在1600/s以内


查询行数据的方式:我们一般查询表数据有多少数据时用到的经典sql语句如下。


但是当数据量过大的时候,这样的查询就可能会超时,所以我们要换一种查询方式。



上述方法不仅可以查询表的数据,还可以输出表的详细信息 , 加 \G 可以格式化输出。包括表名、存储引擎、版本、行数、每行的字节数等等,大家可以自行试一下哈。


2.磁盘空间


查看指定数据库容量大小


selecttable_schema as '数据库',table_name as '表名',table_rows as '记录数',truncate(data_length/1024/1024, 2) as '数据容量(MB)',truncate(index_length/1024/1024, 2) as '索引容量(MB)'from information_schema.tablesorder by data_length desc, index_length desc;


查询单个库中所有表磁盘占用大小


selecttable_schema as '数据库',table_name as '表名',table_rows as '记录数',truncate(data_length/1024/1024, 2) as '数据容量(MB)',truncate(index_length/1024/1024, 2) as '索引容量(MB)'from information_schema.tableswhere table_schema='mysql'order by data_length desc, index_length desc;


查询出的结果如下:



建议数据量占磁盘使用率的70%以内。同时,对于一些数据增长较快,可以考虑使用大的慢盘进行数据归档(归档可以参考方案三)。


3.实例容量


MySQL是基于线程的服务模型,因此在一些并发较高的场景下,单实例并不能充分利用服务器的CPU资源,吞吐量反而会卡在mysql层,可以根据业务考虑自己的实例模式。


出现问题的原因



上面我们已经查到我们数据表的体量了,那么为什么单表数据量越大,业务的执行效率就越慢的根本原因是什么呢?


一个表的数据量达到好几千万或者上亿时,加索引的效果没那么明显啦。性能之所以会变差,是因为维护索引的B+树结构层级变得更高了,查询一条数据时,需要经历的磁盘IO变多,因此查询性能变慢。

大家是否还记得,一个B+树大概可以存放多少数据量呢?


InnoDB存储引擎最小储存单元是页,一页大小就是16k。


B+树叶子存的是数据,内部节点存的是键值+指针。索引组织表通过非叶子节点的二分查找法以及指针确定数据在哪个页中,进而再去数据页中找到需要的数据;



假设B+树的高度为2的话,即有一个根结点和若干个叶子结点。这棵B+树的存放总记录数为=根结点指针数*单个叶子节点记录行数。



因此,一棵高度为2的B+树,能存放1170 * 16=18720条这样的数据记录。同理一棵高度为3的B+树,能存放1170 *1170 *16 =21902400,也就是说,可以存放两千万左右的记录。B+树高度一般为1-3层,已经满足千万级别的数据存储。


如果B+树想存储更多的数据,那树结构层级就会更高,查询一条数据时,需要经历的磁盘IO变多,因此查询性能变慢。


如何解决单表数据量太大,查询变慢的问题


知道了根本原因之后,我们就需要考虑如何优化数据库来解决问题了。这里提供了三种解决方案,包括数据表分区、分库分表、冷热数据归档,了解完这些方案之后大家可以选取适合自己业务的方案。


方案一:数据表分区


为什么要分区:表分区可以在区间内查询对应的数据,降低查询范围,并且索引分区也可以进一步提高命中率,提升查询效率。


分区是指将一个表的数据按照条件分布到不同的文件上面,未分区前都是存放在一个文件上面的,但是它还是指向的同一张表,只是把数据分散到了不同文件而已。


我们首先看一下分区有什么优缺点:


1. 表分区有什么好处?



2. 表分区的限制因素



在进行分区之前可以用如下方法,看下数据库表是否支持分区。


mysql> show variables like '%partition%';+-------------------+-------+| Variable_name     | Value |+-------------------+-------+| have_partitioning | YES   |+-------------------+-------+1 row in set (0.00 sec)


方案二:数据库分表


为什么要分表:分表后,显而易见,单表数据量降低,树的高度变低,查询经历的磁盘io变少,则可以提高效率。


mysql 分表分为两种,水平分表和垂直分表。


分库分表就是为了解决由于数据量过大而导致数据库性能降低的问题,将原来独立的数据库拆分成若干数据库组成 ,将数据大表拆分成若干数据表组成,使得单一数据库、单一数据表的数据量变小,从而达到提升数据库性能的目的。



定义:数据表行的拆分,通俗点就是把数据按照某些规则拆分成多张表或者多个库来存放。分为库内分表和分库。


比如一个表有4000万数据,查询很慢,可以分到四个表,每个表有1000万数据




定义:列的拆分,根据表之间的相关性进行拆分。常见的就是一个表把不常用的字段和常用的字段就行拆分,然后利用主键关联。或者一个数据库里面有订单表和用户表,数据量都很大,进行垂直拆分,用户库存用户表的数据,订单库存订单表的数据。



缺点:垂直分隔的缺点比较明显,数据不在一张表中,会增加join 或 union之类的操作。


知道了两个知识后,我们来看一下分库分表的方案。


1. 取模方案


拆分之前,先预估一下数据量。比如用户表有4000w数据,现在要把这些数据分到4个表user1 user2  uesr3 user4。


比如id = 17,17对4取模为1,加上 ,所以这条数据存到user2表。


注意:进行水平拆分后的表要去掉auto_increment自增长。这时候的id可以用一个id 自增长临时表获得,或者使用  redis incr的方法。



2. range 范围方案


以范围进行拆分数据,就是在某个范围内的订单,存放到某个表中。比如id=12存放到user1表,id=1300万的存放到user2 表。




我们看到以上两种方案都存在缺点,但是却又是互补的,那么我们将这两个方案结合会怎样呢?


3. hash取模和range方案结合


如下图,我们可以看到 group 组存放id 为0~4000万的数据,然后有三个数据库 DB0、DB1、DB2,DB0里面有四个数据库,DB1 和DB2 有三个数据库。


假如id为15000 然后对10取模(为啥对10 取模 因为有10个表),取0 然后 落在DB_0,然后再根据range 范围,落在Table_0 里面。



总结:采用hash取模和range方案结合,既可以避免热点数据的问题,也有利于将来对数据的扩容。


我们已经了解了 mysql分区和分表的知识,那我们看一下这两个技术有何不同以及适用场景


1)分区分表的区别:



2)分区分表的联系:



3)分库分表存在的问题:



在执行分库分表之后,由于数据存储到了不同的库上,数据库事务管理出现了困难。如果依赖数据库本身的分布式事务管理功能去执行事务,将付出高昂的性能代价;如果由应用程序去协助控制,形成程序逻辑上的事务,又会造成编程方面的负担。



在执行了分库分表之后,难以避免会将原本逻辑关联性很强的数据划分到不同的表、不同的库上,这时,表的关联操作将受到限制,我们无法join位于不同分库的表,也无法join分表粒度不同的表,结果原本一次查询能够完成的业务,可能需要多次查询才能完成。



额外的数据管理负担,最显而易见的就是数据的定位问题和数据的增删改查的重复执行问题,这些都可以通过应用程序解决,但必然引起额外的逻辑运算,例如,对于一个记录用户成绩的用户数据表userTable,业务要求查出成绩最好的100位,在进行分表之前,只需一个order by语句就可以搞定,但是在进行分表之后,将需要n个order by语句,分别查出每一个分表的前100名用户数据,然后再对这些数据进行合并计算,才能得出结果。


方案三:冷热归档


为什么要冷热归档:其实原因和方案二类似,都是降低单表数据量,树的高度变低,查询经历的磁盘io变少,则可以提高效率。


如果大家的业务数据,有明显的冷热区分,比如:只需要展示近一周或一个月的数据。那么这种情况这一周和一个月的数据我们称之为热数据,其余数据为冷数据。那么我们可以将冷数据归档在其他的库表中,提高我们热数据的操作效率。


接下来讲一下归档的过程:






以上三种方案我们如何选型


方案使用场景
优点
缺点
数据表分区
1.数据量较大
2.查询场景只在某个区
3.没有联合查询的场景
分区分表是在物理上对数据表所对应的文件进行拆分,对应的表名是不变的,所以不会影响到之前业务逻辑的sql1.分表后的查询等业务会创建对应的对象,也会造成一定的开销分区数据若要聚合的话 耗费时间也较长;
2.使用范围不适合数据量千万级以上的。
数据表分表数据量较大,无法区分明显冷热区,且数据可以完整按照区间划分适用于对冷热分区的界限不是很明显的数据,对后续类似的数据可以采用该方式,将大表拆分成小表,提高查询插入等效率1.若大数据表逐渐增多 那么对应的数据库表越来越多 每个表都需要分表;
2.区间的划分较为固定,若后续单表的数据量大起来 也会对性能造成影响;
3.实现复杂度相对方案三比较复杂,需要测试整个实现过程,在编码层处理对原有业务有影响。
冷热归档分库1.数据量较大
2.数据冷热分区明显
3.冷数据使用频率极低
数据迁移的过程对业务的影响较小,开发量也较少减少成本需要确认分表规则


大家可以根据自己的业务场景,去选择合适自己业务的方案,我这边就给大家提供一下思路~到这里内容就差不多结束了,如果有什么不对的,或者有什么疑惑,欢迎大家指点!


>>>>

参考资料


作者丨马佩

来源丨稀土掘金:juejin.cn/post/7146016771936354312

dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn

阅读原文

跳转微信打开

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

MySQL 大数据 表分区 分库分表 冷热归档
相关文章