zoukankan      html  css  js  c++  java
  • lower_case_table_names 线上默认为0

    在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语句应该和表名一致,在不区分大小写的时候,应用里对同一个表的使用,不要既有大写表名,也有小写表名。
  • 相关阅读:
    (引)spring学习笔记1.什么是控制反转
    Arduino 各种模块篇 步进电机 step motor 舵机 servo 直流电机 总复习
    Raspberry Pi Wireless Adaptor
    Pyramid 使用总结1
    Arduino 各种模块篇 人体红外感应模块 proximity sensor
    Pyramid 使用总结2
    Webcam Streaming Desktop Recording on Linux for ubuntu or its destros
    Arduino 各种模块篇 步进电机 step motor( 不用库,不用shield, 纯)
    Arduino 各种模块篇 motor shield 电机扩展板(舵机、直流电机、步进电机party)
    转载 stepper motors
  • 原文地址:https://www.cnblogs.com/igoodful/p/13559749.html
Copyright © 2011-2022 走看看