MySQL数据导入导出乱码问题

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 数据库数据导入导出是经常发生的操作,但是有的时候却眼看一个看的懂的数据导入或者导出来,发现看不懂了,成了乱码,这是什么情况?

场景

  • 程序使用gbk编码,表使用的是latin1编码,而我再一次倒入数据的操作中使用了utf8的终端,指定--default-character-set='latin1'倒入的数据是乱码,而后来将终端换成gbk之后酒倒入成功了
  • 通过变换插入数据的终端,模拟我们平常需要倒入数据的终端
  • 通过变更查询数据的终端,来模拟我们程序的查询操作
  • default-character-set变更能够正确的读取中文字符

测试环境

  • mysql server和Linux是utf8的字符集
  • 使用xshell作为终端进行输入
  • 建立一张表存储字符集是latin1
  • 使用mysqlclient 进行插入和查询,查看查询到的数据是否正确

实验步骤

  1. 使用mysqlclient,--default-character-set='latin1' 这个选测进行测试,看看他到底改变了那些字符集,如下图所示
    
    [root@5kh4z42 goufu]#  mysql -u superdba -padmin -S /tmp/mysql3443.sock   -e 'show variables like "%char%"';
    
    +--------------------------+----------------------------------------------+
    
    | Variable_name            | Value                                        |
    
    +--------------------------+----------------------------------------------+
    
    | character_set_client     | utf8                                         |
    
    | character_set_connection | utf8                                         |
    
    | character_set_database   | utf8                                         |
    
    | character_set_filesystem | binary                                       |
    
    | character_set_results    | utf8                                         |
    
    | character_set_server     | utf8                                         |
    
    | character_set_system     | utf8                                         |
    
    | character_sets_dir       | /usr/local/xywy/mysql-5.5.38/share/charsets/ |
    
    +--------------------------+----------------------------------------------+
    
    [root@5kh4z42 goufu]#  mysql -u superdba -padmin -S /tmp/mysql3443.sock  --default-character-set='latin1' -e 'show variables like "%char%"';
    
    +--------------------------+----------------------------------------------+
    
    | Variable_name            | Value                                        |
    
    +--------------------------+----------------------------------------------+
    
    | character_set_client     | latin1                                       |
    
    | character_set_connection | latin1                                       |
    
    | character_set_database   | utf8                                         |
    
    | character_set_filesystem | binary                                       |
    
    | character_set_results    | latin1                                       |
    
    | character_set_server     | utf8                                         |
    
    | character_set_system     | utf8                                         |
    
    | character_sets_dir       | /usr/local/xywy/mysql-5.5.38/share/charsets/ |
    
    +--------------------------+----------------------------------------------+
    

  2. 这里可以看出具体的client和connection与results是取决于client的,默认是和文件系统统一的
  3. 创建一张latin1字符集的表进行测试,看看mysql如何对字符集进行转换,老的或者奇葩业务如何使用latin1存储中文
    CREATE TABLE `aa` (
    
    `id` int(11) DEFAULT NULL,
    
    `name` varchar(100) DEFAULT NULL
    
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1

  4. 插入测试数据
    终端切换为UTF8字符集,执行命令
    mysql -u superdba -padmin -S /tmp/mysql.sock  --default-character-set='latin1' -e 'insert into  test.aa values(1,"啊啊")';
    终端切换为GBK字符集,执行命令
    mysql -u superdba -padmin -S /tmp/mysql.sock  --default-character-set='latin1' -e 'insert into  test.aa values(2,"啊啊")';
    终端切换为UTF8字符集,执行命令
    mysql -u superdba -padmin -S /tmp/mysql.sock   -e 'insert into  test.aa values(3,"啊啊")';
    终端切换为GBK字符集,执行命令
    mysql -u superdba -padmin -S /tmp/mysql.sock   -e 'insert into  test.aa values(4,"啊啊")';

  5. 查询数据
    终端切换为UTF8字符集,执行命令
    [root@5kh4z42 goufu]# mysql -u superdba -padmin -S /tmp/mysql3443.sock  --default-character-set='latin1' -e 'select * from test.aa';
    
    +------+--------+
    
    | id   | name   |
    
    +------+--------+
    
    |    1 | 啊啊 |
    
    |    2 | °¡°¡   |
    
    |    3 | ????   |
    
    |    4 | ??     |
    
    +------+--------+
    
    终端切换为GBK字符集,执行命令
    
    [root@5kh4z42 goufu]# mysql -u superdba -padmin -S /tmp/mysql3443.sock  --default-character-set='latin1' -e 'select * from test.aa';
    
    +------+--------+
    
    | id   | name   |
    
    +------+--------+
    
    |    1 | 鍟婂晩 |
    
    |    2 | 啊啊   |
    
    |    3 | ????   |
    
    |    4 | ??     |
    
    +------+--------+
    
    终端切换为UTF8字符集,执行命令
    
    [root@5kh4z42 goufu]# mysql -u superdba -padmin -S /tmp/mysql3443.sock   -e 'select * from test.aa';
    
    +------+----------------+
    
    | id   | name           |
    
    +------+----------------+
    
    |    1 | å•Šå•Š         |
    
    |    2 | °¡°¡           |
    
    |    3 | ????           |
    
    |    4 | ??             |
    
    +------+----------------+
    
    终端切换为GBK字符集,执行命令
    
    [root@5kh4z42 goufu]# mysql -u superdba -padmin -S /tmp/mysql3443.sock   -e 'select * from test.aa';
    
    +------+----------------+
    
    | id   | name           |
    
    +------+----------------+
    
    |    1 | 氓鈥⑴犆モ€⑴?        |
    
    |    2 | 掳隆掳隆           |
    
    |    3 | ????           |
    
    |    4 | ??             |
    
    +------+----------------+

  6. 排查问题
    查看数据库底层存储的值,执行如下命令
    
    [root@5kh4z42 goufu]# mysql -u superdba -padmin -S /tmp/mysql3443.sock   -e 'select id,hex(name) from test.aa';
    
    +------+--------------+
    
    | id   | hex(name)    |
    
    +------+--------------+
    
    |    1 | E5958AE5958A |
    
    |    2 | B0A1B0A1     |
    
    |    3 | 3F3F3F3F     |
    
    |    4 | 3F3F         |
    
    +------+--------------+

    • 问题到这里其实有一些清晰了,结合我们之前的只是utf8存储汉字是3字节存储,即2^8^3,换算成十六进制就是2^4^2^3即6个16位数表示,所以'啊' 对应的编码是%E5%95%8A,同理算出并验证GBK编码的'啊' 的编码是%B0%A1,如我们所看到的3F其实就是?的编码,urf8和gbk是一样的

    • 这里结合之前的只是,得知latin1是单字节编码,如果在存储的时候他识别不了会按照单自己存储,所以将他存储的二进制码交给其他的字符集就可以进行复原,试验中换成了ascii码得到的结果也是一样的,这可能就是单字节码的特性。
  7. 既然单字节存在这种转换规律那么gbk和utf8 之间是怎样进行转换的呢
    • 首先,创建一张表结构如下

      create table cc (
      
      id int ,
      
      name varchar(100),
      
      terminal varchar(100),
      
      `client` varchar(100)
      
      ) charset=gbk;

    • 然后,我们分别使用utf8终端执行如下命令

      mysql -u superdba -pguzaneyR@cj7M6m -S /tmp/mysql3443.sock  --default-character-set='gbk' -e 'insert into  test_liuyaxin.cc values(1,"啊啊","utf8","gbk")';
      
      mysql -u superdba -pguzaneyR@cj7M6m -S /tmp/mysql3443.sock   -e 'insert into  test_liuyaxin.cc values(1,"啊啊","utf8","utf8")';
      

    • 然后在gbk终端下执行命令

      mysql -u superdba -pguzaneyR@cj7M6m -S /tmp/mysql3443.sock  --default-character-set='gbk' -e 'insert into  test_liuyaxin.cc values(2,"啊啊","gbk","gbk")';
      
      mysql -u superdba -pguzaneyR@cj7M6m -S /tmp/mysql3443.sock   -e 'insert into  test_liuyaxin.cc values(2,"啊啊","gbk","utf8")';
      

    • 最后使用utf8终端查询结果

      mysql -u superdba -pguzaneyR@cj7M6m -S /tmp/mysql3443.sock  -e 'select *,hex(name) from  test_liuyaxin.cc';
      
      +------+-----------+----------+--------+--------------+
      
      | id   | name      | terminal | client | hex(name)    |
      
      +------+-----------+----------+--------+--------------+
      
      |    1 | 鍟婂晩    | utf8     | gbk    | E5958AE5958A |
      
      |    1 | 啊啊      | utf8     | utf8   | B0A1B0A1     |
      
      |    2 | 啊啊      | gbk      | gbk    | B0A1B0A1     |
      
      |    2 | ????      | gbk      | utf8   | 3F3F3F3F     |
      
      +------+-----------+----------+--------+--------------+

    • 发现了当终端-->client-->egine这个过程中,如果进行多次编码转换,最后就会是乱码,而且无药可救,因为在第二次编码转换的时候就已经失去了原来的字符的含义,而当这个过程中只有一次转换的时候就算是乱码,他仍然可以存储下来,这样在反向解码的时候就可以还原,而单字节码如果出现不识别字符则会按照传递给他的编码进行存储,这样在反向解码的时候就可以识别出来

    • 这样也可以看出来文件系统应该是不参与解码工作的,而是让两个进程(shell session和mysqlclient)进行编码转换,自己只是在中间将二进制码进行传递,其实mysql变量character_set_filesystem的值为binary时,表示文件系统字符集只负责将传递二进制数据
  8. 回归正题
    • 既然说到导入导出的乱码问题,说到现在,其实解决这个问题的根源就在于,当你想吧备份文件进行恢复的时候,首先你需要能够正确的识别文件,即你传到服务器上的文件不是乱码,而且要与你程序所使用的字符集一致,而你所使用的倒入mysql 数据库的client 的字符集要和你底层的表的字符集相同,这样就不会出现乱码,前提是需要你使用单字节编码
    • 当然,说这么多,只是为了解释这样存储的可能性,并且解答了疑问,但是并不是代表着推荐这么做,对于数据库,当然还是希望字符集进行统一,这样也就生了很多的麻烦
相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
11天前
|
canal 消息中间件 关系型数据库
Canal作为一款高效、可靠的数据同步工具,凭借其基于MySQL binlog的增量同步机制,在数据同步领域展现了强大的应用价值
【9月更文挑战第1天】Canal作为一款高效、可靠的数据同步工具,凭借其基于MySQL binlog的增量同步机制,在数据同步领域展现了强大的应用价值
90 4
|
19天前
|
关系型数据库 MySQL 数据库
RDS MySQL灾备服务协同解决方案构建问题之数据库备份数据的云上云下迁移如何解决
RDS MySQL灾备服务协同解决方案构建问题之数据库备份数据的云上云下迁移如何解决
|
14天前
|
SQL 存储 缓存
MySQL是如何保证数据不丢失的?
文章详细阐述了InnoDB存储引擎中Buffer Pool与DML操作的关系。在执行插入、更新或删除操作时,InnoDB为了减少磁盘I/O,会在Buffer Pool中缓存数据页进行操作,随后将更新后的“脏页”刷新至磁盘。为防止服务宕机导致数据丢失,InnoDB采用了日志先行(WAL)机制,通过将DML操作记录为Redo Log并异步刷新到磁盘,结合双写机制和合理的日志刷新策略,确保数据的持久性和一致性。尽管如此,仍需合理配置参数以平衡性能与数据安全性。
MySQL是如何保证数据不丢失的?
|
11天前
|
存储 关系型数据库 MySQL
|
12天前
|
SQL 关系型数据库 MySQL
SQL Server、MySQL、PostgreSQL:主流数据库SQL语法异同比较——深入探讨数据类型、分页查询、表创建与数据插入、函数和索引等关键语法差异,为跨数据库开发提供实用指导
【8月更文挑战第31天】SQL Server、MySQL和PostgreSQL是当今最流行的关系型数据库管理系统,均使用SQL作为查询语言,但在语法和功能实现上存在差异。本文将比较它们在数据类型、分页查询、创建和插入数据以及函数和索引等方面的异同,帮助开发者更好地理解和使用这些数据库。尽管它们共用SQL语言,但每个系统都有独特的语法规则,了解这些差异有助于提升开发效率和项目成功率。
71 0
|
19天前
|
SQL 关系型数据库 MySQL
mysql误删数据后,你会怎么办?
mysql误删数据后,你会怎么办?
40 0
|
22天前
|
Kubernetes 关系型数据库 MySQL
k8s练习--通过NFS+PV+PVC+POD,部署一个MySQL服务,并将MySQL的数据进行持久化存储
本文档介绍了如何使用Kubernetes (K8s)、NFS、PersistentVolume (PV)、PersistentVolumeClaim (PVC)和Pod来部署并实现MySQL服务的数据持久化存储。Kubernetes是一个用于自动化部署、扩展和管理容器化应用的强大平台。NFS作为一种网络文件系统协议,能够使Kubernetes集群中的Pod跨节点访问共享文件。PV和PVC机制则提供了持久化的存储解决方案,确保数据即使在Pod生命周期结束后仍得以保留。
|
22天前
|
SQL 关系型数据库 MySQL
mysql不等于<>取特定值反向条件的时候字段有null值或空值读取不到数据
对于数据库开发的专业人士来说,理解NULL的特性并知道如何正确地在查询中处理它们是非常重要的。以上所介绍的技巧和实例可以帮助你更精准地执行数据库查询,并确保数据的完整性和准确性。在编写代码和设计数据库结构时,牢记这些细节将有助于你避免许多常见的错误,提高数据库应用的质量与性能。
31 0
|
23天前
|
关系型数据库 MySQL Windows
MySQL数据导入:MySQL 导入 Excel 文件.md
MySQL数据导入:MySQL 导入 Excel 文件.md
|
23天前
|
监控 Cloud Native 关系型数据库
【跨区域PolarDB-MySQL主备互通】:揭秘如何跨越万里实现数据无缝同步,打造坚不可摧的灾备体系!
【8月更文挑战第20天】阿里云PolarDB是一款兼容MySQL协议的云原生数据库服务,提供高性能与高可用性。本文介绍如何在PolarDB-MySQL中实现跨区域主备同步。首先创建主备两个集群,接着通过MySQL复制功能配置同步:获取主节点复制信息、配置备节点复制并启动复制进程。最后,通过`SHOW SLAVE STATUS\G;`监控复制状态,确保数据同步正常。此方法可提升数据的可靠性和可用性,需考虑网络条件对性能的影响。
32 0

热门文章

最新文章