#################################### 

 

1、linux下mysql安装完后是默认:区分表名的大小写,不区分列名的大小写; 

2、用root帐号登录后,在/etc/my.cnf 中的[mysqld]后添加添加lower_case_table_names=1,重启MYSQL服务,这时已设置成功:不区分表名的大小写; 

lower_case_table_names参数详解: 

其中 0:区分大小写,1:不区分大小写 

MySQL在Linux下数据库名、表名、列名、别名大小写规则是这样的:     

  1. 数据库名与表名是严格区分大小写的;    
  2. 表的别名是严格区分大小写的;     
  3. 列名与列的别名在所有的情况下均是忽略大小写的;     
  4. 变量名也是严格区分大小写的;

MySQL在Windows下都不区分大小写。 

3、如果想在查询时区分字段值的大小写,则:字段值需要设置属性,设置的方法有多种:

  1. 创建时设置:  CREATE TABLE T(  A (10)   ); 
  2. 使用alter修改:  ALTER TABLE `tablename` MODIFY  `cloname` (45) ; 
  3. mysql table editor中直接勾选BINARY项。

 

 

如果主库是:
lower_case_table_names=0
表名为:COLUMNS_V2
从库是: lower_case_table_names
=1 表名为:columns_v2 开启读写分离:那么业务执行: select * from columns_v2;
结果是:
当查询从库,发现有数据,正常现象: select * from columns_v2;
当在主库更新该表数据,发现异常:
update table columns_v2 set ,,,, where ,,,,
发现:ERROR 1146 (42S02): Table 'columns_v2' doesn't exist

 

 

 

 

 

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

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

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

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

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

问题描述

这里给出一个在使用过程中改变 lower_case_table_names 导致 drop database 失败的案例。因为lower_case_table_names是个只读变量,只能在启动时指定参数设置值,或者 gdb 挂上去直接改内存。

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

create database db1; use db1; create table t1(a int) engine = InnoDB; create table t2(a int) engine = MyISAM; create table T3(a int) engine = InnoDB; create table T4(a int) engine = MyISAM; 

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

$ls db1 db.opt t1.frm t1.ibd t2.frm t2.MYD t2.MYI T3.frm T3.ibd T4.frm T4.MYD T4.MYI 

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

mysql> drop database db1; ERROR 1010 (HY000): Error dropping database (can't rmdir './db1', errno: 39) 

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

$ls a T3.frm T4.frm T4.MYD T4.MYI 

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

问题分析

mysqld 在执行 drop database 操作的时候,是调用 mysql_rm_db 这个函数,在删除时先把db下的所有表都删掉,然后再把db删掉。为了找出对应db下的所有表,mysqld 是通过遍历数据库目录下的文件来做的,具体是用 find_db_tables_and_rm_known_files 这个函数,遍历数据库目录下的所有文件,然后构造出要 drop 的table列表,然而在构造删除列表过程中,会有这样一个判断:

if (lower_case_table_names) table_list->table_name_length= my_casedn_str(files_charset_info, table_list->table_name); 

意思就是如果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语句有大写表名,也有小写表名,之后改成区分大小写,就会导致应用出错。

所以建议是:

  • 不要轻易的改变lower_case_table_names的值,如果真要改的话,要先检查下已有的表是否有大小写的问题,保证目前的表名和要改的模式是一致的,比如从区分大小写改为不区分大小写,那就不应该有大写表存在,如果有的话,要先把大写表rename成小写的,如果本来有共存同名的大写表和小写表,就要想办法去掉一个。
  • 应用不要依赖于 mysql 的表名转换机制,应用里的sql语句应该和表名一致,在不区分大小写的时候,应用里对同一个表的使用,不要既有大写表名,也有小写表名。