資料庫核心月報-2015/09-MySQL·捉蟲動態·建表過程中crash造成重建表失敗

db匠發表於2016-05-23

問題描述

主庫的create table語句傳到備庫,備庫SQL執行緒執行過程中報錯:

Error `Can`t create table `XXX.XX` (errno: -1)` on query. Default database: `XXX`. Query: `CREATE TABLE XX (  column_a char(32) NOT NULL,  column_b int(10) DEFAULT NULL,  column_c int(10) DEFAULT NULL,  PRIMARY KEY (column_a),  KEY expiry (column_b)) ENGINE=HEAP DEFAULT CHARSET=gbk`

備庫 error log:

InnoDB: Error number 17 means `File exists`.
InnoDB: Some operating system error numbers are described at
InnoDB: http://dev.mysql.com/doc/refman/5.5/en/operating-system-error-	codes.html
InnoDB: The file already exists though the corresponding table did not
InnoDB: exist in the InnoDB data dictionary. Have you moved InnoDB
InnoDB: .ibd files around without using the SQL commands
InnoDB: DISCARD TABLESPACE and IMPORT TABLESPACE, or did
InnoDB: mysqld crash in the middle of CREATE TABLE?You can
!!!InnoDB: resolve the problem by removing the file `...`
InnoDB: under the `datadir` of MySQL.

從error log中可以看出,資料目錄中已存在 .ibd 檔案,推測是在建表過程中發生 crash。

資料目錄下存在 .ibd,不存在 .frm,建立.ibd 檔案的時間:

-rw-rw---- 1 mysql mysql    65536 Sep  5 14:41 XXX.ibd

.ibd 檔案建立時間 150905 14:41,對應時間的 error log:

150905 14:41:58 mysqld_safe Number of processes running now: 0
150905 14:41:58 mysqld_safe mysqld restarted

之後也出現了和該建立失敗的表相關的錯誤記錄:

150905 14:59:45  InnoDB: Error: table `XXX`.`XX` does not exist in the InnoDB internal

問題分析

執行如下語句,模擬建表

create table test.t3 (id int);

create table 時,由函式mysql_create_frm建立 .frm 檔案,mysql_create_frm 呼叫棧如下:

#0  mysql_create_frm
#1  rea_create_table
#2  mysql_create_table_no_lock
#3  mysql_create_table
#4  mysql_execute_command
#5  mysql_parse

t3.frm 檔案生成後,例項 crash(函式mysql_create_frm 執行完畢後kill mysqld),在資料庫中show tables可以看到 test.t3,但是無法插入,資料目錄下 t3.frm 檔案依然存在。

drop table報錯

ERROR 1051 (42S02): Unknown table `test.t3`

之後資料目錄下的t3.frm不存在,show tables 無法看到t3表,可以重新建立t3表。

.ibd 檔案由函式fil_create_new_single_table_tablespace建立,fil_create_new_single_table_tablespace呼叫棧如下:

#0  fil_create_new_single_table_tablespace
#1  dict_build_table_def_step
#2  dict_create_table_step
#3  que_thr_step
#4  que_run_threads_low
#5  que_run_threads
#6  row_create_table_for_mysql
#7  create_table_def
#8  ha_innobase::create
#9  handler::ha_create
#10 ha_create_table
#11 rea_create_table
#12 create_table_impl
#13 mysql_create_table_no_lock
#14 mysql_create_table
#15 mysql_execute_command
#16 mysql_parse

t3.ibd 檔案生成後,例項 crash(函式fil_create_new_single_table_tablespace執行完畢後kill mysqld),在資料庫中show tables可以看到 test.t3,無法插入資料,在資料目錄下存在檔案 t3.frm 和 t3.ibd。

drop table依然可以移除 t3.frm 並使show tables無法看到 t3 表。但無法移除 t3.ibd,並在重建 t3 表時報錯:

ERROR 1813 (HY000): Tablespace for table ``test`.`t3`` exists. Please DISCARD the tablespace before IMPORT.

在資料目錄中刪除 t3.ibd ,可以正常重建 t3 表。

這個 bug 的主要原因是 MySQL 的建表過程不是原子操作。如果建表過程正在進行的時候例項 crash,可能會造成一些在例項重啟後無法自動恢復的問題。就像這個問題當中的檔案殘留,無法通過 MySQL 客戶端中的操作解決,只能手動刪除檔案。如果使用者是遠端連線資料庫,又沒有登入伺服器運算元據檔案的許可權,就會影響資料庫的可用性。

MySQL 5.7 的實驗室版本正在設計和實現新版本的資料字典來解決這一問題。這個版本主要由以下幾個特性:

  • 資料字典將實現事務儲存,首先利用 InnoDB 儲存,其他儲存引擎可能會跟進開發;
  • 把分散式系統中的字典資訊統一成一個整體;
  • 使用統一的規則儲存字典資訊,給字典物件定義統一的API;
  • 避免檔案系統特性帶來的問題。

詳細資訊參見MySQL Server Blog

問題解決

通過問題分析,判斷備庫無法建表是由於在執行create table語句時備庫例項crash,且crash時.ibd 檔案已存在。使用者發現表建立失敗,企圖重建表依然失敗,之後執行了drop table語句,移除了.frm檔案,但.ibd檔案依然存在,無法重建表。
將資料目錄下的.ibd檔案移到其他資料夾作為備份,在備庫start slave後建表成功,主備複製正常。


相關文章