mysql匯入資料load data infile用法整理
有時候我們需要將大量資料批量寫入資料庫,直接使用程式語言和Sql寫入往往很耗時間,其中有一種方案就是使用MySql Load data infile匯入檔案的形式匯入資料,這樣可大大縮短資料匯入時間。
假如是從MySql客戶端呼叫,將客戶端的檔案匯入,則需要使用 load local data infile.
LOAD DATA INFILE 語句以很高的速度從一個文字檔案中讀取行到一個表中。檔名必須是一個文字字串。
1,開啟load local data infile.
假如是Linux下編譯安裝,
如果使用原始碼編譯的MySQL,在configure的時候,需要新增引數:--enable-local-infile 客戶端和伺服器端都需要,否則不能使用local引數。
./configure --prefix=/usr/local/mysql --enable-local-infile
make install
若是其它系統,可在配置檔案中配置:
在MySql 配置檔案My.ini檔案中下面項中加入local-infile=1:
add:
[mysqld]
local-infile=1
[mysql]
local-infile=1
客戶端和服務端度需要開啟,對於客戶端也可以在執行命中加上--local-infile=1 引數:
mysql --local-infile=1 -uroot -pyourpwd yourdbname
如:
如:/usr/local/mysql/bin/mysql -uroot -h192.168.0.2 -proot databaseName --local-infile=1 -e "LOAD DATA LOCAL INFILE 'data.txt' into table test(name,sex) "
2, 編碼格式注意:
若包含中文,請保證匯入檔案、連線字串、匯入表都是UTF-8編碼。
3,執行
在使用LOAD DATA到MySQL的時候,有2種情況:
(1)在遠端客戶端(需要新增選項:--local-infile=1)匯入遠端客戶端文字到MySQL,需指定LOCAL(預設就是ignore),加ignore選項會放棄資料,加replace選項會更新資料,都不會出現唯一性約束問題。
[[email protected] tmp]$mysql -uzhuxu -pzhuxu test -h10.254.5.151 --local-infile=1--show-warnings -v -v -v \
> -e "LOAD DATA LOCAL INFILE '/tmp/2.txt' INTO TABLE tmp_loaddata FIELDS TERMINATED BY ','";
(2)在本地伺服器匯入本地伺服器文字到MySQL,不指定LOACL,出現唯一性約束衝突,會失敗回滾,資料匯入不進去,這個時候就需要加ignore或者replace來匯入資料。
mysql>LOAD DATA INFILE '/home/zhuxu/1.txt' INTO TABLE tmp_loaddata FIELDS TERMINATED BY ',';
4,事務分析
步驟是這樣的:
1,開啟binlog,設定binlog_format=row,執行reset master;
2,load data infile xxxxx;
3,檢視binlog。
可以看出,總共是一個事務,也通過mysqlbinlog查看了binary log,確認中間是被拆分成了多個insert形式。所以load data infile基本上是這樣執行的:
begin
insert into values(),(),(),()...
insert into values(),(),(),()...
insert into values(),(),(),()...
...
...
commit
當然,由於row格式的binlog的語句並不是很明顯的記錄成多值insert語句,它的格式時
insert into table
set @1=
set @2=
...
set @n=
insert into table
set @1=
set @2=
...
set @n=
insert ...
;注意這裡有一個分號‘;’,其實前面這一部分就相當於前面說的多值insert形式
然後接下來就重複上面的那種格式,也就是一個load data infile 拆成了多個多值insert語句。
前面說的是row格式記錄的load data infile,那麼對於statement是怎麼樣的呢?statement格式的binlog,它是這樣記錄的,binlog中還是同樣的load data語句,但是在記錄load data 語句之前,它會先將你master上這個load data 使用到的csv格式的檔案拆分成多個部分,然後傳到slave上(在mysql的tmpdir下),當然傳這些csv格式的檔案也會記錄binlog event,然後最後真正的SQL語句形式就是load data local infile '/tmp/SQL_X_Y'這種形式(這裡假設mysql的tmpdir是預設的/tmp),實際上這樣很危險,比如tmpdir空間不夠,那就會報錯。不過從效率上來說兩者可能差不多,因為statement格式的binlog也是拆分成了多個語句。
附:
(1)load data infile 和 load local data infile 在 innodb和MyISAM 同步方面的區別
對MyISAM引擎:
(1)對master伺服器進行 ‘load’ 操作,
(2)在master上所操作的load.txt檔案,會同步傳輸到slave上,並在tmp_dir 目錄下生成 load.txt檔案
master伺服器插入了多少,就傳給slave多少
(3)當master上的load操作完成後,傳給slave的檔案也結束時,
即:在slave上生成完整的 load.txt檔案
此時,slave才開始從 load.txt 讀取資料,並將資料插入到本地的表中
對innodb引擎:
(1)主資料庫進行 ‘Load’ 操作
(2)主資料庫操作完成後,才開始向slave傳輸 load.txt檔案,
slave接受檔案,並在 tmp_dir 目錄下生成 load.txt 檔案
接受並生成完整的load.txt 後,才開始讀取該檔案,並將資料插入到本地表中
異常情況處理:
1)對MyISAM引擎
當資料庫執行load,此時如果中斷:
Slave端將報錯,例如:
####################################################################
Query partially completed on the master (error on master: 1053) and was aborted.
There is a chance that your master is inconsistent at this point.
If you are sure that your master is ok,
run this query manually on the slave and then restart the slave with SET GLOBAL SQL_SLAVE_SKIP_COUNTER=1;
START SLAVE; . Query: 'LOAD DATA INFILE '/tmp/SQL_LOAD-2-1-3.data' IGNORE INTO TABLE `test_1`
FIELDS TERMINATED BY ',' ENCLOSED BY '' ESCAPED BY '\\' LINES TERMINATED BY '\n' (`id`, `name`, `address`)'
###########################################################################################
按照提示,在slave伺服器上:
(1) 使用提示的load命令,將主伺服器傳輸過來的load檔案,在從伺服器上執行
(2)讓從伺服器跳過錯誤。set global sql_slave_skip_counter=1;
(3)開啟同步
2)對Innodb引擎
由於innodb是事務型的,所以會把load檔案的整個操作當作一個事務來處理,
中途中斷load操作,會導致回滾。
與此相關的一些引數:
max_binlog_cache_size----能夠使用的最大cache記憶體大小。
當執行多語句事務時,max_binlog_cache_size如果不夠大,
系統可能會報出“Multi-statement
transaction required more than 'max_binlog_cache_size' bytes of storage”的錯誤。
備註:以load data 來說,如果load的檔案大小為512M,在執行load 的過程中,
所有產生的binlog會先寫入binlog_cache_size,直到load data 的操作結束後,
最後,再由binlog_cache_size 寫入二進位制日誌,如mysql-bin.0000008等。
所以此引數的大小必須大於所要load 的檔案的大小,或者當前所要進行的事務操作的大小。
max_binlog_size------------Binlog最大值,一般設定為512M或1GB,但不能超過1GB。
該設定並不能嚴格控制Binlog的大小,尤其是Binlog遇到一根比較大事務時,
為了保證事務的完整性,不可能做切換日誌的動作,只能將該事務的所有SQL都記錄進
當前日誌,直到事務結束
備註:有時能看到,binlog生成的大小,超過了設定的1G。這就是因為innodb某個事務的操作比較大,
不能做切換日誌操作,就全部寫入當前日誌,直到事務結束。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
(2)C# 批量插入Mysql
public void loadData(Connection connection)
{
long starTime = System.currentTimeMillis();
String sqlString = "load data local infile ? into table test" ;
PreparedStatement pstmt;
try {
pstmt = connection.prepareStatement(sqlString);
pstmt.setString(1, "tfacts_result" );
pstmt.executeUpdate();
pstmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System. out .println( "program runs " + (endTime - starTime) + "ms" );
}
public static void mysql_batch( string sqlStr, int point)
{
string sql = "insert into test(node1, node2, weight) values(?, ?, ?)" ;
Connection conn = getConn( "mysql" );
conn.setAutoCommit( false );
//clear(conn);
try
{
PreparedStatement prest = conn.prepareStatement(sql);
//long a = System.currentTimeMillis();
for ( int x = 1; x <= count; x++)
{
prest.setInt(1, x);
prest.setString(2, "張三" );
prest.addBatch();
if (x % point == 0)
{
prest.executeBatch();
conn.commit();
}
}
prest.close();
//long b = System.currentTimeMillis();
//print("MySql批量插入10萬條記錄", a, b, point);
}
catch (Exception ex)
{
ex.printStackTrace();
}
finally
{
close(conn);
}
|
使用into outfile 和 load data infile匯入匯出備份資料
如果要匯出一個表中的部分欄位或者部分符合條件的記錄,需要用到了mysql的into outfile 和 load data infile。
例如下面的mysql命令是把select的mytable表中的資料匯出到/home/db_bak2012檔案。
1 |
select * from mytable where status!=0 and name!= '' into outfile '/home/db_bak2012' fields terminated by '|' enclosed by '"' lines terminated by '\r\n' ;
|
假如要匯入剛才備份的資料,可以使用load file方法,例如下面的mysql命令,把匯出的資料匯入了mytable_bak的表中:
1 |
load data infile '/home/db_bak2012' into table mytable_bak fields terminated by '|' enclosed by '"' lines terminated by '\r\n' ;
|
這種方法的好處是,匯出的資料可以自己規定格式,並且匯出的是純資料,不存在建表資訊,你可以直接匯入另外一個同資料庫的不同表中,相對於mysqldump比較靈活機動。
1 2 3 4 5 6 7 8 9 10 |
#基本語法:
load data [low_priority] [ local ] infile 'file_name txt' [replace | ignore]
into table tbl_name
[fields
[terminated by 't' ]
[OPTIONALLY] enclosed by '' ]
[escaped by'\' ]]
[lines terminated by 'n' ]
[ignore number lines]
[(col_name, )]
|
load data low_priority infile "/home/mark/data sql" into table Orders; 2 如果指定local關鍵詞,則表明從客戶主機讀檔案。如果local沒指定,檔案必須位於伺服器上。 3 replace和ignore關鍵詞控制對現有的唯一鍵記錄的重複的處理。如果你指定replace,新行將代替有相同的唯一鍵值的現有行。如果你指定ignore,跳過有唯一鍵的現有行的重複行的輸入。如果你不指定任何一個選項,當找到重複鍵時,出現一個錯誤,並且文字檔案的餘下部分被忽略。例如: load data low_priority infile "/home/mark/data sql" replace into table Orders; 4 分隔符 (1) fields關鍵字指定了檔案記段的分割格式,如果用到這個關鍵字,MySQL剖析器希望看到至少有下面的一個選項:
terminated by分隔符:意思是以什麼字元作為分隔符
enclosed by欄位括起字元
escaped by轉義字元 terminated by描述欄位的分隔符,預設情況下是tab字元(\t)
enclosed by描述的是欄位的括起字元。
escaped by描述的轉義字元。預設的是反斜槓(backslash:\ ) 例如:load data infile "/home/mark/Orders txt" replace into table Orders fields terminated by',' enclosed by '"'; (2)lines 關鍵字指定了每條記錄的分隔符預設為'\n'即為換行符 如果兩個欄位都指定了那fields必須在lines之前。如果不指定fields關鍵字預設值與如果你這樣寫的相同: fields terminated by'\t' enclosed by ’ '' ‘ escaped by'\\' 如果你不指定一個lines子句,預設值與如果你這樣寫的相同: lines terminated by'\n' 例如:load data infile "/jiaoben/load.txt" replace into table test fields terminated by ',' lines terminated by '/n'; 5 load data infile 可以按指定的列把檔案匯入到資料庫中。 當我們要把資料的一部分內容匯入的時候,,需要加入一些欄目(列/欄位/field)到MySQL資料庫中,以適應一些額外的需要。比方說,我們要從Access資料庫升級到MySQL資料庫的時候 下面的例子顯示瞭如何向指定的欄目(field)中匯入資料:
load data infile "/home/Order txt" into table Orders(Order_Number, Order_Date, Customer_ID); 6 當在伺服器主機上尋找檔案時,伺服器使用下列規則:
(1)如果給出一個絕對路徑名,伺服器使用該路徑名。
(2)如果給出一個有一個或多個前置部件的相對路徑名,伺服器相對伺服器的資料目錄搜尋檔案。
(3)如果給出一個沒有前置部件的一個檔名,伺服器在當前資料庫的資料庫目錄尋找檔案。
例如: /myfile txt”給出的檔案是從伺服器的資料目錄讀取,而作為“myfile txt”給出的一個檔案是從當前資料庫的資料庫目錄下讀取。 注意:欄位中的空值用\N表示
我的文章一般淺顯易懂,不會搞那麼深入讓大家很難理解。(其實我水平也不咋樣)
LOAD DATA INFILE 一直被認為是MySQL很強大的一個數據匯入工具,因為他速度非常的快。
不過有幾個問題一定要注意
1、編碼。
2、靈活匯入匯出。
我來舉兩個例子說明一下。
一、關於編碼
我們的示例文字檔案:
"我愛你","20","相貌平常,經常耍流氓!哈哈"
"李奎","21","相貌平常,經常耍流氓!哈哈"
"王二米","20","相貌平常,經常耍流氓!哈哈"
"老三","24","很強"
"老四","34","XXXXX"
"老五","52","***%*¥*¥*¥*¥"
"小貓","45","中間省略。。。"
"小狗","12","就會叫"
"小妹","21","PP的很"
"小壞蛋","52","表裡不一"
"上帝他爺","96","非常英俊"
"MM來了","10","。。。"
"歌頌黨","20","社會主義好"
"人民好","20","的確是好"
"老高","10","學習很好"
"斜三","60","眼睛斜了"
"中華之子","100","威武的不行了"
"大米","63","我愛吃"
"蘋果","15","好吃"
我們的示例表結構:
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table |
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| t0 | CREATE TABLE `t0` (
`id` bigint(20) unsigned NOT NULL auto_increment,
`name` char(20) NOT NULL,
`age` tinyint(3) unsigned NOT NULL,
`description` text NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_name` (`name`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8 |
+-------+-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
我們把這個文字檔案從WINDOWS 下COPY到LINUX下看看
mysql> load data infile '/tmp/t0.txt' ignore into table t0 character set gbk fields terminated by ',' enclosed by '"' lines terminated by '\n' (`name`,`age`,`description`);
Query OK, 19 rows affected (0.01 sec)
Records: 19 Deleted: 0 Skipped: 0 Warnings: 0
mysql> select * from t0;
+----+----------+-----+----------------------------+
| id | name | age | description |
+----+----------+-----+----------------------------+
| 1 | 我愛你 | 20 | 相貌平常,經常耍流氓!哈哈 |
| 2 | 李奎 | 21 | 相貌平常,經常耍流氓!哈哈 |
| 3 | 王二米 | 20 | 相貌平常,經常耍流氓!哈哈 |
| 4 | 老三 | 24 | 很強 |
| 5 | 老四 | 34 | XXXXX |
| 6 | 老五 | 52 | ***%*¥*¥*¥*¥ |
| 7 | 小貓 | 45 | 中間省略。。。 |
| 8 | 小狗 | 12 | 就會叫 |
| 9 | 小妹 | 21 | PP的很 |
| 10 | 小壞蛋 | 52 | 表裡不一 |
| 11 | 上帝他爺 | 96 | 非常英俊 |
| 12 | MM來了 | 10 | 。。。 |
| 13 | 歌頌黨 | 20 | 社會主義好 |
| 14 | 人民好 | 20 | 的確是好 |
| 15 | 老高 | 10 | 學習很好 |
| 16 | 斜三 | 60 | 眼睛斜了 |
| 17 | 中華之子 | 100 | 威武的不行了 |
| 18 | 大米 | 63 | 我愛吃 |
| 19 | 蘋果 | 15 | 好吃 |
+----+----------+-----+----------------------------+
19 rows in set (0.00 sec)
我來說明一下相關的引數
關於我的匯入語句,我現在直說兩個,其他的參考手冊。
character set gbk;
這個字符集一定要寫,要不然就會亂碼或者只匯入一部分資料。
ignore into table
因為name 列加了唯一索引,加這個是為了避免重複資料插入報錯。
加入我們再次執行這個匯入語句就會發現
Query OK, 0 rows affected (0.00 sec)
Records: 19 Deleted: 0 Skipped: 19 Warnings: 0
沒有任何值匯入,因為裡面已經有了相同的值。
這裡也可以用replace into table
MySQL會把相同的先幹掉,再插入新的值。
mysql> load data infile '/tmp/t0.txt' replace into table t0 character set gbk fields terminated by ',' enclosed by '"' lines terminated by '\n' (`name`,`age`,`description`);
Query OK, 38 rows affected (0.00 sec)
Records: 19 Deleted: 19 Skipped: 0 Warnings: 0
mysql> select * from t0;
+----+----------+-----+----------------------------+
| id | name | age | description |
+----+----------+-----+----------------------------+
| 20 | 我愛你 | 20 | 相貌平常,經常耍流氓!哈哈 |
| 21 | 李奎 | 21 | 相貌平常,經常耍流氓!哈哈 |
| 22 | 王二米 | 20 | 相貌平常,經常耍流氓!哈哈 |
| 23 | 老三 | 24 | 很強 |
| 24 | 老四 | 34 | XXXXX |
| 25 | 老五 | 52 | ***%*¥*¥*¥*¥ |
| 26 | 小貓 | 45 | 中間省略。。。 |
| 27 | 小狗 | 12 | 就會叫 |
| 28 | 小妹 | 21 | PP的很 |
| 29 | 小壞蛋 | 52 | 表裡不一 |
| 30 | 上帝他爺 | 96 | 非常英俊 |
| 31 | MM來了 | 10 | 。。。 |
| 32 | 歌頌黨 | 20 | 社會主義好 |
| 33 | 人民好 | 20 | 的確是好 |
| 34 | 老高 | 10 | 學習很好 |
| 35 | 斜三 | 60 | 眼睛斜了 |
| 36 | 中華之子 | 100 | 威武的不行了 |
| 37 | 大米 | 63 | 我愛吃 |
| 38 | 蘋果 | 15 | 好吃 |
+----+----------+-----+----------------------------+
19 rows in set (0.00 sec)
(`name`,`age`,`description`);
這些也就是具體的表屬性了,指明這個就可以匯入想要的資料。
2、關於靈活性,其實也就是一個記錄功能
如果想在匯入的時候記錄一下匯入的具體時間怎麼辦?
我們來看看
先加一個時間屬性記錄匯入時間。
mysql> alter table t0 add update_time timestamp not null;
Query OK, 19 rows affected (0.00 sec)
Records: 19 Duplicates: 0 Warnings: 0
幹掉唯一索引
mysql> alter table t0 drop index idx_name;
Query OK, 19 rows affected (0.00 sec)
Records: 19 Duplicates: 0 Warnings: 0
mysql> load data infile '/tmp/t0.txt' into table t0 character set gbk fields terminated by ',' enclosed by '"' lines terminated by '\n' (`name`,`age`,`description`) set update_time=current_timestamp;
Query OK, 19 rows affected (0.00 sec)
Records: 19 Deleted: 0 Skipped: 0 Warnings: 0
mysql> select * from t0;
+----+----------+-----+----------------------------+---------------------+
| id | name | age | description | update_time |
+----+----------+-----+----------------------------+---------------------+
| 20 | 我愛你 | 20 | 相貌平常,經常耍流氓!哈哈 | 0000-00-00 00:00:00 |
…………
| 24 | 老四 | 34 | XXXXX | 0000-00-00 00:00:00 |
| 25 | 老五 | 52 | ***%*¥*¥*¥*¥ | 0000-00-00 00:00:00 |
…………
| 35 | 斜三 | 60 | 眼睛斜了 | 0000-00-00 00:00:00 |
| 36 | 中華之子 | 100 | 威武的不行了 | 0000-00-00 00:00:00 |
…………
| 60 | 王二米 | 20 | 相貌平常,經常耍流氓!哈哈 | 2008-06-30 14:58:37 |
…………
| 68 | 上帝他爺 | 96 | 非常英俊 | 2008-06-30 14:58:37 |
| 69 | MM來了 | 10 | 。。。 | 2008-06-30 14:58:37 |
…………
| 75 | 大米 | 63 | 我愛吃 | 2008-06-30 14:58:37 |
| 76 | 蘋果 | 15 | 好吃 | 2008-06-30 14:58:37 |
+----+----------+-----+----------------------------+---------------------+
38 rows in set (0.00 sec)
新匯入的19條記錄時間被記錄了下來。
只是之前的資料庫沒有記錄,不過現在不需要這些重複資料了。
幹掉他就可以了
mysql> alter table t0 order by id desc;
Query OK, 38 rows affected (0.01 sec)
Records: 38 Duplicates: 0 Warnings: 0
mysql> alter ignore table t0 add unique index idx_name (`name`);
Query OK, 38 rows affected (0.00 sec)
Records: 38 Duplicates: 19 Warnings: 0
mysql> alter table t0 order by id asc;
Query OK, 19 rows affected (0.01 sec)
Records: 19 Duplicates: 0 Warnings: 0
mysql> select * from t0;
+----+----------+-----+----------------------------+---------------------+
| id | name | age | description | update_time |
+----+----------+-----+----------------------------+---------------------+
| 58 | 我愛你 | 20 | 相貌平常,經常耍流氓!哈哈 | 2008-06-30 14:58:37 |