navicat下对mysql创建索引导致死锁,数据库崩溃,完美解决方案

文章探讨了在MySQL8.0中,即使在没有长事务的短事务场景下,执行DDL语句如添加、修改、删除字段或添加索引仍可能导致死锁,特别是对索引的操作。作者通过实验展示了死锁的情况,并提出了在执行DDL前锁定整表、设置超时时间以及使用命令行作为解决方案。同时,文章解释了在线DDL的工作原理,说明了死锁产生的原因。
摘要由CSDN通过智能技术生成

写在前面

DDL语句,就是对数据库对象(数据库、表、列、索引等)进行创建、删除、修改等。

之前分享过一篇mysql创建索引导致死锁,数据库崩溃,mysql的表级锁之【元数据锁(meta data lock,MDL)】全解

通过上一篇文章我们了解到,MySQL有一种表锁叫做元数据锁(meta data lock,MDL)元数据锁,执行DDL时会检查元数据锁并尝试获取。

之前一直以为,只要保证MySQL数据库当前没有长事务,就可以安枕无忧地执行DDL语句,我们天真的认为短事务场景中,DDL语句总是会在上一个事务结束后,获取到元数据锁,并不会有死锁的危险。

但是最近发现!事情并没有想象中的那么简单!就算是没有长事务,MySQL8.0在创建索引的时候,仍然有可能会导致死锁的发生!
我们一起来分析一下。

注!本文操作都是基于mysql 8.0.21InnoDB引擎,可重复读事务隔离级别下,navicat13版本来完成的。

非navicat环境下,结果或许不同!

一、短事务场景下,执行DDL语句场景分析

先创建一个表:

CREATE TABLE `lock_test` (
  `id` int NOT NULL,
  `name` varchar(20) DEFAULT NULL,
  `age` int DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

1、短事务场景下,执行表字段添加操作

在这里插入图片描述
我们发现,当事务A尚未提交时,事务B、事务C都会处于等待状态。

当事务A一经发起commit命令,事务B紧跟着会进行执行,并执行成功,事务C在事务B之后也会执行成功。

整个过程只有正常的事务等待,并不会发生死锁。

2、短事务场景下,执行表字段修改操作

在这里插入图片描述
我们发现,修改字段的场景下和添加字段场景下的结果是一样的。

3、短事务场景下,执行表字段删除操作

在这里插入图片描述
当该表无数据时,我们会发现,当事务A执行提交之后,事务B和事务C进入死锁,此时该表的任何SQL语句都无法执行!

此时导致数据库表死锁,数据库SQL堆积越来越多,导致数据库崩溃!

(1)往里添加一条数据试试

INSERT INTO `lock_test`(`id`, `name`, `age`, `column_name`) VALUES (1, '1', 1, '1');

在这里插入图片描述
有数据的情况和没有数据的情况,是不一样的!

有数据时,当事务A提交之后,事务C会执行成功,当事务C提交之后,事务B才是最终执行成功。

4、短事务场景下,添加索引操作

还是保持数据库中有数据。

INSERT INTO `lock_test`(`id`, `name`, `age`, `column_name`) VALUES (1, '1', 1, '1');

在这里插入图片描述
我们发现,当事务A执行提交之后,事务B和事务C进入死锁,此时该表的任何SQL语句都无法执行!

此时导致数据库表死锁,数据库SQL堆积越来越多,导致数据库崩溃!

5、总结

用事实说话。

通过实例,我们可以看出,即使没有长事务,执行DDL语句仍会导致表死锁。
尤其是对索引的操作,非常危险!

二、完美解决方案

1、执行DDL之前锁整表

在执行DDL语句之前,通过以下SQL,首先要确保没有长事务:

SELECT * FROM information_schema.INNODB_TRX;

然后,再执行DDL语句之前,先将整表锁住,然后执行DDL语句:
在这里插入图片描述
使用如下操作,完美解决死锁问题!

-- 锁整表,加上写锁
lock table lock_test write;
-- 添加索引
CREATE INDEX index_tb ON lock_test(column_name);
-- 解锁
unlock table;

2、执行DDL之前设置超时时间

在 alter table 时,设定等待时间,如果在这个指定的等待时间里面能够拿到 MDL 写锁最好,拿不到也不要阻塞后面的业务语句,先放弃。之后开发人员或者 DBA 再通过重试命令重复这个过程。

-- 单位 秒,只在当前会话中生效
set lock_wait_timeout=10
-- 添加索引
CREATE INDEX index_tb ON lock_test(column_name);

3、(重要)使用命令行

使用mysql命令行的方式,不用navicat,不会导致死锁!!

三、原因分析

当对非主键字段更改索引时,其实并不是一个原子操作,会先更新非主键字段索引,然后再更新主键索引。

当我们把主键删掉之后:

在这里插入图片描述
这种情况,和当表中有数据,执行表字段删除操作的场景一模一样。

原因是MySQL5.6引入的OnLine-DDL,一个DDL语句其实包含着两个等待操作:

  • prepare阶段:尝试获取MDL排他锁,禁止其他线程读写;
  • ddl执行阶段:降级成MDL共享锁,允许其他线程读取;
  • commit阶段:升级成MDL排他锁,禁止其他线程读写;
  • finish阶段:释放MDL锁;
    1、3、4如果没有锁冲突,执行时间非常短。第2步占用了DDL绝大部分时间,这期间这个表可以正常读写数据,是因此称为“online ”。
    如果第3步升级为MDL写锁的时候,这个表的MDL锁有其他事务占着,那么这个事务会阻塞,等到可以拿到MDL写锁,而且如果不幸一直拿不到,最后锁超时了,就只好回滚这个DDL操作。
    所以,DDL语句只有才开始和结束的时候,才会禁止读和写,在语句执行的时候是可以进行读的。

1、验证OnLine-DDL

在这里插入图片描述
我们发现,事务C,换成Update语句,并不会导致死锁。

因为OnLine-DDL 是 写锁-读锁-写锁,有个锁降级升级的过程。
OnLine-DDL对select语句来说,不是原子操作,分了两步(写锁时相对于select加锁,读锁时相对于select解锁,写锁时又对select加锁)。
对update语句来说,是原子操作,相当于全程加了锁。

写在后面

如果本文对你有帮助,请点赞收藏关注一下吧 ~
在这里插入图片描述

### 回答1: 在 MySQL 中,你可以使用 `CREATE INDEX` 语句来创建一个索引。例如: ``` CREATE INDEX index_name ON table_name (column_name); ``` 你也可以在创建表的时候就创建索引。例如: ``` CREATE TABLE table_name ( column_name1 data_type, column_name2 data_type, ... INDEX index_name (column_name) ); ``` 你也可以使用 `ALTER TABLE` 语句来向现有的表中添加索引。例如: ``` ALTER TABLE table_name ADD INDEX index_name (column_name); ``` 请注意,在同一个表中,索引名称必须唯一。 ### 回答2: MySQL 创建索引是为了提高查询性能和加快数据检索速度。下面是MySQL创建索引的一些常见方法和注意事项: 一、创建索引的方法: 1. 在创建表时,可以在某些列上使用关键字"INDEX"、"PRIMARY KEY"或"UNIQUE"来创建索引。 2. 在已创建的表上使用"CREATE INDEX"语句来添加索引。 3. 在已创建的表上使用"ALTER TABLE"语句来添加索引。 二、创建索引的注意事项: 1. 索引字段的选择:索引字段应该是经常被查询或排序的字段,通常是主键、外键、经常出现在WHERE子句中的字段等。 2. 避免创建过多的索引:虽然索引可以提高查询速度,但是过多的索引会增加数据的存储空间和维护成本,也会影响插入、更新和删除操作的性能。 3. 考虑多列索引:如果多个列组合起来经常作为查询条件,那么可以创建多列索引,以减少索引的数量和提高查询效率。 4. 索引字段的长度:对于字符串类型的字段,如果字段的长度过长,会增加索引的大小和检索的时间,因此最好将字段长度控制在合适的范围内。 5. 定期维护索引:随着数据的增加和删除,索引的效率可能会降低,因此需要定期进行索引的重建或优化。 通过合理的创建索引,可以有效提高数据库的查询性能,并减少数据库的负载。但需要根据具体的业务场景和数据库结构进行分析和选择,以达到最优的性能效果。 ### 回答3: MySQL 创建索引是为了加快数据库的查询速度和提高数据库的性能。当我们在数据库表中添加索引后,查询数据时,MySQL会使用索引来快速定位到符合条件的数据行,而不需要扫描整个表。 在MySQL中,创建索引可以通过以下方式进行: 1. 创建主键索引:主键索引是用于唯一标识表中的每一行数据的索引。通过在创建表时指定某列为主键,MySQL会自动创建主键索引。 例如,创建名为users的表,并指定id列为主键索引: CREATE TABLE users ( id INT PRIMARY KEY, name VARCHAR(20), ... ); 2. 创建唯一索引:唯一索引保证表中的某一列的值是唯一的。使用CREATE INDEX语句创建。 例如,为users表的name列创建唯一索引: CREATE UNIQUE INDEX index_name ON users (name); 3. 创建普通索引:普通索引可以加速对表中某一列的查询。使用CREATE INDEX语句创建。 例如,为users表的age列创建普通索引: CREATE INDEX index_name ON users (age); 4. 创建全文索引:全文索引在文本内容上进行搜索,可用于对文本字段进行关键字搜索。使用ALTER TABLE语句进行创建。 例如,为articles表的content列创建全文索引: ALTER TABLE articles ADD FULLTEXT (content); 5. 创建复合索引:复合索引是指对多个列进行索引,可以提高多列条件查询的性能。使用CREATE INDEX语句创建。 例如,为users表的name和age列创建复合索引: CREATE INDEX index_name ON users (name, age); 需要注意的是,过多的索引会增加数据库的存储空间和维护成本,并且在插入、更新和删除数据时会导致性能下降,因此需要根据实际需求合理地创建索引
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

秃了也弱了。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值