【MySQL系列相关】

1.聊一聊关于MySQL的count(*)

1.背景

在完成一个分表项目后,发现分表的数据迁移后,新库所需的存储容量远大于原本两张表的大小。在做了一番查询了解后,完成了优化。

回过头来,需要进一步了解下为什么会出现这样的情况。

与标题的问题的类似问题还有,为什么表数据内容删除了而表大小没有变化。其本质都是一样的。

要回答这些问题,我们需要从mysql的索引模型谈起。

只是作为封面图:)

2.InnoDB 的索引模型

在 MySQL 中,索引是在存储引擎层实现的,所以并没有统一的索引标准,即不同存储引擎的索引的工作方式并不一样。

而即使多个存储引擎支持同一种类型的索引,其底层的实现也可能不同。由于 InnoDB 存储引擎在 MySQL 数据库中使用最为广泛,所以接下来就以 InnoDB 为例,分析其中的索引模型。

在 InnoDB 中,表都是根据主键顺序以索引的形式存放的,这种存储方式的表称为索引组织表。而InnoDB中,使用了 B+ 树索引模型,所以数据都是存储在 B+ 树中的,每一个索引会对应一颗B+树。

假设,我们有一个主键列为 ID 的表,表中有字段 k,并且在 k 上有索引,建表语句如下

CREATE TABLE `t` (

`id` int(11) NOT NULL,

`k` int(11) NOT NULL,

`name` varchar(16) DEFAULT NULL,

PRIMARY KEY (`id`),

KEY `k` (`k`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8

表中 R1~R5 的 (ID,k) 值分别为 (10,1)、(20,2)、(30,3)、(50,5) 和 (70,7),索引id和索引k的B+树的示例示意图如下。

根据叶子节点的内容,索引类型分为主键索引和非主键索引,主键索引的叶子节点存的是整行数据R1~R5,非主键索引的叶子节点内容是主键的值。

从图中可以看出,基于非主键索引的查询需要多扫描一棵索引树才能找到对应的数据。提一句题外话,我们在应用中应该尽量使用主键查询。

3.索引维护

B+ 树为了维护索引有序性,在增删改数据的时候需要做必要的维护。

假设,我们要删掉 R4 这个记录,InnoDB 引擎只会把 R4 这个记录标记为删除。如果之后要再插入一个 ID 在 300 和 600 之间的记录时,可能会复用这个位置。

如果删掉了一个数据页上的所有记录,那么整个数据页就能被复用了。进一步地,如果我们用 delete 命令把整个表的数据删除呢?结果就是,这个表相关的所有的数据页都会被标记为可复用。

但是,无论如何,磁盘文件的大小并不会缩小。

这些被标记为可复用,而并没有实际被使用的空间,就是一些“存储空洞”。

实际上,不止是删除数据会造成空洞,插入数据也会。

以上图为例,如果插入新的行 ID 值为 80,则只需要在 R5 的记录后面插入一个新记录。

如果新插入的 ID 值为 60,就相对麻烦了,需要逻辑上挪动后面的数据,空出位置。

而更糟的情况是,如果 R5 所在的数据页已经满了,根据 B+ 树的算法,这时候需要申请一个新的数据页,然后挪动部分数据过去。这个过程称为页分裂。在这种情况下,性能自然会受影响。

除了性能外,页分裂操作还影响数据页的利用率。原本放在一个页的数据,现在分到两个页中,插入一条记录竟然使得整体空间利用率降低大约 50%。

可以看到,由于 page 2 满了,再插入一个 ID 是 60 的数据时,就不得不再申请一个新的页面 page 3 来保存数据了。

页分裂完成后,page 2 的末尾就留下了空洞(注意:实际上,可能不止 1 个记录的位置是空洞)。

另外,更新索引上的值,可以理解为删除一个旧的值,再插入一个新值。不难理解,这也是会造成空洞的。

因此,大量的增删改之后的表,都是可能存在很大的“数据空洞”的。

因此,我们就能解释,为什么分表后的总存储变大了。

因为分表后,需要从老库全量同步数据到新库,数据同步平台开启多个线程进行同步,插入各个分表并不是按照递增的顺序插入的,因此,会产生巨量的“数据空洞”,造成存储空间变大。

如果能够把这些空洞去掉,就能达到收缩表空间的目的。而重建表就能达到这样的目的。

4.重建表

如果我们手动重建一张表,可以新建一个与表 A 结构相同的表 B,然后按照主键 ID 递增的顺序,把数据一行一行地(就是递增地)从表 A 里读出来再插入到表 B 中。由于表 B 是新建的表,所以表 A 主键索引上的空洞,在表 B 中就都不存在了。显然地,表 B 的主键索引更紧凑,数据页的利用率也更高。如果我们把表 B 作为临时表,数据从表 A 导入表 B 的操作完成后,用表 B 替换 A,从效果上看,就起到了收缩表 A 空间的作用。

这里,你可以使用 alter table A engine=InnoDB 命令来重建表。在 MySQL 5.5 版本之前,这个命令的执行流程跟我们前面描述的差不多,区别只是这个临时表 B 不需要你自己创建,MySQL 会自动完成转存数据、交换表名、删除旧表的操作。显然,花时间最多的步骤是往临时表插入数据的过程,如果在这个过程中,有新的数据要写入到表 A 的话,就会造成数据丢失。因此,在整个 DDL 过程中,表 A 中不能有更新。也就是说,这个 DDL 不是 Online 的。

MySQL 5.6 版本开始引入的 Online DDL,对这个操作流程做了优化。

建立一个临时文件,扫描表 A 主键的所有数据页;

用数据页中表 A 的记录生成 B+ 树,存储到临时文件中;

生成临时文件的过程中,将所有对 A 的操作记录在一个日志文件(row log)中;

临时文件生成后,将日志文件中的操作应用到临时文件,得到一个逻辑数据上与表 A 相同的数据文件;(应用row log的过程可能又回有页分裂)

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

可以看到,在这个过程中,由于日志文件记录和重放操作这个功能的存在,这个方案在重建表的过程中,允许对表 A 做增删改操作。这也就是 Online DDL 名字的来源。

需要补充说明的是,上述的这些重建方法都会扫描原表数据和构建临时文件。对于很大的表来说,这个操作是很消耗 IO 和 CPU 资源的。因此,如果是线上服务,你要很小心地控制操作时间。

optimize table、analyze table 和 alter table 这三种方式重建表的区别:

从 MySQL 5.6 版本开始,alter table t engine = InnoDB(也就是 recreate)默认的就是上面online DDL 的流程了;

analyze table t 其实不是重建表,只是对表的索引信息做重新统计,没有修改数据,这个过程中加了 MDL 读锁;

optimize table t 等于 recreate+analyze。

参考文献:

《MySQL实战45讲》

《MySQL技术内幕》

非常感谢您能看到这里,如果觉得文章还不错的话,

求在看,求关注,求分享,求留言。

希望能跟大家一起学习,共同进步。

mysql为什么表大了要重建_为什么MySQL分库分表后总存储大小变大了?相关推荐

  1. SQL数据库不用SQL语句能显示全表的内容_阿里巴巴数据库分库分表的实践

    在2006年阿里巴巴B2B团队以开源方式研发了Cobar这一关系型数据的分布式处理系统.该系统在很大程度上解决了最初使用Oracle数据库因为存储数据变得越来越大带来的扩展性问题,并且为开发人员提供了 ...

  2. Linux+MySQL+MyCat实现分库分表,通过MyCat数据库中间件实现分库分表配置实战

    目录 前言 Linux+MySQL+MyCat实现读写分离,主从同步的解决方案 一.Linux下MySQL数据库服务的安装与部署 二.下载Linux MyCat 三.上传Linux服务器,并解压 四. ...

  3. docker二进制安装mysql_Docker搭建MySQL读写分离主从模式 分布式数据库中间件Mycat分库分表应用...

    一.MySQL读写分离主从模式 1. 下载镜像 docker pull mysql 当前最新版本:mysql Ver 8.0.19 for Linux on x86_64 (MySQL Communi ...

  4. mysql 分库分表中间件 mycat_阿里开源的分布式分库分表中间件之MyCat从入门到放弃...

    原标题:阿里开源的分布式分库分表中间件之MyCat从入门到放弃 1.非分片字段查询 Mycat中的路由结果是通过分片字段和分片方法来确定的.例如下图中的一个Mycat分库方案: 根据 tt_waybi ...

  5. MySQL高性能:索引、锁、事务、分库分表如何撑起亿级数据

    最近项目增加,缺人手,面试不少,但匹配的人少的可怜.跟其他组的面试官聊,他也抱怨了一番,说候选人有点儿花拳绣腿,回答问题不落地,拿面试最常问的MySQL来说,并不只是懂"增删改查" ...

  6. 主从复制MySQL的安装和用数据库中间件MyCat实现分库分表、读写分离

    1.MySql主从复制 1.1.安装mysql 1.1.1.下载 下载地址:https://dev.mysql.com/downloads/mysql/ 1.1.2.卸载预装mysql #查看已安装: ...

  7. oracle分库分表原理_题库分库分表架构方案

    方案 项目背景 在现在题库架构下,针对新购买的1300W多道数据进行整合,不影响现有功能.由于数据量偏多,需要进行数据的切分 目标场景 兼容旧的功能 对1300多W数据进行分库分表 需要对旧的数据进行 ...

  8. mysql分表插件_分库分表简单?那我想问如何实现“分库分表插件”?

    随着系统数据量的日益增长,在说起数据库架构和数据库优化的时候,我们难免会常常听到分库分表这样的名词. 当然,分库分表有很多的方法论,比如垂直拆分.水平拆分:也有很多的中间件产品,比如MyCat.Sha ...

  9. mysql分表后怎么索引_分库分表后的索引问题

    摘要 最近遇到一个慢sql,在排查过程中发现和分库分表后的索引设置有关系,总结了下问题. 问题 在进行应用健康度盘点时,发现有个慢sql 如下 select brandgoodid from bran ...

最新文章

  1. tp3 普通模式url模式_Thinkphp 3.2.3 url 路由访问模式
  2. 【深度学习篇】--神经网络中的调优二,防止过拟合
  3. 浏览器URL地址里一堆%或者6E616D6531之类的是啥?编码
  4. jenkins+github+docker+maven自动化构建部署
  5. 攻破JAVA NIO技术壁垒
  6. CentOS 7 解决丢失 nginx.pid
  7. hdu 4160 Dolls 匈牙利算法求最大匹配
  8. Codeforce 1042 D. Petya and Array(树状数组、前缀和)
  9. 北大教授李忠:谁说学数学只是为了升学?数学可以让你受益终生!
  10. etcd官方推荐的硬件配置
  11. Flask mysql
  12. Scala案例:评定成绩等级
  13. Android开发UI之GridLayout的使用
  14. html 多标签页,html更多标签
  15. offset,client,scroll的学习记录
  16. Linux入门之常用命令(10)软连接 硬链接
  17. python背景怎么自定义铃声_Python 上课铃声的定时播放(具有较强的自我管理意识.jpg)...
  18. 【学习笔记】powell法的python实现
  19. iOS 一种连线题效果的实现
  20. 高斯列主元消去法解线性方程组

热门文章

  1. 未能加载文件或程序集“Iesi.Collections”或它的某一个依赖项。参数错误。 (异常来自 HRESULT:0x80070057 (E_INVALIDARG))
  2. 为TIF、JPG图片添加地理坐标/平面直角坐标
  3. C++list常用接口总结
  4. UCOSIII移植问题说明
  5. 【CyberSecurityLearning 20】xu ni zhuan yong wang luo
  6. 转移地址在内存中的jmp指令 检测点9.1
  7. spring,Whitelabel Error Page,This application has no explicit mapping for /error, so you are seeing
  8. 前端基础——day1
  9. if语句的一个错误记录,多了个“;”号
  10. springboot整合shiro-关于登出时,redis中缓存没有清理干净的问题