[MySQL 5.6] Innodb新特性之export/import 表文件

 在MySQL 5.6中,提供了新的接口来支持将单独表文件,从一个实例转移到另外一个实例。

对于MySQL运维同学来说,这是一个非常重要的特性,当MySQL实例挂掉了,或者因为其他什么原因重现异常了,就可以通过该特性,将所有的表迁移到其他实例。

以下分析及实验基于MySQL5.6.12

用法:

简单的实验了一把,使用sysbench创建的表

mysql>  FLUSH TABLES sbtest1 FOR EXPORT;

Query OK, 0 rows affected (0.00 sec)

观察到sb库下产生的新文件:

$ ls -lh sbtest1.*

-rw-rw—- 1 mysql dba  563 Jun 10 10:29 sbtest1.cfg

-rw-rw—- 1 mysql dba 8.5K Jun  7 11:29 sbtest1.frm

-rw-rw—- 1 mysql dba 276M Jun  9 00:49 sbtest1.ibd

.cfg包含了Innodb的数据词典信息,例如有多少列,数据类型及顺序是什么,以及包含哪些索引等等。

将ibd和cfg文件拷贝后,执行unlock tables,注意unlock操作会移除cfg文件

目标库上,建立对应的空表:
mysql> CREATE TABLE `sbtest1` (

    ->   `id` int(10) unsigned NOT NULL AUTO_INCREMENT,

    ->   `k` int(10) unsigned NOT NULL DEFAULT ‘0’,

    ->   `c` char(120) NOT NULL DEFAULT ”,

    ->   `pad` char(60) NOT NULL DEFAULT ”,

    ->   PRIMARY KEY (`id`),

    ->   KEY `k` (`k`)

    -> ) ENGINE=InnoDB;

Query OK, 0 rows affected (0.09 sec)



mysql> alter table sbtest1 discard tablespace; 

Query OK, 0 rows affected (0.04 sec)

将sbtest1.cfg和sbtest1.ibd拷贝到这实例数据目录上面,修改好对应的权限,然后执行。

mysql> alter table sbtest1 import tablespace;

Query OK, 0 rows affected (3.49 sec)



mysql> select count(*) from sbtest1;

+———-+

| count(*) |

+———-+

|  1000000 |

+———-+

1 row in set (1.01 sec)

不过其实cfg文件也不是必须要和ibd文件一一对应

同样的,重新创建表,只拷贝ibd文件:

mysql>  alter table sbtest1 import tablespace;

Query OK, 0 rows affected, 1 warning (4.01 sec)mysql> show warnings;

+———+——+———————————————————————————————————————————————-+

| Level   | Code | Message                                                                                                                                      |

+———+——+———————————————————————————————————————————————-+

| Warning | 1810 | InnoDB: IO Read error: (2, No such file or directory) Error opening ‘./test/sbtest1.cfg’, will attempt to import without schema verification |

+———+——+———————————————————————————————————————————————-+

1 row in set (0.00 sec)

提示没有sbtest1.cfg,但是数据已经完整的插入进去了。实际上tablespace 的Import功能很早就有了,但MySQL5.5及之前的版本存在几点限制:
1.如果目标实例上表在tablespace中存储的id和ibd中的id不匹配,则会导入失败,通常的做法是通过不断创建/rename表的方式推高table id,或者直接改ibd文件头的方式来让其匹配;

2.如果没有主键的话,还要考虑到全局row id的问题;

3.迁移表的ibd上的最大LSN不能超过目标机器的LSN;

4. change buffer导致的DML缓存,脏页等问题。

另外cfg文件也不是必须的,因为他只是从ibdata中抽取的表的定义,但如果表空间也损坏了呢?这种情况下可能就没有作用了。

很显然早就有人意识到这个问题,看看这个bug report http://bugs.mysql.com/bug.php?id=66715, 如果知道表的schema的话,我们就可以在另外一个实例上生成一个cfg文件,因为cfg并不要求和ibd文件是完全对应的。

不过,一个明显的问题是,如果表空间损坏,实例起不来,做不了crash recovery,那么change buffer的操作可能也没有同步到ibd中,这种情况,是不是意味着数据丢失呢?

PS:执行flush tables tbname with read lock也同样会生成cfg文件

简单的跟了下代码

a.  FLUSH TABLES sbtest1 FOR EXPORT

server层的入口函数为flush_tables_for_export

Innodb层进行处理的backtrace为:
flush_tables_for_export->open_and_lock_tables->lock_tables->mysql_lock_tables->lock_external->handler::ha_external_lock->ha_innobase::external_lock

 switch (prebuilt->table->quiesce) { case QUIESCE_START: /* Check for FLUSH TABLE t WITH READ LOCK; */ if (!srv_read_only_mode && thd_sql_command(thd) == SQLCOM_FLUSH && lock_type == F_RDLCK) { row_quiesce_table_start(prebuilt->table, trx); /* Use the transaction instance to track UNLOCK TABLES. It can be done via START TRANSACTION; too implicitly. */ ++trx->flush_tables; } break; case QUIESCE_COMPLETE: /* Check for UNLOCK TABLES; implicit or explicit or trx interruption. */ if (trx->flush_tables > 0 && (lock_type == F_UNLCK || trx_is_interrupted(trx))) { row_quiesce_table_complete(prebuilt->table, trx); ut_a(trx->flush_tables > 0); --trx->flush_tables; } break;

 

 

其中,row_quiesce_table_start是执行flush table for export的逻辑,row_quiesce_table_complete是unlock tables调用的逻辑
代码在 @file row/row0quiesce.cc 中

a1) row_quiesce_table_start
主要做以下几件事情
*停止purge操作
 if (trx_purge_state() != PURGE_STATE_DISABLED) { trx_purge_stop(); }

因此在完成copy后,需要及时的Unlock tables,防止purge线程停止太久,导致history list过长

*合并change buffer :ibuf_contract_in_background(table->id, TRUE)

*确保所有的脏页都刷到磁盘
          buf_LRU_flush_or_remove_pages(

table->space, BUF_REMOVE_FLUSH_WRITE, trx);

*写cfg文件
row_quiesce_write_cfg(table, trx->mysql_thd
row_quiesce_write_header:cfg文件头包括cfg文件版本号IB_EXPORT_CFG_VERSION_V1,当前服务器的hostname,表名,下一个自增列的值,page size, table flag,以及列的个数
row_quiesce_write_table:写入表的列的属性信息,包括长度,类型,位置等
row_quiesce_write_indexes:写入每个索引的元数据信息

a2)row_quiesce_table_complete(执行Unlock tables)
*删除cfg文件(os_file_delete_if_exists)
*继续purge操作(trx_purge_run())

b.import数据
backtrace为:
ha_innobase::discard_or_import_tablespace->row_import_for_mysql
大概看了下,主要包括以下几个步骤:

*读取cfg文件,读取的数据保存到结构体row_import中
err = row_import_read_cfg(table, trx->mysql_thd, cfg);

*检查表的定义和cfg中的是否匹配

*扫描ibd文件中的所有page,检查其是否corruption

*检查ibuf bitmap,如果有标记为IBUF_BITMAP_BUFFERED的,表示存在change buffer的操作没有merge进来,可能会丢失数据。
仅仅写日志提示,可以继续正常import数据

*调整聚集索引的segment header,例如其中记录的space id
      err = btr_root_adjust_on_import(index);

*purge 聚集索引上的标记删除记录

*调整二级索引根节点和叶子节点segment header,更新space id, purgw标记删除的无效记录

*确保当前实例下一个可用的DB_ROW_ID 要大于当前import的表上最大的DB_ROW_ID

*将产生的脏页刷到磁盘,并从buffer pool中移除对应的page
        buf_LRU_flush_or_remove_pages(

prebuilt->table->space, BUF_REMOVE_FLUSH_WRITE, trx);

*更新系统表SYS_INDEXES中记录的索引信息
 err = row_import_update_index_root(trx, table, false, true);

*移除表的discade标记

//////////////////////

原创文章,转载请注明: 转载自Simple Life

本文链接地址: [MySQL 5.6] Innodb新特性之export/import 表文件

Post Footer automatically generated by wp-posturl plugin for wordpress.


Comments

Leave a Reply

Your email address will not be published. Name and email are required


Current month ye@r day *