Total Pageviews

Monday 16 July 2012

错误使用MySQL字符集

发现一种普遍的、错误的使用MySQL字符集的案例:
创建数据表时,指定数据表的default charset=utf8但在客户端写入数据时,却没有设置对应的字符集
默 认MySQL客户端的字符集一般是Latin1,这样就导致数据库将原本是utf8编码的数据,当作Latin1编码,然后转换成utf8编码,然后保存 到数据文件中。由于读取时,客户端依然是Latin1字符集,这时,MySQL数据库服务程序又将数据文件中存储的数据转换成Latin1编码,发送给客 户端。错上加错,负负得正,客户端居然得到了正确的数据。于是很多程序员就误以为就应该这样使用。
错误的影响在于数据文件容量大大增加,看以下的测试用例:

CREATE TABLE `x` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `c` varchar(1000) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=latin1;

CREATE TABLE `x2` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `c` varchar(1000) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8


drop procedure tx;
drop procedure tx2;
truncate table x;
truncate table x2;
delimiter ;;
create procedure tx()
begin
    declare i int default 100000;
    while i > 0 do
        set i=i-1;
        insert into x (c) values ('【要娶的女人】:不一定好看,但在你的眼里要耐看;不一定懂很多道理,但争论时要能和你讲道理;不一定纯洁无瑕,但不能水性杨花;不一定挣很多钱,但 能知道为你省钱;不一定能走进你的思想里,但要能走进你的情感里;不一定爱你死去活来,但要能让你变得更精彩;不一定能为你争面子,但要能和你过日子 ');
    end while;
end;;
delimiter ;

delimiter ;;
create procedure tx2()
begin
    declare i int default 100000;
    while i > 0 do
        set i=i-1;
        insert into x2 (c) values ('【要娶的女人】:不一定好看,但在你的眼里要耐看;不一定懂很多道理,但争论时要能和你讲道理;不一定纯洁无瑕,但不能水性杨花;不一定挣很多钱,但 能知道为你省钱;不一定能走进你的思想里,但要能走进你的情感里;不一定爱你死去活来,但要能让你变得更精彩;不一定能为你争面子,但要能和你过日子 ');
    end while;
end;;
delimiter ;

上面的SQL要存为UTF8字符集,然后执行。

分别调用两个存储过程,对x、x2两个数据表进行数据初始化。

然后查看数据表的信息:

mysql> show table status like 'x'\G
*************************** 1. row ***************************
           Name: x
         Engine: InnoDB
        Version: 10
     Row_format: Compact
           Rows: 100037
 Avg_row_length: 488
    Data_length: 48840704
Max_data_length: 0
   Index_length: 0
      Data_free: 6291456
 Auto_increment: 100001
    Create_time: 2011-02-28 09:52:02
    Update_time: NULL
     Check_time: NULL
      Collation: latin1_swedish_ci
       Checksum: NULL
 Create_options:
        Comment:
1 row in set (0.00 sec)

mysql> show table status  like 'x2'\G
*************************** 1. row ***************************
           Name: x2
         Engine: InnoDB
        Version: 10
     Row_format: Compact
           Rows: 100025
 Avg_row_length: 1033
    Data_length: 103415808
Max_data_length: 0
   Index_length: 0
      Data_free: 5242880
 Auto_increment: 100001
    Create_time: 2011-02-28 09:52:14
    Update_time: NULL
     Check_time: NULL
      Collation: utf8_general_ci
       Checksum: NULL
 Create_options:
        Comment:
1 row in set (0.00 sec)

[root@vm10110041 test]# ls -lh x*.ibd
-rw-rw---- 1 mysql mysql 108M Feb 28 10:09 x2.ibd
-rw-rw---- 1 mysql mysql  56M Feb 28 10:04 x.ibd


可以看到,表x2,由于错误的使用字符集,导致数据文件比x大出一倍左右。

再来看一下真实的数据:

数据表x中存储的是正确的数据.
数据表x2中存储的是由Latin1强制转换成utf8后的数据.

有兴趣的同学可以分析一下x2的数据是如何转换的,以及如何逆向转换成正确的数据。

综上所述,错误的使用字符集,可能导致大量的资源(磁盘空间,内存空间,CPU资源)的浪费,一定要给予高度的重视。