在MySQL中,表是和操作系统中的文件对应的,而文件名在有的操作系统下是区分大小写的(比如linux),有的是不区分大小写(比如Windows),表名与文件名的大小写对应关系,MySQL 是通过 这个变量来控制的。

    这个变量的有效取值是0,1,2,按照官方文档 的解释:

    0表示,表在文件系统存储的时候,对应的文件名是按建表时指定的大小写存的,MySQL 内部对表名的比较也是区分大小写的; 1表示,表在文件系统存储的时候,对应的文件名都小写的,MySQL 内部对表名的比较是转成小写的,即不区分大小写; 2表示,表在文件系统存储的时候,对应的文件名是按建表时指定的大小写存的,但是 MySQL 内部对表名的比较是转成小写的,即不区分大小写。

    0适用于区分大小写的系统,1都适用,2适用于不区分大小写的系统。

    如果在开始使用MySQL选定了一个合适的值后,就不要改变,不然的话在之后使用中就会出现问题。

    问题描述

    首先在启动 mysqld 的时候,指定 lower_case_table_names = 0,我们执行这样的语句:

    查看对应数据库目录下的表文件:

    然后重启mysqld,指定 lower_case_table_names =1,执行删除db1

    可以看到删库语句执行失败,我们再看下数据库目录下的表文件

    可以看到,大写的 T3 和 T4 表没有被删掉,为什么呢?

    问题分析

    意思就是如果lower_case_table_names非0的话,就把 table_name 转成小写的,T3 和 T4 就被转成 t3 和 t4,这样生成的 table_list 中的对应的表是 t1,t2,t3,t4。之后拿着这样的 table_list 通过 mysql_rm_table_no_locks 一个个删表,这样就只把t1,t2 给删了,t3和t4不存在,并且删表时的逻辑是带有 if exists 的,所以也不会报错。

    在list表都删除完后,调用rm_dir_w_symlink来删除db目录,此时db1目录下还有 T3 T4 对应的文件,这个函数会调用系统的 rmdir 函数,而当目录非空的时候,rmdir是执行失败的。

    所以我们看到最终的错误提示 Error dropping database (can’t rmdir ‘./db1’, errno: 39)

    建议

    上面的问题是改变 lower_case_table_names 导致 drop database 失败,其实还有许多其它的因为lower_case_table_names值改变导致的问题,比如主备库本来这个值本来是一致的,如果只改主库的值的话,就会导致备库复制中断,报找不到表的问题,或者本来是不区分大小写的,应用里的写的SQL语句有大写表名,也有小写表名,之后改成区分大小写,就会导致应用出错。

    所以建议是:

    • 应用不要依赖于 mysql 的表名转换机制,应用里的sql语句应该和表名一致,在不区分大小写的时候,应用里对同一个表的使用,不要既有大写表名,也有小写表名。