如何复制一张表的,首先说,如果原表的数据很小的话,可以考虑使用 insert … select 语句来进行实现

当然,这种方式需要对原表进行加锁,为了避免加锁可能带来不必要的麻烦,可以考虑将其导入到一个外部文件,再写回目标表

复制的方式有两种,为了演示这两种导出方式,我们创建了两张表,一个是db1.t一个是db2.t

create database db1;

use db1;

create table t(id int primary key, a int, b int, index(a))engine=innodb;

delimiter ;;

create procedure idata()

begin

declare i int;

set i=1;

while(i<=1000)do

insert into t values(i,i,i);

set i=i+1;

end while;

end;;

delimiter ;

call idata();

create database db2;

create table db2.t like db1.t

第一种:mysqldump方法

使用了mysqldump命令,将数据写成一组INSERT语句,使用下面的命令,将结果输出到临时文件中

mysqldump -h$host -P$port -u$user -p$password –add-lock=0 –no-create-info –single-transaction –set-gtid-purged=OFF db1 t –where=”a>900″ –result-file=/clinet_tmp/t.sql

我们分解下上面的命令

–single-transaction 导出的时候不加表锁

–add-lock=0 输出文件结果,不加LOCK TABLES t WRITE

–no-create-info 不导出表结构

–set-gtid-purged=OFF 不输出GTID相关信息

–result-file 输出文件路径,client表示是客户端机器上的

这样就生成了一个t.sql文件,包含了INSERT语句,默认为:

图片

当然,如果想要是每一行都有一个INSERT语句的话,那么可以执行命令的时候,加上-skip-extended-insert

可以通过这个命令,将Insert放入db2库去执行

mysql -h$host -P$port -u$user -p$password db2 -e “sourcce /client_tmp/t.sql”

需要说明的是,source是一个客户端命令,告知mysql去哪里打开文件,并且读取其中SQL语句,发给服务器端去执行

服务器端其实还是接受的是INSERT语句

2.导出为CSV文件

将本地的数据导为csv文件,MySQL提供了语句 ,查询结果导出至服务器本地目录

select * from db1.t where a>900 into outfile ‘/server_tmp/t.csv’

1.这个结果保存在服务器端,如果客户端和MySQL服务器端不在一个机器上,客户端机器的临时目录不会生成csv文件

2.into_outile指定了文件的生成位置(/server_tmp/)这个位置必须接受参数secure_file_priv的限制,这个参数可以设置为

empty,不限制文件生成,当然,不安全

路径,就是只能放在这个路径的目录和子目录下

NULL,禁止使用select ….into outfile操作

3.这个命令无法去覆盖文件,所以一旦目录下已经有了这个同名文件,就会报错

4.这条命令生成的文本文件中,一个数据行对应文本中的一行,但是如果字段找那个包含换行符等,文本中也会有换行符,前面还会加上”\”这个转义符,可以跟字段之间 数据行之间的分隔符隔开

然后得到了这个文件后

讲这个文件进行重新导入,load data infile ‘/server_tmp/t.csv’ into table db2.t;

这个语句的执行流程为

打开文件 t.csv,以制表符(\t)作为字段之间的分隔符 (\n)作为分隔符,进行数据读取

启动事务

进行判断每一行的字段数和表db2.t是否相同

如果不相同,则直接报错回滚

如果相同,则构成一行,进行插入

这个流程,如果存在主备复制,则在备库上执行为

主库执行完成,将/server_tmp/t.csv文件的内容直接放在binlog文件中

往binlog文件中,写入load data local infile ‘/tmp/SQL_LOAD_MB-1-0’INTOTABLE ‘db2′.’t’

把这个binlog给备库

备库的apply线程在执行这个日志的时候

先将binlog中的t.csv读出来,写入到本地的临时目录 /tmp/SQL_LOAD_MB-1-0中

在执行load data,进行插入

图片

在备库执行的load data语句中,多了一个local,意思为将执行这个命令的客户端所在机器的本地文件/tmp/SQL_LOAD_MB-1-0的内容,加载到表db2.t中

load data命令有两种用法

不加 local,读取服务端的文件,必须在secure_file_priv指定目录或者子目录下

加上 local,读取客户端的文件,只要mysql客户端有这个访问权限即可

顺便一提, 这个命令并不会生成表结构文件,如果需要表结构的定义,需要在mysqldump中加入-tab参数,可以导出表的定义和csv数据文件

mysqldump -h$host -P$port -u$user —single-transaction  –set-gtid-purged=OFF db1 t –where=”a>900″ –tab=$secure_file_priv

上面的介绍的都是都通过逻辑导出并导入数据,有没有直接物理导入数据的方法

比如直接将db1.t表的.frm文件和.idb文件拷贝到db2目录上

有没有物理导数据的方法?直接把db1.t表的.frm文件上,和idb文件拷贝到db2目录下,可行吗

直接当然不行的,两个物理文件,需要在数据字典中注册的

MySQL 5.6引入了可传输表空间的方法,

会复制一个跟表t相同的表 可以通过导出+导入表空间的方式,来实现物理拷贝表

假设要从db1库下,拷贝一个表t相同的表r,流程如下

1.执行create table r like t,创建一个相同表结构的空表

2.alter table r discard tablespace,那么r.idb会被删除

3.执行flush table t fro export 这时候会在db1目录下生成一个t.cfg文件

4.在db1目录下,执行cp t.cfg r.cfg; cp t.ibd r.ibd两个命令

5.执行unlock tables t.cfg文件会被删除

6.alter table r import tablespace 这个r.idb文件作为表r的新的表空间,而且文件的数据内容和t.ibd是相同的,于是表r中有了表t中相同的数据

图片

关于整个表的拷贝过程,需要注意

1.执行完成flush table命令后,整个表处于只读状态,直到执行unlock tables才释放锁

2.执行import tablespace的时候,为了数据一致,会修改r.idb的表空间id,而这个表空间id存在于每一个数据页中,因此是一个非常大的文件

本章说了三种将一个表的数据导入另一个表中

分别为两种逻辑到导入

导成sql文件,导成csv文件

和一种物理备份

物理备份的速度最快,对大表来说事合适,但是有一定的局限性,必须要全表拷贝,需要到服务器端拷贝,必须是innoDB引擎的

mysqldump不支持复杂的语句导出

select … into outfile 不能导出表结构,但支持的语句就多了

至于一个问题

为什么binlog_formate=statement的时候,binlog记录的load data命令是带local的,这个命令是发往备库去执行的,备库执行的时候是本地执行的,为什么需要这个local呢

因为如果不加客户端,就需要在服务端上去执行这个load命令,受到了secure_file_priv这个参数的限制

但是如果放在客户端上执行,只需要客户端具有本地文件夹的权限即可,而且客户端的线程既然能创建文件,想必也能执行文件

发表评论

邮箱地址不会被公开。 必填项已用*标注