insert into t2(c,d) select c,d from t;
在可重复读隔离级别下,binlog_format=statement,需要对表 t 的所有行和间隙加锁。
insert into t2(c,d) select c,d from t;
insert into t values(-1,-1,-1);
上面两个语句序列,如果不对表T进行加锁,那么就可能出现 第一个 insert 语句先执行,但是后写入 binlog 的情况。于是,在 binlog_format=statement 的情况下,binlog 里面语句序列就相反了。到了备库,insert到T2的数据就多了-1这条记录。
insert into t2(c,d) (select c+1, d from t force index(c) order by c desc limit 1);
insert into t(c,d) (select c+1, d from t force index(c) order by c desc limit 1);
上面这两条语句 ,区别在于是否往select源表的数据中插入数据,这可能会导致循环插入的情况,建议引入临时表进行处理。
insert 语句如果出现唯一键冲突,会在冲突的唯一值上加共享的 next-key lock(S 锁)。因此,碰到由于唯一键约束导致报错后,要尽快提交或回滚事务,避免加锁时间过长。
insert into … on duplicate key update 这个语义的逻辑是,插入一行数据,如果碰到唯一键约束,就执行后面的更新语句。如果有多个行违反了唯一性约束,就会按照索引的顺序,修改跟第一个索引冲突的行。
两个表之间拷贝数据:
1)insert into select 。如果数量比较大,加上limit ,确认select条件是否走索引。缺点是会锁select的表。
2)导出成excel,然后拼sql 成 insert into values(),(),()的形式
3)任务的逻辑是查询m条记录,然后多个线程分到几个任务执行,比如每个线程n条记录,插入后,在查询新的m条记录处理。
Mysqldump:使用 mysqldump 命令将数据导出成一组 INSERT 语句。
mysqldump -h$host -P$port -u$user --add-locks=0 --no-create-info --single-transaction --set-gtid-purged=OFF db1 t --where="a>900" --result-file=/client_tmp/t.sql
主要参数含义如下:
1)–single-transaction 的作用是,在导出数据的时候不需要对表 db1.t 加表锁,而是使用 START TRANSACTION WITH CONSISTENT SNAPSHOT 的方法;
2)–add-locks 设置为 0,表示在输出的文件结果里,不增加" LOCK TABLES t WRITE;" ;
3)–no-create-info 的意思是,不需要导出表结构;
4)–set-gtid-purged=off 表示的是,不输出跟 GTID 相关的信息;
5)–result-file 指定了输出文件的路径,其中 client 表示生成的文件是在客户端机器上的。
一条 INSERT 语句里面会包含多个 value 对,这是为了后续用这个文件来写入数据的时候,执行速度可以更快。
导入到其他数据库中
mysql -h127.0.0.1 -P13000 -uroot db2 -e "source client_tmp/t.sql"
source 并不是一条 SQL 语句,而是一个客户端命令。这个命令的流程是这样的:
1)打开文件,默认以分号为结尾读取一条条的 SQL 语句;
2)将 SQL 语句发送到服务端执行。
导出 CSV 文件,直接将结果导出成.csv 文件,导出到服务端本地目录:
select * from db1.t where a>900 into outfile '/server_tmp/t.csv';
这条语句会将结果保存在服务端。
1)如果你执行命令的客户端和 MySQL 服务端不在同一个机器上,客户端机器的临时目录下是不会生成 t.csv 文件的
2)into outfile 指定了文件的生成位置(/server_tmp/),这个位置必须受参数 secure_file_priv的限制。
3)参数 secure_file_priv 的可选值和作用分别是:
3.1如果设置为 empty,表示不限制文件生成的位置,这是不安全的设置;
3.2如果设置为一个表示路径的字符串,就要求生成的文件只能放在这个指定的目录,或者它的子目录;
3.3如果设置为 NULL,就表示禁止在这个 MySQL 实例上执行 select … into outfile 操作。
4)这条命令不会覆盖文件,需要确保 /server_tmp/t.csv 这个文件不存在,否则执行语句时就会因为有同名文件的存在而报错。
得到.csv 导出文件后,可以用 load data 命令将数据导入到目标表
load data infile '/server_tmp/t.csv' into table db2.t;
如果 binlog_format=statement,这个 load 语句记录到 binlog 里以后,怎么在备库重放
1) 主库执行完成后,将 /server_tmp/t.csv 文件的内容直接写到 binlog 文件中。
2) 往 binlog 文件中写入语句 load data local infile ‘/tmp/SQL_LOAD_MB-1-0’ INTO TABLE `db2`.`t`。
3) 把这个 binlog 日志传到备库。
4) 备库的 apply 线程在执行这个事务日志时:
4.a. 先将 binlog 中 t.csv 文件的内容读出来,写入到本地临时目录 /tmp/SQL_LOAD_MB-1-0 中;
4.b. 再执行 load data 语句,往备库的 db2.t 表中插入跟主库相同的数据。
可传输表空间(transportable tablespace) 的方法(物理导入)
MySQL 5.6 版本引入,可以通过导出 + 导入表空间的方式,实现物理拷贝表的功能
直接把 db1.t 表的.frm 文件和.ibd 文件拷贝到 db2 目录下,是不行的,因为一个 InnoDB 表,还需要在数据字典中注册。因为数据字典中没有 db2.t 这个表,系统是不会识别。
目标是在 db1 库下,复制一个跟表 t 相同的表 r,具体的执行步骤如下:
1) 执行 create table r like t,创建一个相同表结构的空表;
2) 执行 alter table r discard tablespace,这时候 r.ibd 文件会被删除;
3) 执行 flush table t for export,这时候 db1 目录下会生成一个 t.cfg 文件;
(flsuh table 命令之后,db1.t 整个表处于只读状态,直到执行 unlock tables 命令后才释放读锁)
4) 在 db1 目录下执行 cp t.cfg r.cfg; cp t.ibd r.ibd;这两个命令(这里需要注意的是,拷贝得到的两个文件,MySQL 进程要有读写权限);
5) 执行 unlock tables,这时候 t.cfg 文件会被删除;
6) 执行 alter table r import tablespace,将这个 r.ibd 文件作为表 r 的新的表空间,由于这个文件的数据内容和 t.ibd 是相同的,所以表 r 中就有了和表 t 相同的数据。
(import tablespace 的时候,为了让文件里的表空间 id 和数据字典中的一致,会修改 r.ibd 的表空间 id;这个表空间 id 存在于每一个数据页中,所以每一页都需要修改,相对来说耗时较长,但是比逻辑导入还是要快)
三种方式优缺点比较
1)物理拷贝的方式速度最快,尤其对于大表拷贝来说是最快的方法;必须是全表拷贝,不能只拷贝部分数据;源表和目标表都是使用 InnoDB 引擎
2)mysqldump 生成包含 INSERT 语句文件的方法,可以在 where 参数增加过滤条件,来实现只导出部分数据。缺点:不能使用 join 这种比较复杂的 where 条件写法。
3)select … into outfile 的方法是最灵活的,支持所有的 SQL 写法。缺点:每次只能导出一张表的数据,而且表结构也需要另外的语句单独备份




