資料遷移(MYSQL--ORACLE)中碰到的亂碼問題

Steven1981發表於2009-11-09

MYSQL字符集UTF8,ORACLE字符集GBK
LINUX 5.3
MYSQL 5.1
ORACLE 9208
資料行數1800W

[@more@]

基於這個資料量,我們首先想到的是將MYSQL資料DUMP到文字檔案,再用SQLLOAD泵進ORACLE.

但我們在第一步(MYSQL資料DUMP到文字檔案,)就出現了問題. 中文顯示為亂碼.

所以我們把首要問題先解決: 如何讓MYSQL正確地DUMP到文字檔案?

其實這個歸根還是字符集的問題. 但在這個場景,我們還要考慮到MYSQL對不同的輸出方式也有不同的字符集轉換模式.


首先來我們來看一下,DUMP資料有多種方法:
1. set names gbk; select ... into outfile '/tmp/a1.txt' from test.t1;
2. mysql -uroot -h127.0.0.1 --default-character-set=gbk -e " select ... from test.t1" >>/tmp/a1.txt
3. mysqldump -uroot -h127.0.0.1 --tab "/tmp" --fields-terminated-by='&&&' --lines-terminated-by='$$$$$' --default-character-set=utf8 test t1


建立測試表:
set names gbk;
CREATE TABLE `t1` ( `col0` varchar(100) , `col1` varchar(100) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 ;
insert into t1 values ('中國','1aaaaaa');
select * from t1;

下面我們分別來看一下,上面說的三種方法能不能正確DUMP中文到文字檔案

1. set names gbk; select * into outfile '/tmp/a1.txt' from test.t1;
========================================================================
: (none) 15:35:26> use test;
Database changed
: test 15:35:27> set names gbk;
Query OK, 0 rows affected (0.00 sec)

: test 15:35:30> select * from t1;
+------+---------+
| col0 | col1 |
+------+---------+
| 中國 | 1aaaaaa |
+------+---------+
1 row in set (0.00 sec)

: test 15:35:33> select * into outfile '/tmp/a1.txt' from test.t1;
Query OK, 1 row affected (0.00 sec)

: test 15:35:53> system cat /tmp/a1.txt
涓?浗 1aaaaaa

: test 15:35:59> system hexdump /tmp/a1.txt
0000000 b8e4 e5ad bd9b 3109 6161 6161 6161 000a
000000f
========================================================================
註解:用MYSQL CLIENT GBK看能正常顯示中文,但OUTFILE裡存的卻是UTF8的編碼資料.
在這裡猜測是:
當資料返回給MYSQL客戶端的時候, 資料經過character_set_results=gbk的轉換.
當資料返回給OUTFILE的時候,是直接DUMP資料.(經過測試,character_set_results不管設成什麼,都不影響OUTFILE生成的結果)

2. mysql -e "select .." >> /tmp/a2.txt
========================================================================
[root@PerfTestDB1 tmp]# mysql -uroot -h127.0.0.1 -N -s --default-character-set=gbk -e " select * from test.t1" >/tmp/a2.txt
[root@PerfTestDB1 tmp]# more /tmp/a2.txt
中國 1aaaaaa
[root@PerfTestDB1 tmp]# hexdump /tmp/a2.txt
0000000 d0d6 fab9 3109 6161 6161 6161 000a
000000d
========================================================================
註解:在這裡能正常DUMP出來,是因為返回給MYSQL CLIENT的時候,已經經過character_set_results=gbk的轉換.
這個相當於第一種方法裡的前半部分,直接檢視(select * from t1;)

3. mysqldump (這種方式將會產生檔案: .sql--建表語句 .txt--資料)
========================================================================
[root@PerfTestDB1 tmp]# mysqldump -uroot -h127.0.0.1 --tab "/tmp" --fields-terminated-by='&&&' --lines-terminated-by='$$$$$' --default-character-set=gbk test t1
[root@PerfTestDB1 tmp]# more t1.txt
涓?浗&&&1aaaaaa$$$$$
[root@PerfTestDB1 tmp]# hexdump t1.txt
0000000 b8e4 e5ad bd9b 2626 3126 6161 6161 6161
0000010 2424 2424 0024
0000015
========================================================================
註解: 不管上面的--default-character-set設成GBK/UTF8/LATIN1,匯出的結果都是一致的.
其實這也說明這種方式也是直接將表的真實資料編碼直接DUMP出來,而沒有經過轉換.
為了進一步證明上述的說法.我STRACE了一下第2,第3兩種方法.
strace mysql -uroot -h127.0.0.1 -N -s --default-character-set=gbk -e " select * from test.t1" > /tmp/mysql.log
strace mysqldump -uroot -h127.0.0.1 --tab "/tmp" --fields-terminated-by='&&&' --lines-terminated-by='$$$$$' --default-character-set=gbk test t1 >>/tmp/mysqldump.log

透過檢視日誌檔案:/tmp/mysql.log /tmp/mysqldump.log ,發現在mysql.log中.有這麼一段:
-----------------------------------------------------
munmap(0xb7f4e000, 4096) = 0
stat64("/usr/share/mysql/charsets/Index.xml", {st_mode=S_IFREG|0755, st_size=18173, ...}) = 0
open("/usr/share/mysql/charsets/Index.xml", O_RDONLY|O_LARGEFILE) = 3
read(3, "

相關文章