每日一个MySQL问题: MTS 高并发下主从表空间不一致的问题

news2024/9/21 20:24:17

简单记录原因,最近我也遇到这样的问题,涉及的知识点其实很多,我也仅仅是简单分析了一下,供参考。模拟版本8.0.28。


一、问题说明和模拟方式

就是主从一个表,主库大约600M,从库大约900M,当然主从的环境肯定是一致的,但是从库的并发比较高MTS使用了16个 worker线程,从并发来看基本都在使用。

我模拟的方法也很简单,无非就是主库开启writeset,将参数binlog_transaction_dependency_history_size调大,这样能够保证last commit尽可能降低,如下:

mysql> set global binlog_transaction_dependency_history_size=1000000;
mysql> set global binlog_transaction_dependency_tracking='writeset';
表结构
mysql> show create table mytestpri  \G
*************************** 1. row ***************************
       Table: mytestpri
Create Table: CREATE TABLE `mytestpri` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(20) COLLATE utf8mb4_general_ci DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2337812 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
1 row in set (0.01 sec)

然后循环每次插入100行数据。使用语句
insert into mytestpri(name) select name from mytestpri limit 100;

这样生成的binlog的last commit大概如下:

反正就是很多相同的可以在从库并发执行。这样达到的目的就是主库看起来是连续插入的,因为是自增,但是到了从库并发下则不是连续的,是可以并发的,那么比如990-1000这个数据可能在1000-1010之后才插入,虽然提交是考虑了顺序的(slave_preserve_commit_order=ON),但是执行并没有顺序而是并发的。

下面是我模拟的16线程woker的并发执行截图。

从这个输出我们显然能看到几个状态,首先协调线程在等待worker线程处理事务,而几个worker都在处理事务插入的数据。
经过这个操作过后,发现主库的表为70M,但是从库的数据文件大小有110M,然后当我把MTS关闭后从库的数据文件大小也是70M了如下:

其中mytestpri_mts.ibd为mts并发的,mytestpri_sql.ibd为单SQL线程执行的,我单独将他们拷贝出来了。下面是主库的数据文件大小:

那么说明在MTS大并发下,数据文件大了40M左右。

二、问题分析

首先我们需要明确,主从数据是通过binlog event进行传递的,也就是不是物理page级别的,而是在从库需要重新执行一遍的,这也为这种问题的出现提供的可能。

前面我们说从库并发执行了这些insert语句,那么打个比方很可能trx1和trx2交叉写入数据,trx1上次插入的值为1000,但是下次插入的值为trx2的990,再下一次插入的又是trx1的1001,当trx2插入991的时候,发现其插入点为990,而不是1001,这样带来的影响是PAGE_LAST_INSERT这个值会哦不断的前后变动,且大概率在分裂的时候,对比当前插入点并不是上一次的PAGE_LAST_INSERT,导致分裂可能选择middle分裂方式,也就是从page的中间分开,也就是pageold和pagenew各自占用了50%的空间,但是pageold剩下的50%可能用不到多少了,因为从大体来讲,一旦990-1000这个数据插入完成过后,pageold的空间就永远不会再用了,这样就浪费了大量的空间。
如果是总体按照顺序执行,比如单SQL线程,则不会有这种情况,分裂会选择right顺序分裂,且分裂点始终为最后一个记录,那么pageold为100%,pagenew为0%,这样新插入的数据在pagenew里面使用空间。

这个问题实际上只要稍微看看btr_page_split_and_insert函数的分裂方式就可以发现如下:

 else if (btr_page_get_split_rec_to_right(cursor, &split_rec)) { //右分裂
    direction = FSP_UP;
    hint_page_no = page_no + 1; 

  } else if (btr_page_get_split_rec_to_left(cursor, &split_rec)) { //左分裂
    direction = FSP_DOWN;
    hint_page_no = page_no - 1;
    ut_ad(split_rec); 
  } else {
    direction = FSP_UP;
    hint_page_no = page_no + 1;

    /* If there is only one record in the index page, we
    can't split the node in the middle by default. We need
    to determine whether the new record will be inserted
    to the left or right. */

    if (page_get_n_recs(page) > 1) { 
      split_rec = page_get_middle_rec(page); //中间进行分裂 
    } else if (btr_page_tuple_smaller(cursor, tuple, offsets, n_uniq, heap)) {
      split_rec = page_rec_get_next(page_get_infimum_rec(page));
    } else {
      split_rec = nullptr;
    }
  }

这里几个函数分别对应了响应的分裂点选择方式,

  • btr_page_get_split_rec_to_right
  • btr_page_get_split_rec_to_left
  • page_get_middle_rec

而原则就是上次插入点PAGE_LAST_INSERT是否和本次插入点的选择相同,如果相同则可能是顺序插入,不同则可能是乱序插入,如下:

page_header_get_ptr(page, PAGE_LAST_INSERT) == insert_point

这个函数后面还有移动page的方式,就不过多解释了。

并发写入,除了引发大量的middle分裂外,从算法来看,乱序的方式即便是right分裂也可能不会选择让pageold存在100%的数据,因为这个选择就是看当前记录插入的点是否和上一次插入的记录点相同,如果相同则选择当前点的下一条记录作为分裂点,比如trx1 插入了1000-1100数据,然后trx2插入990-1000的数据,他们是顺序执行的,当trx2执行到995的时候需要分裂,那么可能995之前的数据在一个oldpage,而995之后的在一个newpage,这样分裂其实2个page也都是有数据的。

总的来说乱序插入,不管是middle分裂还是right分裂都可能导致page出现大量的碎片,而且这些碎片空间不一定都能用到,导致了大量的空间损耗。

三、问题验证

要验证这个问题,因为page太多了,不可能去一个一个page的查看,只能在关键位置加输出,我加入的输出为:

  • 分裂方式
  • 分裂点选择的offset

如果MTS下大量的选择了middle分裂方式则可能浪费大量的空间,且分裂次数更多,而分裂点为page的逻辑链表,可能是一种乱序的方式,因为插入的顺序不规定,那么小的记录可能在物理空间(heap no)的后面,但是实际上在逻辑链表的中间。

  • 单SQL线程执行

这里只截取的一部分输出,其实都是一样的,其中112 就是sup伪列的offset,且几乎所有的分裂都是right分裂,且选择的点都是sup伪列,那么pageold会存放当前100%的数据,papenew 就存放新的数据。

  • MTS并发执行

反正差不多就是惨不忍睹,又是middle分裂,又是right分裂,而且逻辑链表的offset基本是乱的,那么这种情况下肯定有大量的page碎片出现。

  • 对比分裂次数

其中new1.err为MTS并发下的分裂输出,new2.err为 单SQL线程下的输出,可以看到分裂次数多很多,middle分裂mts下很多,但是单SQL线程下几乎没有。很显然分裂次数越多也测面体现page碎片比较多。当然也可以借助一些工具来查看page的碎片程度,可以自行搜索一下,我用得不多吧。

当然MTS作为很好的特性,并且作为新版本默认的特性,不管是主从还是MGR都大量使用。当主从空间不一致的情况下,我们不需要太惊讶,知道原因就可以了,这也是当前版本无法避免的。最后也可以试试重组空间alter table engine=innodb,来释放这些碎片,不过大表的话代价实在有点高。

四、其他可能的原因

  • 除了这种碎片导致,还遇到过del flag 在从库不清理的案例,这可能导致主从之间的空间相差N倍,参考:
    MySQL:主从表大小相差巨大和一个BUG - 简书 MySQL:主从表大小相差巨大和一个BUG

  • 叶老师的叶问16期,也说明一下其他可能的情况,这里就不涉及了。

在主从服务器上,同一个表的表空间文件大小相差特别大,可能原因是什么,怎么解决?可能的原因:
1、MySQL表默认是InnoDB引擎且目前索引只支持B+树索引,在数据的增删改过程中,会导致表产生碎片,主从服务器上同张表的碎片率不同也会导致表空间相差很大(这个应该就是我们这里说的问题了)
2、主库整理过碎片,从库是从原先的未整理的物理备份中恢复出来的
3、主从表结构不一致,如从库可能比主库多索引
4、主从表的行格式不一致,如主库为dynamic,从库为compressed
5、个别云数据库在从库上可能采用特殊的并行复制技术,导致在从库上有更高的碎片率(有个极端的案例,同一个表在主库只有6G,从库上则有将近150G)

解决方式:
1、保证主从表结构一致(包括page大小、索引、行格式等)
2、在业务低峰期使用pt-osc或gh-ost通过alter table xxx engine=innodb;重整表空间,消除碎片(切记:执行前要先检查有无未结束事务或其他未释放锁)

  • GreatSQL社区,最近一篇类似问题的解析,可以自行参考

MySQL批量导入数据时,为何表空间膨胀了N倍

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

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

相关文章

推荐一个网络安全网站(HTML)

废话不多,直接上链接https://www.hackthissite.org/ 这不是一个新手向的网站,建议有一定基础的同学访问,里面大部分都是黑客实操,对大部分希望学习网络安全并有一定基础的人都可以有较大提升 全区英文,这对各位应该不…

Java面试题汇总(包含算法题及底层知识)

花了很久时间整理了Java领域互联网公司常考的面试题,主要包含六大类: 第一类:Java及Javaweb常考基础题及高级题,包含了很多公司爱问的非常冷门的知识点,很多面试官喜欢通过问非常冷门的知识来判断面试者的基础掌握程度。 第二类:Java虚拟机以…

EasyRecovery16电脑硬盘数据恢复软件功能讲解

硬盘是很常见的存储数据的设备,硬盘中很多重要的数据一旦丢失会很麻烦,不过现在有硬盘数据恢复软件可以自行在家恢复数据。今天的文章就带大家来看看硬盘恢复数据的软件EasyRecovery。 EasyRecovery 是一款专业的数据恢复软件,支持恢复不同存…

【Proteus仿真】51单片机Blink点灯实验

【Proteus仿真】51单片机Blink点灯实验 🔖Proteus仿真基础实验-点亮第一个LED灯。🌿Proteus8.12平台🌿本实验代码基于VSM Studio,采用SDCC编译器。🎬仿真演示: ⛳头文件使用说明 ⚡第一次调用STC89.h头文…

MySQL is null 走不走索引?

网上很多的说法,都是说不能走索引。但其实是错误的。 其实也是又可能走索引的。 比如,select * from table where a xxx or a is null; 通过explain 看这条sql的执行计划,type ref_or_null,这条sql语句会查询两次,第…

带拉绳的按钮

看看效果&#xff1a; 再上代吗&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><script src"https://unpkg.co/gsap3/dist/gsap.min.js"></scr…

可视区域兼容性问题的思考及方法封装

今日在复习可视化尺寸获取时突发奇想&#xff0c;为什么要在怪异模式下使用document.body.clientWidth&#xff0c;在标准模式下使用document.documentElement.clientWidth&#xff1f;以及是否在IE8及以下的版本中其中一个获取方式将返回undefined或0。  出于该问题的思考&am…

C++的cin详解

2023年5月20日&#xff0c;周六早上&#xff1a; 我发现我找不到非常详细的cin类的成员函数&#xff0c;只好自己写了。 不定期更新。 cin的继承关系 cin类继承自istream类&#xff0c;ostream类继承自ios类&#xff0c;ios类继承自ios_base类 cin类拥有的所有成员函数 1. …

【算法题】2352. 相等行列对

插&#xff1a; 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站。 坚持不懈&#xff0c;越努力越幸运&#xff0c;大家一起学习鸭~~~ 题目&#xff1a; 给你一个下标从 0 开始、大小为 n …

ChatGPT 推出 iOS 应用,支持语音输入,使用体验如何?

最近&#xff0c;OpenAI 宣布推出官方 iOS 应用&#xff0c;允许用户随时随地访问其高人气 AI 聊天机器人&#xff0c;此举也打破了近几个月内苹果 App Store 上充斥似是而非的山寨服务的窘境。 该应用程序是 ChatGPT 的首个官方移动应用程序。ChatGPT 软件程序在去年推出后迅速…

Inertial Explorer处理pospac数据总结

Inertial Explorer处理pospac数据的过程包括&#xff1a;1&#xff09;从pospac提取出gps数据和imu数据&#xff1b;2&#xff09;gps数据转成rinex格式&#xff1b;3)imu数据转成imr格式&#xff1b;4&#xff09;IE对gps数据进行PPP解算&#xff1b;5&#xff09;紧耦合融合解…

帆软FineReport之版本升级

最近有个需求是将帆软报表版本从9升级到10&#xff0c;记录升级过程&#xff0c;方便备查。 前置条件&#xff1a;服务器上帆软10部署成功 版本区别 fineReport 9.0 http://IP:Port/WebReport/ReportServer?reportletabc.cpt fineReport 10.0 http://IP:Port/webroot/decision…

biopython: runningtime:generator didnot stop after throw

在运行某个蛋白【3NPS】的时候出现报错: 代码: parse=PDBParse(QUIET=True) structure=parser.get_structure(X,pdb) 报错: runningtime:generator didnot stop after throw 解决: (1)检查是否biopython版本和python 版本不一致: Biopython 支持多个 Python 版本…

第一章:VMware 虚拟机安装

Linux 的开发需要在 Linux 系统下进行&#xff0c;这就要求我们的 PC 主机安装 Linux 系统&#xff0c;本篇我 们选择 Ubuntu 这个 Linux 发行版系统。本篇讲解如何安装虚拟机&#xff0c;以及如何在虚拟机中安装 Ubuntu 系统&#xff0c;安装完成以后如何做简单的设置。如…

Android大作业(三)——修改应用图标

Android大作业&#xff08;三&#xff09;——修改应用图标 系列文章目录前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 在Android studio开发安卓应用时&#xff0c;工程中带有默认应用图标。安卓的图标有圆角、方形、高低像素的不同版本&#x…

这就是冒泡排序,像可乐中的气泡滋滋向上冒一样(44)

小朋友们好&#xff0c;大朋友们好&#xff01; 我是猫妹&#xff0c;一名爱上Python编程的小学生。 和猫妹学Python&#xff0c;一起趣味学编程。 今日主题 什么是冒泡排序&#xff1f; 用Python写段代码&#xff0c;实现冒泡排序。 冒泡排序 冒泡排序(Bubble Sort)是一种…

springboot+jsp高校社交校友交流平台的设计与实现

在学校里我们结识了很多朋友。当我们毕业离校走上各自的人生道路&#xff0c;这份友谊将成为宝贵的人生精神财富。但世事变迁&#xff0c;或许我们原本留下的联系方式已经不能再用&#xff0c;使得朋友之间失去联系&#xff0c;更别提相聚&#xff0c;这份精神财富也将丢失。这…

一文会用断码屏

断码屏的使用 1、断码屏显示文字原理 我理解应该是偏压原理达到显示效果的。 LCD驱动分为A型、B型&#xff0c;如果LCD偏压类型为C型&#xff0c;固定为 1/3 偏压。 由数据手册得知&#xff0c;以下&#xff1a; LCD 驱动器提供的 COM 和 SEG 输出数目&#xff0c;以及偏压…

【运维知识进阶篇】集群架构-Nginx四层负载均衡详解

四层负载均衡含义及应用场景 四层负载均衡是基于传输层协议包来封装的&#xff08;如&#xff1a;TCP/IP&#xff09;&#xff0c;那我们介绍的的七层是指的应用层&#xff0c;他的组装在四层的基础之上&#xff0c;无论四层还是七层都是指的OSI网络模型。我们之前介绍了七层负…

LabVIEWCompactRIO 开发指南24 第5章通过LabVIEW FPGA定制硬件

LabVIEWCompactRIO 开发指南24 第5章通过LabVIEW FPGA定制硬件 本章将介绍了一些练习&#xff0c;以及使用LabVIEW FPGA模块和CompactRIO开发高性能控制和监测系统的高级技巧和技巧。它介绍了推荐的编程实践、避免常见错误方法以及创建快速、高效且可靠的LabVIEW FPGA应用程序…