首页 » SEO优化 » Phpmysql存入图片技巧_mysql总结笔记006表空间释放count效率

Phpmysql存入图片技巧_mysql总结笔记006表空间释放count效率

访客 2024-12-10 0

扫一扫用手机浏览

文章目录 [+]

InnoDB 引擎谈论

一个 InnoDB 表包含两部分,即:表构造定义和数据。
在 MySQL 8.0 版本以前,表构造是存在以.frm 为后缀的文件里。
而 MySQL 8.0 版本,则已经许可把表构造定义放在系统数据表中了。

Phpmysql存入图片技巧_mysql总结笔记006表空间释放count效率

1.1 表构造和表数据如何存放?

Phpmysql存入图片技巧_mysql总结笔记006表空间释放count效率
(图片来自网络侵删)

表数据既可以存在共享表空间里,也可以是单独的文件。
这个行为是由参数 innodb_file_per_table 掌握的:

1. 这个参数设置为 OFF 表示的是,表的数据放在系统共享表空间,也便是跟数据字典放在一起;

2. 这个参数设置为 ON 表示的是,每个 InnoDB 表数据存储在一个以 .ibd 为后缀的文件中。
表构造和表数据分开存放。

从 MySQL 5.6.6 版本开始,它的默认值便是 ON 了。

将 innodb_file_per_table 设置为 ON,是推举做法,我们接下来的谈论都是基于这个设置展开的

我们在删除全体表的时候,可以利用 drop table 命令回收表空间。
但是,我们碰着的更多的删除数据的场景是删除某些行,这时就碰着了问题:表中的数据被删除了,但是表空间却没有被回收。

1.2 数据删除流程

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

InnoDB 的数据是按页存储的,那么如果我们删掉了一个数据页上的所有记录,会怎么样?答案是,全体数据页就可以被复用了。

1.2.1 数据页的复用跟记录的复用是不同的

1 记录的复用,只限于符合范围条件的数据。
比如上面的这个例子,R4 这条记录被删除后,如果插入一个 ID 是 400 的行,可以直接复用这个空间。
但如果插入的是一个 ID 是 800 的行,就不能复用这个位置了。

2 当全体页从 B+ 树里面摘掉往后,可以复用到任何位置。
如果将数据页 page A 上的所有记录删除往后,page A 会被标记为可复用。
这时候如果要插入一条 ID=50 的记录须要利用新页的时候,page A 是可以被复用的。

如果我们用 delete 命令把全体表的数据删除呢?结果便是,所有的数据页都会被标记为可复用。
但是磁盘上,文件不会变小。

delete 命令实在只是把记录的位置,或者数据页标记为了“可复用”,但磁盘文件的大小是不会变的。
也便是说,通过 delete 命令是不能回收表空间的。
这些可以复用,而没有被利用的空间,看起来就像是“空洞”。

1.2.2 删除,插入,更新索引值都会造成空洞

如果数据是按照索引递增顺序插入的,那么索引是紧凑的。
但如果数据是随机插入的,就可能造成索引的数据页分裂。

假设图 1 中 page A 已经满了,这时我要再插入一行数据,会若何呢?

可以看到,由于 page A 满了,再插入一个 ID 是 550 的数据时,就不得不再申请一个新的页面 page B 来保存数据了。
页分裂完成后,page A 的末端就留下了空洞(把稳:实际上,可能不止 1 个记录的位置是空洞)。
其余,更新索引上的值,可以理解为删除一个旧的值,再插入一个新值。
不难明得,这也是会造成空洞的。

造成空洞缘故原由:插入,删除,更新索引上的值。
经由大量增编削的表,都是可能是存在空洞的。
以是,如果能够把这些空洞去掉,就能达到紧缩表空间的目的。
而重修表,就可以达到这样的目的。

1.3 重修表

如果你现在有一个表 A,须要做空间紧缩,为了把表中存在的空洞去掉,你可以怎么做呢?

笨办法:新建一个与表 A 构造相同的表 B,然后按照主键 ID 递增的顺序,把数据一行一行地从表 A 里读出来再插入到表 B 中。
由于表 B 是新建的表,以是表 A 主键索引上的空洞,在表 B 中就都不存在了。
用表 B 更换 A,从效果上看,就起到了紧缩表 A 空间的浸染

利用 alter table A engine=InnoDB 命令来重修表。
在 MySQL 5.5 版本之前,这个命令的实行流程跟我们前面描述的差不多,差异只是这个临时表 B 不须要你自己创建,MySQL 会自动完成转存数据、交流表名、删除旧表的操作。

图3

显然,花韶光最多的步骤是往临时表插入数据的过程,如果在这个过程中,有新的数据要写入到表 A 的话,就会造成数据丢失。
因此,在全体 DDL 过程中,表 A 中不能有更新。
也便是说,这个 DDL 不是 Online 的。

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

我给你大略描述一下引入了 Online DDL 之后,重修表的流程:

图4 Online DDL

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

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

3. 天生临时文件的过程中,将所有对 A 的操作记录在一个日志文件(row log)中,对应的是图中 state2 的状态;

4. 临时文件天生后,将日志文件中的操作运用到临时文件,得到一个逻辑数据上与表 A 相同的数据文件,对应的便是图中 state3 的状态;

5. 用临时文件更换表 A 的数据文件。

特殊把稳:重修方法都会扫描原表数据和构建临时文件。
对付很大的表来说,这个操作是很花费 IO 和 CPU 资源的。
因此,如果是线上做事,你要很小心地掌握操作韶光。
如果想要比较安全的操作的话,我推举你利用 GitHub 开源的 gh-ost 来做。
https://www.cnblogs.com/zhoujinyi/p/9187421.html

1.4 Online 和 inplace

在图 3 中,我们把表 A 中的数据导出来的存放位置叫作 tmp_table。
这是一个临时表,是在 server 层创建的。
在图 4 中,根据表 A 重修出来的数据是放在“tmp_file”里的,这个临时文件是 InnoDB 在内部创建出来的。
全体 DDL 过程都在 InnoDB 内部完成。
对付 server 层来说,没有把数据挪动到临时表,是一个“原地”操作,这便是“inplace”名称的来源。

如果你有一个 1TB 的表,现在磁盘间是 1.2TB,能不能做一个 inplace 的 DDL 呢?

答案是不能。
由于,tmp_file 也是要占用临时空间的。

我们重修表的这个语句 alter table t engine=InnoDB,实在隐含的意思是:

alter table t engine=innodb,ALGORITHM=inplace;

跟 inplace 对应的便是拷贝表的办法了,用法是:

alter table t engine=innodb,ALGORITHM=copy;

当你利用 ALGORITHM=copy 的时候,表示的是逼迫拷贝表,对应的流程便是图 3 的操作过程。

inplace 跟 Online 是不是便是一个意思?实在不是的,只是在重修表这个逻辑中刚好是这样而已。

比如,如果我要给 InnoDB 表的一个字段加全文索引,写法是:alter table t add FULLTEXT(field_name);这个过程是 inplace 的,但会壅塞增编削操作,是非 Online 的。

如果说这两个逻辑之间的关系是什么的话,可以概括为:

1. DDL 过程如果是 Online 的,就一定是 inplace 的;

2. 反过来未必,也便是说 inplace 的 DDL,有可能不是 Online 的。
截止到 MySQL 8.0,添加全文索引(FULLTEXT index)和空间索引 (SPATIAL index) 就属于这种情形。

利用 optimize table、analyze table 和 alter table 这三种办法差异

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

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

optimize table t 即是 recreate+analyze。

小结

如果要紧缩一个表,只是 delete 掉表里面不用的数据的话,表文件的大小是不会变的,你还要通过 alter table 命令重修表,才能达到表文件变小的目的。
我跟你先容了重修表的两种实现办法,Online DDL 的办法是可以考虑在业务低峰期利用的,而 MySQL 5.5 及之前的版本,这个命令是会壅塞 DML 的,这个你须要特殊小心。

Truncate 会开释表空间吗?

Truncate 可以理解为drop+create

如果将 alter 操作显式的放到事务里 ,事务不提交 , 另一个事务查询的时候会查询到alter 操作后的表构造 , 比如新增了一个字段。
这个是什么缘故原由 ,是否冲破了 mvcc 的定义呢?

好问题 , 不过alter table 语句会默认提交前面的事务,然后自己独立实行、

optimize table t 即是 recreate+analyze

老师请教个问题recreate出来该当是险些全新的,analyze的必要性?

作者回答: 好问题,这个得是比较极度的情形下才有必要,以是我比较喜好直接用alter

1.5 思考题

假设现在有人碰到了一个“想要紧缩表空间,结果揠苗助长”的情形,看上去是这样的:

1. 一个表 t 文件大小为 1TB;

2. 对这个表实行 alter table t engine=InnoDB;

3. 创造实行完成后,空间不仅没变小,还轻微大了一点儿,比如变成了 1.01TB。

(1)这个表,本身就已经没有空洞的了,比如说刚刚做过一次重修表操作。
在 DDL 期间,如果刚好有外部的 DML 在实行,这期间可能会引入一些新的空洞。

(2)在重修表的时候,InnoDB 不会把整张表占满,每个页留了 1/16 给后续的更新用。
也便是说,实在重修表之后不是“最”紧凑的。

如果是这么一个过程:

1. 将表 t 重修一次;

2. 插入一部分数据,但是插入的这些数据,用掉了一部分的预留空间;

3. 这种情形下,再重修一次表 t,就可能会涌现问题中的征象。

2 count()这么慢,我该怎么办?

2.1 count() 的实现办法

在不同的 MySQL 引擎中,count() 有不同的实现办法。

MyISAM 引擎:把一个表的总行数存在了磁盘上,因此实行 count() 的时候会直接返回这个数,效率很高。
如果加了 where 条件的话,MyISAM 表也是不能返回得这么快的

InnoDB 引擎:,它实行 count() 的时候,须要把数据一行一行地从引擎里面读出来,然后累积计数。

为什么 InnoDB 不跟 MyISAM 一样,也把数字存起来呢?

由于纵然是在同一个时候的多个查询,由于多版本并发掌握(MVCC)的缘故原由,InnoDB 表“该当返回多少行”也是不愿定的。

假设表 t 中现在有 10000 条记录,我们设计了三个用户并行的会话。

会话 A 先启动事务并查询一次表的总行数;

会话 B 启动事务,插入一行后记录后,查询表的总行数;

会话 C 先启动一个单独的语句,插入一行记录后,查询表的总行数。

我们假设从上到下是按照韶光顺序实行的,同一行语句是在同一时候实行的。

图 1 会话 A、B、C 的实行流程

在末了一个时候,三个会话 A、B、C 会同时查询表 t 的总行数,但拿到的结果却不同。

这和 InnoDB 的事务设计有关系,可重复读是它默认的隔离级别,在代码上便是通过多版本并发掌握,也便是 MVCC 来实现的。
每一行记录都要判断自己是否对这个会话可见,因此对付 count() 要求来说,InnoDB 只好把数据一行一行地读出依次判断,可见的行才能够用于打算“基于这个查询”的表的总行数。

MySQL,在实行 count() 操作的时候还是做了优化的。
InnoDB 是索引组织表,主键索引树的叶子节点是数据,而普通索引树的叶子节点是主键值。
以是,普通索引树比主键索引树小很多。
因此,MySQL 优化器会找到最小的那棵树来遍历。
在担保逻辑精确的条件下,只管即便减少扫描的数据量,是数据库系统设计的通用法则之一。

show table status 这个命令的输出结果里面也有一个 TABLE_ROWS 用于显示这个表当前有多少行,这个命令实行挺快的,那这个 TABLE_ROWS 能代替 count() 吗?

索引统计的值是通过采样来估算的。
TABLE_ROWS 便是从这个采样估算得来的,官方文档说偏差可能达到 40% 到 50%。
以是,show table status 命令显示的行数也不能直策应用。

小结一下:

MyISAM 表虽然 count() 很快,但是不支持事务;show table status 命令虽然返回很快,但是不准确;InnoDB 表直接 count() 会遍历全表,虽然结果准确,但会导致性能问题。

如果你现在有一个页面常常要显示交易系统的操作记录总数,到底该当怎么办呢?答案是,我们只能自己计数。

2.2 用缓存系统保存计数

对付更新很频繁的库来说,你可能会第一韶光想到,用缓存系统来支持。

你可以用一个 Redis 做事来保存这个表的总行数。
这个表每被插入一行 Redis 计数就加 1,每被删除一行 Redis 计数就减 1。
这种办法下,读和更新操作都很快,但你再想一下这种办法存在什么问题吗?

没错,缓存系统可能会丢失更新。
但实际上,将计数保存在缓存系统中的办法,还不但是丢失更新的问题。
纵然 Redis 正常事情,这个值还是逻辑上禁绝确的。

设想一下有这么一个页面,要显示操作记录的总数,同时还要显示最近操作的 100 条记录。
那么,这个页面的逻辑就须要先到 Redis 里面取出计数,再到数据表里面取数据记录。

在 T3 时候会话 B 来查询的时候,会显示出新插入的 R 这个记录,但是 Redis 的计数还没加 1。
这时候,就会涌现我们说的数据不一致。

会话 B 在 T3 时候查询的时候,Redis 计数加了 1 了,但还查不到新插入的 R 这一行,也是数据不一致的情形。

并发系统里面,我们是无法精确掌握不同线程的实行时候的,由于存在图中的这种操作序列,以是,我们说纵然 Redis 正常事情,这个计数值还是逻辑上禁绝确的。

2.3 在数据库保存计数

如果我们把这个计数直接放到数据库里单独的一张计数表 C 中,又会怎么样呢?首先,这办理了崩溃丢失的问题,InnoDB 是支持崩溃规复不丢数据的。

现在我们就利用“事务”这个特性,把问题办理掉。

虽然会话 B 的读操作仍旧是在 T3 实行的,但是由于这时候更新事务还没有提交,以是计数值加 1 这个操为难刁难会话 B 还不可见。
因此,会话 B 看到的结果里, 查计数值和“最近 100 条记录”看到的结果,逻辑上便是同等的。

2.4 不同的 count 用法

在 select count(?) from t 这样的查询语句里面,count()、count(主键 id)、count(字段) 和 count(1) 平分歧用法的性能,

这count() 是一个聚合函数,对付返回的结果集,一行行地判断,如果 count 函数的参数不是 NULL,累计值就加 1,否则不加。
末了返回累计值。

以是,count()、count(主键 id) 和 count(1) 都表示返回知足条件的结果集的总行数;而 count(字段),则表示返回知足条件的数据行里面,参数“字段”不为 NULL 的总个数。

剖析性能差别的时候,你可以记住这么几个原则:

1. server 层要什么就给什么;

2. InnoDB 只给必要的值;

3. 现在的优化器只优化了 count() 的语义为“取行数”,其他“显而易见”的优化并没有做。

(1)对付 count(主键 id) 来说,InnoDB 引擎会遍历整张表,把每一行的 id 值都取出来,返回给 server 层。
server 层拿到 id 后,判断是不可能为空的,就按行累加。
count(id)可能会选择最小的索引来遍历

(2)对付 count(1) 来说,InnoDB 引擎遍历整张表,但不取值。
server 层对付返回的每一行,放一个数字“1”进去,判断是不可能为空的,按行累加。

单看这两个用法的差别的话,你能比拟出来,count(1) 实行得要比 count(主键 id) 快。
由于从引擎返回 id 会涉及到解析数据行,以及拷贝字段值的操作。

(3)对付 count(字段) 来说:

1. 如果这个“字段”是定义为 not null 的话,一行行地从记录里面读出这个字段,判断不能为 null,按行累加;

2. 如果这个“字段”定义许可为 null,那么实行的时候,判断到有可能是 null,还要把值取出来再判断一下,不是 null 才累加。
count(字段)的话,如果字段上没有索引,就只能选主键索引

(4)但是 count() 是例外,并不会把全部字段取出来,而是专门做了优化,不取值。
count() 肯定不是 null,按行累加。

以是结论是:按照效率排序的话,count(字段)<count(主键 id)<count(1)≈count(),以是我建议你,只管即便利用 count()。

小结

在不同引擎中 count() 的实现办法是不一样的,也剖析了用缓存系统来存储计数值存在的问题。
实在,把计数放在 Redis 里面,不能够担保计数和 MySQL 表里的数据精确同等的缘故原由,是这两个不同的存储构成的系统,不支持分布式事务,无法拿到精确同等的视图。
而把计数值也放在 MySQL 中,就办理了同等性视图的问题。
InnoDB 引擎支持事务,我们利用好事务的原子性和隔离性,就可以简化在业务开拓时的逻辑。
这也是 InnoDB 引擎备受青睐的缘故原由之一。

2.5 思考题

在谈论的方案中,我们用了事务来确保计数准确。
由于事务可以担保中间结果不被别的事务读到,因此修正计数值和插入新记录的顺序是不影响逻辑结果的。
但是,从并发系统性能的角度考虑,你以为在这个事务序列里,该当先插入操作记录,还是该当先更新计数表呢?

由于更新计数表涉及到行锁的竞争,先插入再更新能最大程度地减少了事务之间的锁等待,提升了并发度。

标签:

相关文章

小偷网站php技巧_PHP采集 抓取

一、 什么是php采集程序?二、 为什么要采集?三、 采集些什么?四、 如何采集?五、 采集思路六、 采集范例程序七、 采集心得什...

SEO优化 2024-12-12 阅读0 评论0