记一次小破站emoji导致数据丢失引发的思考
在写一篇文章时,因为在文章前面插入了一个emoji表情,保存后该篇文章丢失得剩下前面一部分
排查下来发现是emoji及后面的部分入库全部截断了
而恰好这台服务器的mysql的binlog状态为OFF,意味着丢失的数据无法恢复。这么悲剧的事情,也就只能当做教训
接下来要为这次事故做防灾和解决方案
原因分析
表文本字段为utf8,而emoji需要utf8mb4编码才能支持
测试条件:mysql 版本:5.5.53,不开启严格模式(默认模式)
创建表指定编码为uft8
CREATE TABLE `test1` (
"name" varchar(1) DEFAULT NULL
) ENGINE=MyISAM AUTO_INCREMENT=545 DEFAULT CHARSET=utf8;
测试插入emoji
INSERT INTO `mydemo`.`test1` (`name`) VALUES ('🤣')
> Affected rows: 1
> 时间: 0.001s
测试结果:截断了emoji的字符,存入了一个?
意味着非严格模式,存储的内容可能因为超出而自动截断
检查mysql是否开启严格模式
在my.cnf配置文件中,发现没有配置sql_mode字段,没配置是不是默认宽松模式?
不猜测,直接本地环境测试
由于本地是Windows环境,mysql的配置文件为my.ini(Linux下为my.cnf)
测试字段为name:
CREATE TABLE "test1" ("name" varchar(1) DEFAULT NULL);
测试情况1:不存在sql_mode字段的情况,向表插入超出长度的字符
测试结果:插入成功,自动截断,无警告(说明默认就是宽松模式)
测试情况2:宽松模式 sql-mode="ANSI"
插入一串字符
INSERT INTO `mydemo`.`test1` (`name`) VALUES ('defefeefe')
> Affected rows: 1
> 时间: 0.001s
测试结果:插入成功,插入内容为为“d”,自动截断,无警告
插入一个emoji
INSERT INTO `mydemo`.`test1` (`name`) VALUES ('🤣')
> Affected rows: 1
> 时间: 0.001s
测试结果:插入成功,插入内容为“?”,自动截断,无警告
测试情况3:严格模式(之一) sql-mode = "TRADITIONAL"
插入一串字符串
INSERT INTO `mydemo`.`test1` (`name`) VALUES ('cdefefeefe')
> 1406 - Data too long for column 'name' at row 1
> 时间: 0s
测试结果:插入失败,提示Data too long
插入一个emoji
INSERT INTO `mydemo`.`test1` (`name`) VALUES ('🤣')
> 1366 - Incorrect string value: '\xF0\x9F\xA4\xA3' for column 'name' at row 1
> 时间: 0.001s
测试结果:插入失败,提示字段值非法Incorrect string value
已上测试结果表明,如果不开启严格模式,则数据丢失问题,只要长度超出就会丢失
另外,数据库字符集、表字符集、字段字符集是互不影响的,即数据库字符集为utf8,表字符集为utf8,字段字符集为utf8mb4,测试sql带emoji依然能够正确存取
猜测数据库字符集只是用来给创建表时默认使用字符集,而表字符集是创建字段时,给字段设定的字符集。因此哪怕数据库和表都不是utf8mb4,单独字段为utf8mb4,emoji表情依然能插入成功
解决方案第一步:开启binlong日志,做数据灾难演练
正在补充
解决方案第二步:让小破站支持emoji
这里有三个步骤
1、数据库表文本字段需要从utf8改为utf8mb4
2、在mysql配置文件my.cnf里配置sql-mode为严格模式
3、连接mysql的终端需要修改database的字符集charset从'utf8'修改为'utf8mb4'
其中2不是必要的,但是不做,以后还有有其他数据丢失问题,超出自动截断
做了1之后在mysql命令终端测试插入存储均没问题,但是不做3这一步,会导致存入的emoji依然是问号???
具体操作:
为安全起见,数据库、数据库表、数据库字段都需要改为utf8mb4
修改数据库编码
--修改数据库字符集
ALTER DATABASE 数据库名 CHARACTER SET = utf8mb4;
--修改表字符集
alter table 数据表名 convert to character set utf8mb4;
--修改字符字符集
ALTER TABLE `test` CHANGE COLUMN `字段名` `字段名` varchar(12) CHARACTER SET utf8mb4;
2、进入my.cnf,在[mysqld]下增加一行
sql-mode = "TRADITIONAL"
3、修改项目内连接数据库的配置
'charset' => 'utf8mb4',
4、重启mysql,httpd服务即可
# 重启mysql服务
service mariadb restart
# 重启apache http服务
service httpd restart
自此,小破站的文章可以正确存取emoji表情了,说多了都是泪
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
评论已关闭