从长远的角度来看,新技术所节省下来的成本,并不会给生产者带来更高的利润,而是降低了产品的价格,使消费者受惠。
《牛奶可乐经济学》
今日一朋友联系说他们oracle数据库中表的中文数据都变成乱码了,需要帮忙查找为什么出现该问题。因对方数据库在外网,得到消息后,立马通过远程工具连接过去进行分析。
1,基本情况
oracle数据库:两套
源端数据库表中建立了触发器,对表的增、删、改操作进行监听,一旦操作,将会把对应数据写入中间表,然后后台任务不断读取中间表信息,往目标数据库写入数据。
客户读取的是目标端数据库数据。
2,问题初步分析
oracle数据库字符集一般在数据库创建过程中就会指定,字符集指定后,一般不建议再次进行修改(如若要修改,需要重启数据库)。现场数据库中中文数据突然乱码,可能由以下几种情况导致:
-
客户端字符集与服务器端字符集不一致,导致获取到的数据为乱码
-
服务器端字符集突然进行了修改,导致数据库中中文为乱码
-
源端同步数据库字符集与目标端字符集不一致,导致同步过来数据为乱码。
-
源端数据库采集的数据为乱码
3,问题处理
根据客户提供表,查看数据,发现中文为乱码
select * from ***_***_result;(为了保护客户隐私,此处星号用代表字母)
SERIAL PRODUCT_NAME
-201808-00446 澶ч娇杞?鎶辫酱绠辫閰?
-201808-00446 澶ч娇杞?鎶辫酱绠辫閰?
-201808-00446 澶ч娇杞?鎶辫酱绠辫閰?
-201808-00446 澶ч娇杞?鎶辫酱绠辫閰?
表中数据为乱码,第一时间,我想到客户端字符集,进行查看,发现字符集正常(配置了环境变量NLS_LANG)
select * from nls_instance_parameters;
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
于是我进行数据库server端字符集查看,可以看到服务端字符集为ZHS16GBK。
select * from nls_database_parameters;
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_CURRENCY $
NLS_ISO_CURRENCY AMERICA
NLS_NUMERIC_CHARACTERS .,
NLS_CHARACTERSET ZHS16GBK
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DD-MON-RR
NLS_DATE_LANGUAGE AMERICAN
NLS_SORT BINARY
NLS_TIME_FORMAT HH.MI.SSXFF AM
NLS_TIMESTAMP_FORMAT DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_TZ_FORMAT DD-MON-RR HH.MI.SSXFF AM TZR
NLS_DUAL_CURRENCY $NLS_COMP BINARY
NLS_LENGTH_SEMANTICS BYTENLS_NCHAR_CONV_EXCP FALSE
NLS_NCHAR_CHARACTERSET AL16UTF16
NLS_RDBMS_VERSION 11.2.0.3.0
目标端数据库字符集没问题,没办法,只能查看源端数据库字符集(源端数据库在不同网络),结果字符集也为ZHS16GBK:
select * from nls_database_parameters;
NLS_LANGUAGE AMERICAN
NLS_TERRITORY AMERICA
NLS_CURRENCY $
NLS_ISO_CURRENCY AMERICA
NLS_NUMERIC_CHARACTERS .,
NLS_CHARACTERSET ZHS16GBK
NLS_CALENDAR GREGORIAN
NLS_DATE_FORMAT DD-MON-RR
NLS_DATE_LANGUAGE AMERICAN
NLS_SORT BINARY
NLS_TIME_FORMAT HH.MI.SSXFF AM
NLS_TIMESTAMP_FORMAT DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_TZ_FORMAT DD-MON-RR HH.MI.SSXFF AM TZR
NLS_DUAL_CURRENCY $
NLS_COMP BINARY
NLS_LENGTH_SEMANTICS BYTE
NLS_NCHAR_CONV_EXCP FALSE
NLS_NCHAR_CHARACTERSET AL16UTF16
NLS_RDBMS_VERSION 11.2.0.1.0
看到两端数据库字符集一致,我顿时傻眼了,两端数据库字符集都一样,为什么客户会说数据库中中文字符乱码了呢?
于是我想难道是有人突然动了数据库字符集,或者源端今日录入的数据为乱码?于是我登陆源端进行目标端乱码对应表:
select serial,product_name from ***_***_ZXJ.***_***_RESULT where serial='-201808-00446';(为了保护客户隐私,此处星号用代表字母)
SERIAL PRODUCT_NAME
-201808-00446 大齿/装配
-201808-00446 大齿/装配
结果源端数据库表数据显示没问题,查看oracle alert日志,发现源端也没人修改字符集。
自此,只能一个解释了,那么就是目标端字符集有人动过,查看目标端alert日志,发现问题:
ALTER DATABASE DEFAULT TABLESPACE "USERS"
Completed: ALTER DATABASE DEFAULT TABLESPACE "USERS"
alter database character set INTERNAL_CONVERT AL32UTF8
Mon Aug 20 10:26:04 2018
Thread 1 advanced to log sequence 2 (LGWR switch)
Current log# 2 seq# 2 mem# 0: D:\ORACLE\ORADATA\ORCL\REDO02.LOG
Mon Aug 20 10:26:06 2018
Updating character set in controlfile to AL32UTF8
Synchronizing connection with database character set information
Database Characterset is AL32UTF8
No Resource Manager plan active
WARNING: AQ_TM_PROCESSES is set to 0. System operation might be adversely affected.
Starting background process CJQ0
Completed: ALTER DATABASE OPEN
Tue Nov 27 22:41:44 2018
CJQ0 started with pid=16, OS id=9160
Tue Nov 27 22:41:48 2018
ALTER DATABASE CHARACTER SET ZHS16GBK
ORA-12712 signalled during: ALTER DATABASE CHARACTER SET ZHS16GBK ...
ALTER DATABASE CHARACTER SET INTERNAL_USE ZHS16GBK
Tue Nov 27 22:42:05 2018
可以看到,2018年8月20日,数据库字符集为AL32UTF8,11月27日,字符集变为ZHS16GBK。
那么为什么客户提到数据是突然变为乱码的呢,至此,我怀疑是因为客户之之前调用的是2018年11月27日之后的数据,因为源端与目标端字符集一致,所以没显示乱码。今日因为调取到2018年11月27日之前的数据,因之前字符集AL32UTF8,变为ZHS16GBK,所有中文为乱码(AL32UTF8为ZHS16GBK的父集)。为了验证,我查询了一个拥有时间字段的表,进行数据验证。
最新数据,数据正常:
select name from ***_**_MRL where create_date>=to_date('2019-01-01','yyyy-mm-dd') order by create_date desc; (为了保护客户隐私,此处星号用代表字母)
NAME
Tc/M车II端点子
历史数据,数据乱码:
select name from ***_**_MRL where create_date<=to_date('2018-01-01','yyyy-mm-dd') order by create_date desc;(为了保护客户隐私,此处星号用代表字母)
NAME
CCB-Tp杞is鐓ф槑瀹夎鍥?
CCB-Mc杞is鐓ф槑绯荤粺瀹夎鍥?
至此,问题根源找到。
4,总结
通过本案例可以看到:平常我们使用数据库或者进行数据库变更时,不会注意到字符集这个小小的配置项,就好像字符集对数据库没影响一样。但是,一旦出现问题,字符集引起的问题是致命的。任何数据库管理员对于生产系统数据库的改动,都须慎之又慎!
欢迎大家关注以下公众号进行数据库方面知识探讨: