MySQL CRUD 与 DDL 的执行逻辑

278 阅读11分钟

本文将着重分析一下常见CRUD 与 DDL 的执行逻辑。主要是对一些参考文章的整理,方便自己理解。

1 select simple

主要涉及到对索引的利用,可以在后面再分析。

2 select join

参考 time.geekbang.org/column/arti…

2.1 可以使用被驱动表的索引:( Index nested-loop join)

2.1.1 步骤

1 对驱动表 t1 做了全表扫描,这个过程需要扫描 100 行;

2 而对于每一行 R,根据 a 字段去表 t2 查找,走的是树搜索过程。由于我们构造的数据都是一一对应的,因此每次的搜索过程都只扫描一行,也是总共扫描 100 行;所以,整个执行流程,总扫描行数是 200。

2.1.2 小结

1、使用 join 语句,性能比强行拆成多个单表执行 SQL 语句的性能要好;

2、如果使用 join 语句的话,需要让小表做驱动表。

2.2 不能使用被驱动表的索引:(Simple Nested-Loop Join 或者“Block Nested-Loop Join)

2.2.1 步骤

t1 是驱动表,t2被驱动表

(1)把表 t1 的数据读入线程内存 join_buffer 中,由于我们这个语句中写的是 select *,因此是把整个表 t1 放入了内存;

(2)扫描表 t2,把表 t2 中的每一行取出来,跟 join_buffer 中的数据做对比,满足 join 条件的,作为结果集的一部分返回。

2.2.2 小结

应该让小表当驱动表。

如果小表在 join_buffer 中 放不下,则需要在内存中分段存储了。

2.3 优化

time.geekbang.org/column/arti…

2.3.1 针对 Index nested-loop join(NLJ) 的优化-- BKA 算法

原来 NLJ 从驱动表中每次取一行,然后去被驱动表匹配;

而 BKA 算法是 一次性从驱动表中取多行数据,然后放入临时内存 join_buffer 中排序,然后再一起传给被驱动表。排序的目的是可以利用顺序性,从被驱动表拿数据。

2.3.2 BNL 算法的优化

1 BNL 对系统的三个影响

(1)多次扫描被驱动表

(2)join 比较次数多,如果是大表就会占用非常多的 CPU 资源

(3)Buffer pool 的热数据被淘汰,影响内存命中率。

2 常见优化

2.2.1 常见做法:给被驱动表的 join 字段加上索引,把 BNL 转换为 BKA 算法。

2.2.2 如果不能加索引,就采用临时表的做法:

将被驱动表的数据放入临时表,并且给临时表增加 join 字段的索引。

image.png

BKA 算法流程

3 select limit

blog.csdn.net/u014745069/…

7步:SELECT 查询列表

第1步:FROM 表名

第2步:JOIN 关联表

第3步:ON 关联条件

第4步:WHERE 筛选条件

第5步:GROUP BY 分组字段

第6步:HAVING 分组后筛选条件

第8步:ORDER BY 排序字段

第9步:LIMIT 输出限制

4 select group by / order by

1、www.cnblogs.com/yayazhang22…


Group By 和 Having, Where ,Order by这些关键字是按照如下顺序进行执行的:Where, Group By, Having, Order by

2、www.modb.pro/issue/7216


select语句的执行顺序为:

1、加载表数据到内存,没有索引时,会将表中所有的数据都加载到内存;有索引时,则根据索引和where条件有针对性的取出符合条件的数据,再加载到内存

2、无索引时,执行where条件,留下符合条件的内容,删除其它不符合条件的

3、执行group by子句,使用内存中的一块区域,专门用于分组,留下select指定的需要的列内容和新产生的聚合列的内容,其它删除

4、执行having子句,对group by的结果进行条件过滤

5、执行order by子句

6、执行limit子句

根据以上的执行顺序,个人理解为:

select语句在取数据的时候,一开始是取整行数据的,然后在group by的时候会剔除其它未被选中的列,你的语句没有用到group by,所以没有剔除,最后再用order by排序的时候,num列还是存在的,所以可以排列;只是最后显示给你看的时候才把多余列剔除掉。

5 insert

5.1 insert 的大体流程 mysql.taobao.org/monthly/201…


1、给表加 IX 锁

2、插入记录

(1)获取 row_id 的索引

(2)遍历所有索引,向每个索引中插入记录

(2.1) 主键冲突检查

(2.2)插入索引。 如果正在 ONLINE_DDL ,则先记录 insert,待 online_ddl 结束的时候再执行 insert.

5.2 insert 过程中的锁

www.aneasystone.com/archives/20…


我们看 insert 语句的调用堆栈,一开始时还比较容易理解,从 mysql_parse -> mysql_execute_command -> mysql_insert -> write_record -> handler::ha_write_row -> innobase::write_row -> row_insert_for_mysql,这里就进入 InnoDb 引擎了。

然后继续往下跟:row_ins_step -> row_ins -> row_ins_index_entry_step -> row_ins_index_entry -> row_ins_clust_index_entry -> row_ins_clust_index_entry_low -> btr_cur_optimistic_insert -> btr_cur_ins_lock_and_undo -> lock_rec_insert_check_and_lock。


InnoDb 在插入记录时,是不加锁的。如果事务 A 插入记录且未提交,这时事务 B 尝试对这条记录加锁,事务 B 会先去判断记录上保存的事务 id 是否活跃,如果活跃的话,那么就帮助事务 A 去建立一个锁对象,然后自身进入等待事务 A 状态,这就是所谓的隐式锁转换为显式锁。



执行 insert 语句,对要操作的页加 RW-X-LATCH,然后判断是否有和插入意向锁冲突的锁,如果有,加插入意向锁,进入锁等待;如果没有,直接写数据,不加任何锁,结束后释放 RW-X-LATCH;

执行 select ... lock in share mode 语句,对要操作的页加 RW-S-LATCH,如果页面上存在 RW-X-LATCH 会被阻塞,没有的话则判断记录上是否存在活跃的事务,如果存在,则为 insert 事务创建一个排他记录锁,并将自己加入到锁等待队列,最后也会释放 RW-S-LATCH;

6 delete

6.1 mysql 删除其实是个假删除

cloud.tencent.com/developer/a…

只会置一个删除标识,并不会真实的删除空间。空间是会进行复用的。

删除记录的行 空间可以被复用,叫做行记录复用;

还有一种是数据页复用,是指整个数据页都被标记删除了,然后这个页被复用了。

6.2 mysql 写操作过程

blog.csdn.net/weixin_4268…

image.png

读操作的流程图:

image.png

6.3 为什么表数据删掉一半,表文件大小不变?time.geekbang.org/column/arti…

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

7 select 与 update 的执行流程

blog.csdn.net/zwx900102/a…

redo log, undo log, bin log 的写入顺序。

8 DDL (增加索引、增加字段、删除字段、增加多个字段)

8.1 ONLINE DDL 的整体介绍

juejin.cn/post/684490…

1 ONLINE DDL 虽然可以支持 并发DML,但是也会有短暂的锁表操作。


无论任何模式下,Online DDL开始之前都需要一个短时间排它锁(exclusive)来准备环境,所以alter命令发出后,会首先等待该表上的其它操作完成,在alter命令之后的请求会出现等待waiting meta data lock。同样在DDL结束之前,也要等待alter期间所有的事务完成,也会堵塞一小段时间。所以尽量在ALTER TABLE之前确保没有大事务在执行,否则一样出现连环锁表。

2 Online DDL 存在的问题:


主从复制延迟,只有主库上DDL执行成功才会写入到binlog中,而DDL操作在从库上不能并发执行,因此即使主库执行DDL时允许并发DML操作,对于大表操作,仍会引发严重的复制延迟。

主库执行Online DDL时,不能根据负载暂停DDL操作。

使用Inplace方式执行的DDL,发生错误或被KILL时,需要一定时间的回滚期,执行时间越长,回滚时间越长。

使用Copy方式执行的DDL,需要记录过程中的undo和redo日志,同时会消耗buffer pool的资源,效率较低,优点是可以快速停止。

Online DDL并不是所有时间段的Online,在特定时间段需要加元数据锁或其他锁。

允许并发DML的DDL,可能会导致Duplicate entry问题。

3 建议


执行DDL前查看下该表有没有被事务占用,防止出现MDL锁。

执行DDL前确保datadir,tmpdir磁盘空间足够。

能业务低峰期操作的DDL,都尽量安排在业务低峰期进行。

对于大表和较大表,如果对复制延迟和主库性能敏感,建议改为gh-ost或pt-osc工具。

对于并发操作较高的表,无论表数据量多少,不能在业务高峰期操作。

同个表的多个DDL语句可以合并在一起进行,避免多次table rebuild带来的消耗。但是也要注意分组,比如需要copy table和只需inplace就能完成的,应该分两个alter语句。

8.2不要在业务期间进行DDL操作

www.cnblogs.com/timePasser-…

1 MDL 锁


MDL锁,全称为metadata lock,即元数据锁。MDL锁的主要作用是维护表元数据的数据一致性,当表上有活动事务(注意MDL锁伴随事务提交而释放,而不是SQL结束而释放)的时候,不可以对元数据(即表结构)进行任何修改操作。

2 MDL 锁 队列机制,先到先得


MDL锁申请遵循一个队列机制,即先到先得,因此如果一个DDL操作一直无法得到MDL X锁,那么后续所有该表上的SQL都会等待这个DDL操作拿到MDL X锁并且释放为止,这也是为啥我们经常听到不要在业务期间进行DDL操作的原因之一,DDL操作很容易因为某个慢SQL导致后续所有的SQL都被卡住(等待MDL锁)

8.3 Online DDL

1、DDL 的问题


copy:创建新表结构的临时表,锁原表禁止DML,将数据copy到临时表,完成后删除原表,重命名新表,需要拷贝原始表,执行过程中源表不允许写但可读。

inplace:在进行DDL操作时,MDL写锁会降级为MDL读锁,这样就可以支持并发DML,然后通过row_log记录原表上的DML增量操作,最后通过回放增量数据保证数据一致性。

rebuild table:部分DDL操作类型在inplace模式下,需要进行重建表(原表基础上进行更新),往往表越大越费时。

no rebuild table:部分DDL操作类型在inplace模式下,不需要重建表,往往只需要修改元数据,因此速度比较快。

instant:从 MySQL 8.0.12 才开始引入,加列操作可以不需要重建表,只需要修改元数据,可以实现秒加列。

2 Online DDL过程


PREPARE:会申请MDL X锁,然后更新数据字典并分配row_log开始记录表上的DML增量数据,这个过程如果没有被MDL锁阻塞,那么是非常快的。

DDL:将所持有的MDL X锁降级为MDL S锁。

一方面进行数据拷贝,比如建二级索引或者重建原表。

另一方面记录这期间产生的DML日志,写到row_log中后进行数据重放。

COMMIT:等到重放至最后一个block时,从MDL S锁升级到MDL X锁,回放最后一个block,最后更新数据字典,完成DDL,释放MDL锁。

8.3 MySQL Online DDL的改进与应用 www.cnblogs.com/xinysu/p/67… (写的非常棒)

1 旧的 DDL 方式


COPY 方式:copy 整个表,copy 期间不可以 DML

inplace 方式:不需要 copy 整个表格,只需要在原来的 ibd 文件上,新建所需要的索引页,减少了 IO。 但是只支持索引的创建与删除,不支持其他 DDL。

2 Online DDL

语句类型划分:

6b4e354ca1621ca04559c9e342e1d77a.png

3 Online DDL 优势


Online DDL可以有效改善DDL期间对数据库的影响:

(1)Online DDL期间,查询和DML操作在多数情况下可以正常执行,对表格的锁时间也会大大减少,尽可能的保证数据库的可扩展性;

(2)允许 in-place 操作的 DDL,避免重建表格占用过多磁盘IO及CPU资源,减少对数据库的整体负荷,使得在DDL期间,能够维持数据库的高性能及高吞吐量;

(3)允许 in-place 操作的 DDL,比需要COPY到临时文件的操作要更少占用buffer pool,避免以往DDL过程中性能的临时下降,因为以前需要拷贝数据到临时表,这个过程会占用到buffer pool ,导致内存中的部分频繁访问的数据会被清理出去。

4 MySQL InnoDB Online DDL学习

www.cnblogs.com/dbabd/p/103…

(4.1) 5.6.7 之前版本的 inplace 用途及弊端:


INPLACE方式:

INPLACE方式也称为InnoDB fast index creation,是MySQL5.5及之后版本为了提高创建二级索引效率的方式,所以INPLACE方式仅限于二级索引的创建跟删除

相对于COPY方式,INPLACE方式在原表上进行,不会生成临时表,也不会拷贝原表数据,减少了很多系统I/O资源占用,但还是无法进行DML操作,也只适用于索引的创建与删除,并不适用于其他类型的DDL语句。