Oracle与Hive中文乱码问题总结
核心问题:字符集不一致导致中文乱码。
Oracle处理:
Hive处理:
最佳实践:新建表统一使用UTF-8编码,历史表通过ETL转码处理,避免直接修改生产表编码。银行数据迁移需特别注意源文件与目标系统编码一致性。
Oracle和Hive都是怎么查sql的字符集编码格式的,中文乱码怎么处理
在银行的数据迁移和日常查询中,中文乱码是绕不开的坎,根源通常是数据库字符集与客户端/文件字符集不一致。
下面分别讲 Oracle 和 Hive 怎么查字符集、怎么处理乱码。
一、Oracle 字符集查看
Oracle 的字符集分为服务端和客户端两个概念,乱码往往就是它俩不一致导致的。
1. 查询服务端数据库字符集
— 方法1:查看NLS_CHARACTERSET参数(最常用)
SELECT * FROM nls_database_parameters
WHERE parameter IN ('NLS_CHARACTERSET', 'NLS_NCHAR_CHARACTERSET');
— 方法2:获取完整的语言环境信息(包含客户端设置)
SELECT userenv('language') FROM dual;
结果示例:
NLS_CHARACTERSET = ZHS16GBK 或 AL32UTF8
ZHS16GBK 是简体中文字符集,AL32UTF8 是 Unicode 编码。
2. 查看客户端/当前会话字符集
— 查看当前会话的字符集设置(即客户端NLS_LANG)
SELECT userenv('language') FROM dual;
二、Hive 字符集查看与元数据乱码解决
Hive 依赖 MySQL(或 PostgreSQL)存储元数据,中文乱码通常是 Hive 元数据库(Metastore)的字符集不是 UTF-8 造成的。
1. 查看 Hive 表注释/字段注释是否正常
DESC FORMATTED your_table_name;
如果 COMMENT 字段显示为 ??? 或乱码,基本就是元数据字符集问题。
2. 检查并修改 Hive 元数据库(MySQL)字符集
登录 MySQL 元数据库:
— 查看 Metastore 数据库的字符集设置
SHOW VARIABLES LIKE 'character_set%';
— 查看具体表的字符集
SHOW CREATE DATABASE metastore;
修改元数据库中相关表的字段字符集为 UTF-8:
use metastore;
— 修改表注释(TABLE_PARAMS)的字符集
ALTER TABLE TABLE_PARAMS MODIFY COLUMN PARAM_VALUE VARCHAR(4000) CHARACTER SET utf8;
— 修改字段注释(COLUMNS_V2)的字符集
ALTER TABLE COLUMNS_V2 MODIFY COLUMN COMMENT VARCHAR(256) CHARACTER SET utf8;
— 修改分区参数(PARTITION_PARAMS)的字符集(如有)
ALTER TABLE PARTITION_PARAMS MODIFY COLUMN PARAM_VALUE VARCHAR(4000) CHARACTER SET utf8;
3. 检查数据文件本身的字符集
可以通过 Linux 命令 file -i 数据文件名 查看文件编码是否为 utf-8。
如果源文件是 GBK,需要使用 iconv 等工具进行转码后再加载。
4. 查询/修改当前会话的字符集
在执行查询前,可以设置当前会话的字符集:
— 设置当前会话字符集为 UTF-8
SET character_set_client = utf8;
SET character_set_results = utf8;
SET character_set_connection = utf8;
— 或者直接使用 SET NAMES 命令(效果等同于上面三条)
SET NAMES 'utf8';
三、中文乱码处理实战
场景1:Oracle 查询出来的中文是问号/乱码
原因:客户端字符集(NLS_LANG)与服务端不一致。
解决方案:统一客户端 NLS_LANG 设置。
-
Linux/Unix 环境:
export NLS_LANG="SIMPLIFIED CHINESE_CHINA.ZHS16GBK"
# 或者
export NLS_LANG="AMERICAN_AMERICA.AL32UTF8" -
Windows 环境:
-
打开注册表编辑器(regedit)
-
找到路径 HKEY_LOCAL_MACHINE\\SOFTWARE\\ORACLE\\KEY_OracleHome
-
修改或新建字符串值 NLS_LANG,设置为服务端一致的字符集(如 SIMPLIFIED CHINESE_CHINA.ZHS16GBK)
-
设置 NLS_LANG 后必须重启客户端才能生效。
场景2:Hive 建表时 COMMENT 显示乱码
原因:Hive 的元数据存储在 MySQL 中,默认字符集可能是 latin1,不支持中文。
解决方案:参照“第二部分:Hive 元数据乱码解决”,修改 Metastore 相关表的字段字符集为 utf8。
场景3:数据迁移过程中出现的乱码
排查思路:
检查源文件编码:是否为 UTF-8(Hive 对 UTF-8 支持最好)。
检查导入方式:使用 LOAD DATA 命令时,可尝试加上 CHARSET 'UTF-8' 参数(取决于具体版本)。
使用转换函数:如果数据已经乱码,在 Hive 查询时可尝试通过内置函数进行修正:
— 将 latin1 编码的列转换为 utf8 显示(慎用,取决于数据实际编码)
SELECT conv(column_name, 'latin1', 'utf8') FROM table_name;
总结:最佳实践
| Oracle | 查看服务端字符集 | SELECT * FROM nls_database_parameters; |
| Oracle | 查看客户端字符集 | SELECT userenv('language') FROM dual; |
| Oracle | 修复乱码 | 修改环境变量 NLS_LANG 与服务端一致 |
| Hive | 查看元数据乱码 | DESC FORMATTED table_name; |
| Hive | 解决元数据乱码 | 修改 Metastore 数据库中相关表的字段字符集为 utf8 |
| 通用 | 设置会话字符集 | SET NAMES 'utf8'; (MySQL) |
一句话总结:
Oracle 乱码先看 NLS_LANG,Hive 乱码先修 MySQL Metastore。
在银行数据开发中,养成统一使用 UTF-8 编码的习惯,能避免 90% 的中文乱码问题。
在银行数据开发中,养成统一使用 UTF-8 编码的习惯,是什么意思?不是说有的表的格式不能随便修改,而且改编码是需要更高权限才行?
你这个追问非常到位,说明你已经在用生产环境的思维考虑问题了,而不仅仅是学语法。
核心结论:
“养成统一使用 UTF-8 编码的习惯”不是让你去改生产表,而是让你在新建表、写 ETL、导文件时主动选 UTF-8,并理解乱码产生的根源。
下面分三个层次帮你彻底搞清楚。
一、“养成使用 UTF-8 习惯”到底是什么意思?
这句话针对的是你可以控制的范围,不是让你去动老旧系统。
| 新建表 | 建表时指定 CHARSET=utf8 | — |
| ETL 中间文件 | CSV/Parquet 统一用 UTF-8 | — |
| 数据交换 | 接口文件、Kafka 消息用 UTF-8 | — |
| 历史表 / 老旧系统 | ❌ 不要乱改 | 不要自己改 Oracle / MySQL 元数据 |
✅ 一句话:
从你手里出去的东西,尽量用 UTF-8。存量系统,按它原来的编码处理,不乱动。
二、为什么不能随便改?你说得很对
你说的是事实:
1️⃣ 改表编码确实需要更高权限
-
修改 Hive Metastore 字符集
→ 需要 MySQL 的 ALTER 权限,普通开发没有 -
修改 Oracle 数据库字符集
→ 需要 DBA 权限 + 停业务 + 重建库,几乎不可能让你干
2️⃣ 改编码可能造成更严重的乱码
text
GBK → 强制转 UTF-8(不按规则转)
=> 变成 UTF-8 环境下的“乱码永久化”
=> 比原来还难修
3️⃣ 表格式不能随便改
-
历史表下游依赖很多
-
你改了编码 → 下游 ETL / 应用 全部可能报错
✅ 所以:
存量表不是你的事,是 DBA + 数据治理 + 业务方一起决定的事。
三、银行实际工作中怎么处理编码问题?
场景 1:你新建一张表(你有控制权)
✅ 正确做法:主动用 UTF-8
Hive
sql
CREATE TABLE my_table (
id INT,
name STRING
)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\\t'
STORED AS TEXTFILE
TBLPROPERTIES ('serialization.encoding'='UTF-8');
如果底层存储是 ORC / Parquet,已经是二进制,不存在编码问题
MySQL(极少作为数仓,但可能用)
sql
CREATE TABLE my_table (
id INT,
name VARCHAR(100)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
场景 2:你发现历史表乱码(你不能改表)
❌ 错误做法
-
提工单要求 DBA 改表编码
-
自己写 ALTER 命令
✅ 正确做法(数据层解决)
Hive 查询时转换
sql
SELECT
col,
CONVERT(col USING UTF8) AS col_utf8
FROM old_table;
Sqoop 导出 / 导入时指定编码
bash
— 强制使用 UTF-8
–map-column-java "name=String" \\
–map-column-hive "name=STRING" \\
–fields-terminated-by '\\001' \\
–null-string '\\\\N' \\
–null-non-string '\\\\N' \\
–connect "jdbc:mysql://host/db?useUnicode=true&characterEncoding=UTF-8"
场景 3:两个异构系统对接(最真实)
-
Oracle(GBK) → Hive(UTF-8)
-
你不能改 Oracle
✅ 正确做法:在 ETL 中转码
bash
# 导出阶段指定编码
sqlplus user/pwd@orcl
set NLS_LANG=SIMPLIFIED CHINESE_CHINA.ZHS16GBK
spool data.csv
# 导入 Hive 时明确使用 UTF-8
LOAD DATA INPATH '/path/data.csv'
INTO TABLE hive_table
— 明确文件编码
— 部分版本支持
WITH SERDEPROPERTIES ('serialization.encoding'='UTF-8');
四、一个你一定能听懂的比喻
改表编码 ≈ 把一仓库老账本的文字全用修正液改一遍
-
你可以从今天开始用新本子(新表)
-
但不能动老账本(历史表)
-
老账本看不懂时,你可以配翻译(ETL 转码)
✅ 你问的“不能随便改”、“要更高权限”
→ 完全正确,不是你能动的事情
五、面试 / 工作中可以这样说(非常加分)
*“我理解,编码问题不是随便改表。我的原则是:新表主动用 UTF-8;存量表不动;乱码在 ETL 层用转码函数或调整连接参数解决。如果全局改编码,会申请 DBA 配合,并经过充分测试。”*
总结(请你记住这三条)
| 新建表 | ✅ 可以 | 主动指定 CHARSET=utf8 |
| 老表乱码 | ❌ 不能 | ETL 中转码 / 查询时转换 |
| 两个系统不一致 | ❌ 不能 | 导出 / 导入时统一编码(不碰表) |
“养成 UTF-8 习惯”不是改历史,而是规范未来。
不越权、不瞎改、不假设,这是银行数据开发的底线。


