天天看點

mysql show tables報錯_show tables能看到表卻無法讀寫?

原标題:show tables能看到表卻無法讀寫?

作者:高鵬(八怪)

來源:http://www.jianshu.com/p/8af0b92e4fc8

MySQL版本 5.7.14(社群版源碼基礎上增加一些debug代碼)

在MySQL使用innodb的時候我們有時候會看到如下報錯:

ERROR 1146 (42S02): Table 'test.test1bak' doesn't exist

首先總結下原因:

缺少frm檔案;

innodb資料字典不包含這個表。

我們重點讨論情況2,因為情況1是顯而易見的。

在使用innodb存儲引擎的時候某些時候我們show tables能夠看到這個表,但是如果進行任何操作會報錯如下:

mysql> show tables;| test1bak |

mysql> desc test1bak ;ERROR 1146 (42S02): Table 'test.test1bak' doesn't exist

也許你會說我明明能夠看到這個表啊,為什麼通路還會報錯呢?

其實要清楚innodb有自己的資料字典,隻要有frm 檔案存在show tables就能看到,但是最終是否能夠正常打開表結構在innodb中還依賴于innodb的資料字典,主要的包含:

INNODB_SYS_columns

INNODB_SYS_FIELDS

INNODB_SYS_TABLES

INNODB_SYS_INDEXES

如果報錯出現我們需要首先檢視的是INNODB_SYS_TABLES是否包含了這個表的資訊。也許在這些資料字典中某些列顯示值并不是那麼明确,比如

mysql> select * from information_schema.innodb_sys_tables where name='test/kkkkm1';+----------+-------------+------+--------+-------+-------------+------------+---------------+------------+| TABLE_ID | NAME | FLAG | N_COLS | SPACE | FILE_FORMAT | ROW_FORMAT | ZIP_PAGE_SIZE | SPACE_TYPE |+----------+-------------+------+--------+-------+-------------+------------+---------------+------------+| 374 | test/kkkkm1 | 33 | 6 | 540 | Barracuda | Dynamic | 0 | Single |+----------+-------------+------+--------+-------+-------------+------------+---------------+------------+

比如這裡的FLAG列為33,他實際上是一個位圖表示方式,分别表示如下資訊:

#define DICT_TF_WIDTH_COMPACT 1#define DICT_TF_WIDTH_ZIP_SSIZE 4#define DICT_TF_WIDTH_ATOMIC_BLOBS 1#define DICT_TF_WIDTH_DATA_DIR 1#define DICT_TF_WIDTH_SHARED_SPACE 1

接下來我們分析一下為什麼是FLAG是33如下:

33的二進制為00100001從低位開始 1:從源碼注釋來看本位COMPACT/COMPRESSED/DYNAMIC均為1 0000: ZIP_SSIZE flag 這四位用于支援壓縮功能如COMPRESSED 1:ATOMIC_BLOBS flag 這一位是COMPACT和DYNAMIC主要差別所在,請看源碼注釋 0:DATA DIRECTORY and innodb-file-per-table flag為了支援DATA DIRECTORY文法 0:SHARED tablespace flag為了支援TABLESPACE文法

然後我們測試一下:

如果我們建立如下的表:CREATE TABLE t2 (c1 INT PRIMARY KEY) TABLESPACE = innodb_file_per_tableDATA DIRECTORY = '/root/mysql5.7.14/percona-server-5.7.14-7/mysql-test/var/mysqld.1';

其type為97二進制為 01100001:使用DATA DIRECTORY建立使用ATOMIC_BLOBS且無壓縮則DYNAMIC格式。詳見:15.5.5 Creating a File-Per-Table Tablespace Outside the Data Directory

如果我們建立如下的表:CREATE TABLESPACE tt1 ADD DATAFILE '/root/mysql5.7.14/tt1.ibd';CREATE TABLE tsh (c1 INT ) TABLESPACE tt1 ROW_FORMAT=COMPACT ;

其type為129二進制為 10000001:使用TABLESPACE文法建立不使用ATOMIC_BLOBS且無壓縮則為COMPACT格式。

詳見:15.5.9 InnoDB General Tablespaces

我們可以看到使用8位一個位元組而已就可以表示出大量的資訊,這也是位圖的優勢,其他比如 MTYPE/PRTYPE也是這種表示方式。

接下來我們回到主題,需要看看這個錯到底是哪裡報錯來的?進行trace後如下,我們來看看主要部分:

注意這裡的trace是mysql debug版本下檢視函數調用的主要方法參考官方文檔 26.5.1.2 Creating Trace Files502 [email protected]: | | | | | | | | | | | >ha_innobase::open_dict_table 503 [email protected]: | | | | | | | | | | | | >dict_table_open_on_name 504 [email protected]: | | | | | | | | | | | | | dict_table_open_on_name: table: 'test/test1bak' 505 [email protected]: | | | | | | | | | | | | | >dict_table_check_if_in_cache_low 506 [email protected]: | | | | | | | | | | | | | | dict_table_check_if_in_cache_low: table: 'test/test1bak' 507 [email protected]: | | | | | | | | | | | | | dict_load_table 509 [email protected]: | | | | | | | | | | | | | | dict_load_table: loading table: 'test/test1bak' 510 [email protected]: | | | | | | | | | | | | | | >dict_table_check_if_in_cache_low 511 [email protected]: | | | | | | | | | | | | | | | dict_table_check_if_in_cache_low: table: 'test/test1bak' 512 [email protected]: | | | | | | | | | | | | | | dict_load_table_one 514 [email protected]: | | | | | | | | | | | | | | | dict_load_table_one: table: test/test1bak 515 [email protected]: | | | | | | | | | | | | | | | >dict_table_check_if_in_cache_low 516 [email protected]: | | | | | | | | | | | | | | | | dict_table_check_if_in_cache_low: table: 'SYS_TABLES' 517 [email protected]: | | | | | | | | | | | | | | | btr_cur_search_to_nth_level 519 [email protected]: | | | | | | | | | | | | | | | sql_print_warning 525 [email protected]: | | | | | | | | | | | | >error_log_print 526 [email protected]: | | | | | | | | | | | | | >print_buffer_to_file 527 [email protected]: | | | | | | | | | | | | | | enter: buffer: InnoDB: Cannot open table test/test1bak from the internal data dictionary of InnoDB though the .frm file for the table exists. Please refer to http://dev.mysql.com/doc/refman/5.7/en/innodb-troubleshooting.html for how to resolve the issue. 528 [email protected]: | | | | | | | | | | | | |

Checks if a table is in the dictionary cache

根據dict_sys->table_hash尋找

Loads a table definition and also all its index definitions.

通過掃描字典的B+樹進行加載

如果不能找到則報錯

這樣也就解釋了為什麼show tables能夠看到但是select卻報錯Table doesn't exist ,而從原理上講show tables隻是檢視了frm檔案。

另外這裡也提一個案列,曾經有一個朋友問我他将整個庫目錄都拷貝了,但是表能看到但是一操作就報Table doesn't exist,顯然他沒有拷貝ibdata1,資料字典的引導資訊都存在這裡面檔案的第7個page中,其b+樹也是存在其中,用源碼解釋一下:

dict_hdr_t*dict_hdr_get( mtr_t* mtr) { buf_block_t* block; dict_hdr_t* header; block = buf_page_get(page_id_t(DICT_HDR_SPACE, DICT_HDR_PAGE_NO), univ_page_size, RW_X_LATCH, mtr); header = DICT_HDR + buf_block_get_frame(block); buf_block_dbg_add_level(block, SYNC_DICT_HEADER); return(header);}

注意這裡的 DICT_HDR_SPACE, DICT_HDR_PAGE_NO分别是宏定義

#define DICT_HDR_SPACE 0 #define DICT_HDR_PAGE_NO FSP_DICT_HDR_PAGE_NO#define FSP_DICT_HDR_PAGE_NO 7

space 0就是ibdata1的space_no,7當然就是引導塊,這哥們連ibdata1都沒拷貝,當然innodb資料字典自然不包含這些表了。其實也是上面描述的原理 。

那麼正确的拷貝的方式一定是停機後,整個資料目錄進行拷貝,而不是僅僅拷貝需要的庫的目錄,否則innodb資料字典是不能正常加載的。

最後附帶space 0的部分塊解釋 #define FSP_XDES_OFFSET 0 #define FSP_IBUF_BITMAP_OFFSET 1 #define FSP_FIRST_INODE_PAGE_NO 2 #define FSP_IBUF_HEADER_PAGE_NO 3 #define FSP_IBUF_TREE_ROOT_PAGE_NO 4 #define FSP_TRX_SYS_PAGE_NO 5 #define FSP_FIRST_RSEG_PAGE_NO 6 #define FSP_DICT_HDR_PAGE_NO 7 ********

您可能感興趣的文章:傳回搜狐,檢視更多

責任編輯: