41 怎麼最快的複製一張表

2021-09-26 03:28:35 字數 3116 閱讀 7200

如果可以控制對源表的掃瞄行數和加鎖範圍很小的話,我們簡單地使用insert ... select 語句即可實現。

為了避免對源表加讀鎖,更穩妥的方案是先將資料寫到外部檔案,然後再寫回目標表。有兩種方法。

建立乙個表db1.t,插入1000行資料,同時建立乙個相同結構的表db2.t。

如果要把db1.t中a>900的資料匯出來,插入db2.t中。

使用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

把結果輸出到臨時檔案。

命令中主要引數含義如下:

--single-transaction的作用是,在匯出資料的時候不需要對錶db1.t加鎖,而是使用start transaction with consistent sanpshot的方法;

--add-locks設定為0,表示在輸出檔案結果裡,不增加「lock tables t write;」;

--no-create-info的意思是,不需要匯出表結構;

--set-gtid-purged=off表示的是,不輸出gtid相關的資訊;

--result-file指定了輸出檔案的路徑,其中client表示生成的檔案是在客戶端機器上的。

匯出結果類似下面:

如果希望生成的檔案中一條insert語句直插入一行的話,可以在執行mysqldump命令的時候,加上引數--skip-extended-insert。

然後,通過下面命令,將這些insert語句放到db2庫里執行。

mysql -h127.0.0.1 -p13000 -uroot db2 -e "source /client_tmp/t.sql"

source是乙個客戶端命令,mysql客戶端執行這個命令的流程是這樣的:

開啟檔案,預設以分號為結尾讀取一條條的sql語句;

將sql語句傳送到服務端執行。

select * from db1.t where a>900 into outfile '/server_tmp/t.csv';

使用這條語句,需要注意:

這條語句會將結果儲存在服務端。如果執行命令的客戶端和mysql服務端不在同乙個機器上,客戶端機器的臨時目錄下是不會生成t.csv檔案的。

into outfile指定了檔案的生成位置(/server-tmp/),這個位置必須受引數secure_file_priv的限制。引數secure_file_priv的可選值和作用分別是:

2.1 如果設定為empty,表示不限制檔案生成的位置,這是不安全的設定;

2.2 如果設定為乙個表示路徑的字串,就要求生成的檔案只能放在這個指定的目錄,或者它的子目錄;

2.3 如果設定為null,就表示禁止這個mysql例項上執行select ... into outfile操作。

3.這條命令不會幫你覆蓋檔案,因此你要確保/server_tmp/t.csv這個檔案不存在,否則執行語句時就會因為有同名檔案的存在而報錯。

4.這條命令生成的文字檔案中,原則上乙個資料行對應文字檔案的一行。但是,如果欄位中包含換行符,在生成的文字中也會有換行符。不過類似換行符、製表符這類符號,前面都會跟上「\」這個轉義字元,這就可以跟字段之間、資料行之間的分隔符區分開。

得到.csv檔案後,可以用下面的load data命令將資料匯入到目錄表db2.t中。

load data infile '/server_tmp/t.csv' into table db2.t;

這條語句的執行流程如下:

開啟檔案/server_tmp/t.csv,以製表符(\t)作為欄位間的分隔符,以換行符(\n)作為記錄之間的分隔符,進行資料讀取;

啟動事務;

判斷每一行的字段數與表db2.t是否相同:

若不相同,直接報錯,事務回滾;

若相同,則構造成一行,呼叫innodb引擎介面,寫入到表中。

4.重複步驟3,直到/server_tmp/t.csv整個檔案讀入完成,提交事務。

前面都是邏輯導資料方法。

物理導資料方法?比如,直接把db1.t表的.frm檔案和.ibd檔案拷貝到db2目錄下,是否可行呢?

答案是不行。

因為,乙個innodb表,除了包含這兩個物理檔案外,還需要再資料字段中註冊。直接拷貝,資料字典中沒有db2.t這個表,系統是不會識別和接受它們的。

在mysql 5.6版本引入了可傳輸表空間的方法,可以通過匯出+匯入表空間的方式,實現物理拷貝的功能。

假設現在的目標是在db1庫下,複製乙個跟表t相同的表r,具體的執行步驟如下:

執行create table r like t,建立乙個相同表結構的空表;

執行alter table r discard tablespace,這時候r.ibd檔案會被刪除;

執行flush table t for export,這時候db1目錄下會生成乙個t.cfg檔案;

在db1目錄下執行cp t.cfg r.cfg; cp t.idb r.ibd; 這兩個命令(需要注意,拷貝得到的兩個檔案,mysql程序要有讀寫許可權);

執行unlock tables,這時候t.cfg檔案會被刪除;

執行alter table r import tablespace,將這個r.ibd檔案作為表r的新的表空間,由於這個檔案的資料內容和t.idb是相同的,所以表r中就有了和表t相同的資料。

這個流程,需要注意:

在第三步執行完flush table命令之後,db1.t整個表處於唯讀狀態,直到執行unlock tables命令後才釋放讀鎖;

在執行import tablespace的時候,為了讓檔案裡的表空間id和資料字典中的一致,會修改r.ibd的表空間id。而這個表空間id存在於每乙個資料頁中。因此,如果是乙個很大的檔案(比如tb級別),每個資料頁都需要修改,所以你會看到這個import語句的執行時需要一些時間的。當然,如果是相比於邏輯匯入的方法,import語句的耗時是非常短的。

從一張表中複製資料到另一張表中

分為兩種情況,一種是目標表不存在,另一種是目標表已存在,語法是不同的。分別以sqlserver和oracle為例,兩者略有不同。sqlserver中,如果目標表不存在 select into新錶名from舊表名 sqlserver中,如果目標表已存在 insertinto新錶名select from...

將一張表的資料插入另外一張表

表cmb send sms create table cmb send sms send id bigint 20 not null auto increment comment 主鍵id phone no varchar 32 not null comment 手機號碼 status varcha...

根據一張表去更新另一張表

最近在改乙個專案,由於是別人做好的,很多資料表資訊不全。不得不手工用sql更新資料表。現在又這麼2張表 第一張是管理員表 id 使用者id c id 分公司id p id 部門id name 使用者名稱 第二張是訂單表 id 訂單id com id 訂單所屬銷售的公司id dep id 訂單所屬銷售...