MySQL · 捉虫动态 · 删除索引导致表无法打开

本文涉及的产品
云原生数据库 PolarDB 分布式版,标准版 2核8GB
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
云数据库 RDS SQL Server,基础系列 2核4GB
简介:

问题背景

最近线上遇到一个问题,用户重启实例后发现有张表打不开了,经调研后发现是用户之前的霸蛮操作导致的,下面给出复现步骤:

create table t1 (id int not null primary key, name varchar(100) not null) engine=innodb;
create table t2 (id int not null primary key, fid int not null, name varchar(100) not null, CONSTRAINT `fk_fid` FOREIGN KEY (`fid`) REFERENCES `t1` (`id`)) engine=innodb;
insert into t1 values(1, 'test 1'),(2, 'test 2'),(3,'test 3');
insert into t2 values(1, 1, 'test 1'),(2, 1, 'test 2'),(3, 2, 'test 3');
show create table t2;
show create table t2\G
*************************** 1. row ***************************
Table: t2
Create Table: CREATE TABLE `t2` (
`id` int(11) NOT NULL,
`fid` int(11) NOT NULL,
`name` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
KEY `fk_fid` (`fid`),
CONSTRAINT `fk_fid` FOREIGN KEY (`fid`) REFERENCES `t1` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

-- 霸蛮的删掉外键约束用的索引
set foreign_key_checks=off;
alter table t2 drop index `fk_fid`;


-- 重启mysqld前表t2正常
set foreign_key_checks=on;
show create table t2;
*************************** 1. row ***************************
Table: t2
Create Table: CREATE TABLE `t2` (
`id` int(11) NOT NULL,
`fid` int(11) NOT NULL,
`name` varchar(100) NOT NULL,
PRIMARY KEY (`id`),
CONSTRAINT `fk_fid` FOREIGN KEY (`fid`) REFERENCES `t1` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

-- 重启mysqld
show tables;
+----------------+
| Tables_in_test |
+----------------+
| t1             |
| t2             |
+----------------+
show create table t2;
ERROR 1146 (42S02): Table 'test.t2' doesn't exist

重启后t2表打不开了。

问题分析

在MySQL中,为了能够快速的检查外键约束,需要子表和父表对应的列上都要有索引,如果对应字段上没有索引, 子表在建立外键约束的时候,会自动加上。

MySQL requires indexes on foreign keys and referenced keys so that foreign key checks can be fast and not require a table scan. In the referencing table, there must be an index where the foreign key columns are listed as the first columns in the same order. Such an index is created on the referencing table automatically if it does not exist.

子表上的外键索引正常情况是删不掉,如果试图去删除的话会报错:

ERROR 1553 (HY000): Cannot drop index 'fk_fid': needed in a foreign key constrain

删索引时的外键检查逻辑在 innobase_check_foreign_key_index()函数中,感兴趣的同学可以自己看下,调用栈如下:

#0  innobase_check_foreign_key_index
#1  ha_innobase::prepare_inplace_alter_table
#2  handler::ha_prepare_inplace_alter_table
#3  mysql_inplace_alter_table
#4  mysql_alter_table
#5  Sql_cmd_alter_table::execute
#6  mysql_execute_command
#7  mysql_parse
#8  dispatch_command
#9  do_command
#10 do_handle_one_connection
#11 handle_one_connection
#12 pfs_spawn_thread
#13 start_thread
#14 clone

但是如果用户设置了 set foreign_key_checks=off 后,删除索引时就不会进入 innobase_check_foreign_key_index() ,所以索引能被删掉。

在上面的复现步骤中还可以看到,如果不重启的话,表是能正常打开的,即使是做了flush tables,表也能正常打开,这又是为什么呢?

我们知道MySQL有个table_open_cache,这是server层对打开表的缓存,flush tables会把这个cache清掉;InnoDB层的字典系统对打开的表也有一个缓存,基于这个缓存又构建了2个链表dict_sys->table_LRU 和 dict_sys->table_non_LRU,位于前者中的表缓存会被系统用LRU算法清理掉,后者不会,清理由InnoDB后台线程做,flush tables 不会触发清理。并且如果InnoDB表有外键的话,缓存是被放在dict_sys->table_non_LRU上的,所以不会被后台线程清理掉,因此成功打开一次后就不会出问题了。关于含外键表缓存被放在 dict_sys->table_non_LRU 上,具体的调用栈如下:

#0  dict_table_move_from_lru_to_non_lru
#1  dict_foreign_add_to_cache
#2  dict_load_foreign
#3  dict_load_foreigns
#4  dict_load_table
#5  dict_table_open_on_name
#6  ha_innobase::open
#7  handler::ha_open
#8  open_table_from_share
#9  open_table
#10 open_and_process_table
#11 open_tables
#12 open_tables
#13 mysqld_show_create
#14 mysql_execute_command
#15 mysql_parse
#16 dispatch_command
#17 do_command
#18 do_handle_one_connection
#19 handle_one_connection
#20 pfs_spawn_thread
#21 start_thread
#22 clone

关于这点官方文档也有描述:

InnoDB system table instances and parent and child table instances with foreign key relationships are not placed on the LRU list and are not subject to eviction from memory.

问题解决

通过上面的分析可以看出之所以表打不开,是因为用户霸蛮的删除了外键依赖的索引,但是能让用户通过SQL就把表弄的不能访问,也算是数据库系统的bug,也早有人给官方提了bug,详见bug#68148bug#70260bug#74659

MySQL 5.6.12 对此进行了修复,如果遇到这种情况表打不开的话,只需要先 set foreign_key_checks=off,然后表就可以打开了,这时把缺失的索引再加上就可以了,修复详情见patch

上面的修复是在出问题后,其实最好是能从源头上杜绝,不让用户删除外键约束需要的索引,因为这本身就是错误的操作,因此MySQL 5.7.5又出了一个patch,让用户无法删除外键约束的索引,即使用 set foreign_key_checks=off 关闭掉外键约束检查。

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
22天前
|
缓存 关系型数据库 MySQL
MySQL索引策略与查询性能调优实战
在实际应用中,需要根据具体的业务需求和查询模式,综合运用索引策略和查询性能调优方法,不断地测试和优化,以提高MySQL数据库的查询性能。
|
2月前
|
存储 关系型数据库 MySQL
阿里面试:为什么要索引?什么是MySQL索引?底层结构是什么?
尼恩是一位资深架构师,他在自己的读者交流群中分享了关于MySQL索引的重要知识点。索引是帮助MySQL高效获取数据的数据结构,主要作用包括显著提升查询速度、降低磁盘I/O次数、优化排序与分组操作以及提升复杂查询的性能。MySQL支持多种索引类型,如主键索引、唯一索引、普通索引、全文索引和空间数据索引。索引的底层数据结构主要是B+树,它能够有效支持范围查询和顺序遍历,同时保持高效的插入、删除和查找性能。尼恩还强调了索引的优缺点,并提供了多个面试题及其解答,帮助读者在面试中脱颖而出。相关资料可在公众号【技术自由圈】获取。
|
2月前
|
存储 NoSQL 关系型数据库
为什么MySQL不使用红黑树做索引
本文详细探讨了MySQL索引机制,解释了为何添加索引能提升查询效率。索引如同数据库的“目录”,在数据量庞大时提高查询速度。文中介绍了常见索引数据结构:哈希表、有序数组和搜索树(包括二叉树、平衡二叉树、红黑树、B-树和B+树)。重点分析了B+树在MyISAM和InnoDB引擎中的应用,并讨论了聚簇索引、非聚簇索引、联合索引及最左前缀原则。最后,还介绍了LSM-Tree在高频写入场景下的优势。通过对比多种数据结构,帮助理解不同场景下的索引选择。
103 6
|
2月前
|
SQL 关系型数据库 MySQL
案例剖析:MySQL唯一索引并发插入导致死锁!
案例剖析:MySQL唯一索引并发插入导致死锁!
150 0
案例剖析:MySQL唯一索引并发插入导致死锁!
|
2月前
|
存储 关系型数据库 MySQL
Mysql(4)—数据库索引
数据库索引是用于提高数据检索效率的数据结构,类似于书籍中的索引。它允许用户快速找到数据,而无需扫描整个表。MySQL中的索引可以显著提升查询速度,使数据库操作更加高效。索引的发展经历了从无索引、简单索引到B-树、哈希索引、位图索引、全文索引等多个阶段。
69 3
Mysql(4)—数据库索引
|
1月前
|
监控 关系型数据库 MySQL
数据库优化:MySQL索引策略与查询性能调优实战
【10月更文挑战第27天】本文深入探讨了MySQL的索引策略和查询性能调优技巧。通过介绍B-Tree索引、哈希索引和全文索引等不同类型,以及如何创建和维护索引,结合实战案例分析查询执行计划,帮助读者掌握提升查询性能的方法。定期优化索引和调整查询语句是提高数据库性能的关键。
192 1
|
2月前
|
存储 关系型数据库 MySQL
如何在MySQL中进行索引的创建和管理?
【10月更文挑战第16天】如何在MySQL中进行索引的创建和管理?
79 1
|
1月前
|
监控 关系型数据库 MySQL
数据库优化:MySQL索引策略与查询性能调优实战
【10月更文挑战第26天】数据库作为现代应用系统的核心组件,其性能优化至关重要。本文主要探讨MySQL的索引策略与查询性能调优。通过合理创建索引(如B-Tree、复合索引)和优化查询语句(如使用EXPLAIN、优化分页查询),可以显著提升数据库的响应速度和稳定性。实践中还需定期审查慢查询日志,持续优化性能。
79 0
|
2月前
|
监控 关系型数据库 MySQL
MySQL数据表索引命名规范
MySQL数据表索引命名规范
88 1
|
2月前
|
存储 SQL 关系型数据库
mysql中主键索引和联合索引的原理与区别
本文详细介绍了MySQL中的主键索引和联合索引原理及其区别。主键索引按主键值排序,叶节点仅存储数据区,而索引页则存储索引和指向数据域的指针。联合索引由多个字段组成,遵循最左前缀原则,可提高查询效率。文章还探讨了索引扫描原理、索引失效情况及设计原则,并对比了InnoDB与MyISAM存储引擎中聚簇索引和非聚簇索引的特点。对于优化MySQL性能具有参考价值。

相关产品

  • 云数据库 RDS MySQL 版