今日寄语:努力的阶段,往往是最不养生的阶段!

一张千万级的数据表,删除了一半的数据,你觉得B+树索引文件会不会变小?

(答案在文章中!!)

我们先来做个实验,看看表的大小是如何变化的??

做个实验,让数据说话

1、首先,在mysql中创建一张用户表,表结构如下:

CREATE TABLE `user` (`id` bigint(20) NOT NULL AUTO_INCREMENT,`user_name` varchar(128) NOT NULL DEFAULT '' COMMENT '用户名',`age` int(11) NOT NULL  COMMENT '年龄',`address` varchar(128) COMMENT '地址',PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8mb4 COMMENT='用户表';

2、造数据。用户表中批量插入1000W条数据

@GetMapping("/insert_batch")
public Object insertBatch(@RequestParam("batch") int batch) {// 设置批次batch=100000,共插入 1000W 条数据for (int j = 1; j <= batch; j++) {List<User> userList = new ArrayList<>();for (int i = 1; i <= 100; i++) {User user = User.builder().userName("Tom哥-" + ((j - 1) * 100 + i)).age(29).address("上海").build();userList.add(user);}userMapper.insertBatch(userList);}return "success";
}

批量插入,每个批次100条记录,100000个批次,共1000W条数据

3、查看表文件大小

索引文件大小约 595 M,最后修改时间 02:17

明:

  • MySQL 8.0 版本以前,表结构是存在以.frm为后缀的文件里

  • 独享表空间存储方式使用.ibd文件来存放数据和索引,且每个表一个.ibd文件

表数据既可以存在共享表空间,也可以是单独文件。通过innodb_file_per_table参数控制。MySQL 5.6.6 版本之后,默认是ON,这样,每个 InnoDB 表数据存储在一个以 .ibd为后缀的文件中。

4、删除 约500W条数据

@GetMapping("/delete_batch")
public Object deleteBatch(@RequestParam("batch") int batch) {for (int j = 1; j <= batch; j++) {List<Long> idList = new ArrayList<>();for (int i = 1; i <= 100; i += 2) {idList.add((long) ((j - 1) * 100 + i));}userMapper.deleteUser(idList);}return "success";
}

开始时user表有1000W条数据,删除若干后,目前剩余约 550W 条

5、在删除约500W条记录后,再次查看表文件大小

索引文件大小约 595 M,最后修改时间 10:34

实验结论:

对于千万级的表数据存储,删除大量记录后,表文件大小并没有随之变小。好奇怪,是什么原因导致的?不要着急,接下来,我们来深入剖析其中原因

数据表操作有新增、删除、修改、查询,其中查询属于读操作,并不会修改文件内容。修改文件内容的是写操作,具体分为有删除、新增、修改三种类型。

接下来,我们开始逐一分析

删除数据

InnoDB 中的数据采用B+树来组织结构。如果对B+树存储结构不清楚的话,可以先看下我之前写的一篇文章,巩固下基础知识。

面试题:mysql 一棵 B+ 树能存多少条数据?

假如表中已经插入若干条记录,构造的B+树结构如下图所示:

删除id=7这条记录,InnoDB引擎只是把id=7这条记录标记为删除,但是空间保留。如果后面有id位于(6,19)区间内的数据插入时,可以重复使用这个空间。

上图,表示新插入一条id=16的记录。

除了记录可以复用外,数据页也可以复用。当整个页从B+树摘掉后,可以复用到任何位置。

比如,将page number=5页上的所有记录删除以后,该page标记为可复用。此时如果插入一条id=100的记录需要使用新页,此时page number=5便可以被复用了。

如果相邻两个page的利用率都很低,数据库会将两个页的数据合并到其中一个page上,另一个page被标记为可复用。

当然,如果是像上面我们做的实验那样,将整个表的数据全部delete掉呢?所有的数据页都会被标记为可复用,但空间并没有释放,所以表文件大小依然没有改变。

总结:delete命令只是把数据页或记录位置标记为可复用,表空间并没有被回收,该现象我们称之为”空洞“

新增数据

如果是插入的数据是随机的非主键有序,可能会造成数据页分裂。

上图可以看到,假如page number=5的数据页已经满了,此时插入id=15的记录,需要申请一个新的页page number=6来保存数据。待页分裂完成后,page number=5的最后位置就会留下一个可复用的空洞。

相反,如果数据是按照索引递增顺序插入的,那么索引是紧凑的,不会出现数据页分裂。

修改数据

如果修改的是非索引值,那么并不会影响B+树的结构

比如,更新id=7的其它字段值,主键id保持不变。整个B+树并没有发生结构调整。

但是,如果修改的内容包含了索引,那么操作步骤是先删除一个旧的值,然后再插入一个新值。可能会造成空洞。

分析发现,新增、修改、删除数据,都可能造成表空洞,那么有没有什么办法压缩表空间??

客官,请继续往下看

新建表

我们可以新建一个影子表B与原表A的结构一致,然后按主键id由小到大,把数据从表A迁移到表B。由于表B是新表,并不会有空洞,数据页的利用率更高。

待表A的数据全部迁移完成后,再用表B替换表A。

MySQL 5.5 版本之前,提供了一键命令,快捷式完成整个流程,转存数据、交换表名、删除旧表

alter table 表名  engine=InnoDB

但是,该方案有个致命缺点,表重构过程中,如果有新的数据写入表A时,不会被迁移,会造成数据丢失。

Online DDL

为了解决上面问题,MySQL 5.6 版本开始引入  Online DDL,对流程做了优化。

执行步骤:

  • 新建一个临时文件

  • 扫描表A主键的所有数据页,生成B+ 树,存储到临时文件中

  • 在生成临时文件过程中,如果有对表A做写操作,操作会记录到一个日志文件中

  • 当临时文件生成后,再重放日志文件,将操作应用到临时文件

  • 用临时文件替换表A的数据文件

  • 删除旧的表A数据文件

新建表的最大区别,增加了日志文件记录和重放功能。迁移过程中,允许对表A做增删改操作。

面试题:mysql 表删除一半数据,B+树索引文件会不会变小???相关推荐

  1. MySQL删除退出后数据未更新,mysql一不小心删除了数据或更新了数据没有加where 条件...

    mysql一不小心删除了数据或更新了数据没有加where 条件 1,show variables like '%log_bin%'; 2.show master logs; 3.show master ...

  2. mysql空洞数据,Mysql 表空间和 数据页空洞

    一.表空间 1.表空间: innodb 引擎存储的最高层: 存放所有的数据 2.独立表空间:Mysql 版本5.6 后默认开启的单表单空间 (1)Innodb 默认存储引擎页的大小为 16K :默认表 ...

  3. mysql 表损坏_MYSQL数据表损坏的原因分析和修复方法小结(推荐)

    1.表损坏的原因分析以下原因是导致mysql 表毁坏的常见原因: 1. 服务器突然断电导致数据文件损坏. 2. 强制关机,没有先关闭mysql 服务. 3. mysqld 进程在写表时被杀掉. 4. ...

  4. MySQL 中删除的数据都去哪儿了?

    不知道大家有没有想过下面这件事? 我们平时调用 DELETE 在 MySQL 中删除的数据都去哪儿了? 这还用问吗?当然是被删除了啊 那么这里又有个新的问题了,如果在 InnoDB 下,多事务并发的情 ...

  5. Mysql批量删除大量数据

    一.Mysql批量删除大量数据 方案1 假设有一个表(syslogs)有1000万条记录,需要在业务不停止的情况下删除其中statusid=1的所有记录,差不多有600万条, 直接执行 DELETE ...

  6. mysql千万级数据怎么删除,MySQL 快速删除大量数据(千万级别)的几种实践方案详解...

    这篇文章主要介绍了MySQL 快速删除大量数据(千万级别)的几种实践方案详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧 笔者 ...

  7. php去除重复的数据保留一条,mysql查找删除重复数据并只保留一条实例详解

    有这样一张表,表数据及结果如下: school_id school_name total_student test_takers 1239 Abraham Lincoln High School 55 ...

  8. Python 基于Python从mysql表读取千万数据实践

    基于Python 从mysql表读取千万数据实践   by:授客 QQ:1033553122 场景:   有以下两个表,两者都有一个表字段,名为waybill_no,我们需要从tl_waybill_b ...

  9. sparksql加载mysql表中的数据

    sparksql加载mysql表中的数据 <dependency><groupId>mysql</groupId><artifactId>mysql-c ...

最新文章

  1. 双十一来这儿,华为昇腾的秘密都给你!
  2. java多态口诀,Java之路---Day12(多态),多态Java
  3. dict过滤 python_关于python:过滤dict以只包含某些键?
  4. TopN算法与排行榜
  5. Kettle常用的配置文件
  6. Java多线程学习笔记一
  7. Java链接MySQL练习题:格式化日期、性别;避免代码注入
  8. 推荐一个可以把网页背景色调成护眼色的Chrome扩展应用
  9. 计算机仿真在机械应用,浅谈计算机仿真在机械的应用.doc
  10. php打印mysql sql_php的打印sql语句的方法
  11. 微课|中学生可以这样学Python(8.4节):递归算法例题讲解1
  12. CTA策略05_AtrRsiStrategy
  13. 从LR到DNN点击率预估
  14. 基于Python生成Markdown的标题序号
  15. LeetCode常用算法模式大厂面试题整理
  16. win python 判断 所有 子进程 结束_python 多进程如何终止或重启子进程?
  17. 软件测试中的杀虫剂效应与金字塔模型
  18. Mysql如何添加环境变量(详细教程)
  19. React Native带你一步步实现热更新(CodePush-Android)
  20. Windows环境运行shell脚本

热门文章

  1. Python中的super()用法
  2. HDU4720(最小圆覆盖问题)
  3. poj3304(线段相交问题)
  4. 图论500题 ---- 并查集求路径上最大值最小不超过K的点对数 HDU Portal
  5. Codeforces Round #370 (Div. 2)E. Memory and Casinos[期望概率+线段树区间合并]详细推导
  6. 符合python语言变量命名规则_Python变量命名规则
  7. mysql分页 redis_分页查询和redis
  8. thinkphp6 接收不到数据_单片机红外接收与红外发射
  9. C#使用Xamarin开发可移植移动应用进阶篇(7.使用布局渲染器,修改默认布局),附源码...
  10. Handler消息处理机制详解