让建站和SEO变得简单

让不懂建站的用户快速建站,让会建站的提高建站效率!

公司资讯
五分钟看懂 MySQL 编解码道理
发布日期:2022-05-15 16:33    点击次数:78
  绪言

一位读者在腹地部署 MySQL 测试环境时遭遇一个问题,我合计挺有代表性的,是以写篇著作先容一下,看完深信你会对 MySQL 的编码机制有最实践的了解,本文的目次结构如下

读者问题简介 MyQL 编解码机制先容 问题解答 读者问题简介

MyQL 编解码机制先容

问题解答

读者问题简介

为求教便捷,以下的「我」指代读者

咱们清澈在 Java 中是通过 JDBC 来访谒数据库的,以访谒 MySQL 为例,需要建立以下 url 才智访谒 MySQL

jdbc:mysql://10.65.110.9:3306/test?connectTimeout=5000&socketTimeout=20000 

这么建立之前在我司的测试环境中 CRUD 是莫得问题的,可是自后想在个人的机器上部署一下 MySQL 环境就出问题了,领先为了保证数据的竣工性,我将公司测试机的 SQL 沿途导出后再导入到个人的 MySQL 环境中,可是诡异的事情发生了:此时在 Java 工程中如果查询的 SQL 中都是英文是不错通俗责任的,但如果包含汉文(比如 SELECT * FROM USER WHERE name = '张三')是无法查询到效果的。

遭遇这种情况,一般咱们会意想是编码鬈曲出现了问题,深信贤慧你不难发现上头的 jdbc url 似乎少了点什么,没错,便是莫得指定编码口头,只好按如下口头指定了编码口头(characterEncoding=UTF-8)即可通俗责任

jdbc:mysql://10.65.110.9:3306/test?connectTimeout=5000&socketTimeout=20000&characterEncoding=UTF-8 

至此问题也就科罚了,但奇怪的是之前为什么没指定编码口头亦然不错的呢,应该是 server 指定了编码口头,在哪指定的?要复兴这个问题,就必须得对 MySQL 的编码机制有所了解

MyQL 编解码机制先容

咱们先来望望 MySQL 中波及到哪些编码进程,假定客户端用的是 UTF-8 编码,那么发送一条 SQL 语句会发生如下的编解码进程:

假定此时的客户端为 Java 工程,用的是 intellj idea,其默许编码为 UTF-8,那么施行后这条语句会领先被 UTF-8 编码,然后再将其转成 unicode,在 Java 中统共的 String 都是以 unicode 字符存在的,然后再将 unicode 转为用 character_set_client 来编码 character_set_client 编码后是以二进制流的体式传到 MySQL 奇迹器的,然后再用 character_set_connection 解码,然后 MySQL 引擎(比如 innodDB 引擎)会对这条语句进行语法,词法融会,施行操作 施行后的效果会转为 DB 的编码入库 如果是 SELECT * FROM t 这么的查询操作,那么数据会从 DB 中解码后再用 character_set_connection 编码,再转为用 character_set_result 编码传给客户端,客户端再用 UTF-8 解码获取通俗效果

先通俗先容一下上述顺序中波及到的编码集

character_set_client: 客户端最终发送到奇迹端 SQL 所取舍的编码字符集 character_set_connection: MySQL 奇迹端收到顺序 1 编码后的二进制流后取舍的编码字符集,会将顺序 1 传过来的数据进行解码。一般与 character_set_client 是相同的,有人可能会奇怪,为什么会有这个字符集,平直用 character_set_client 来解码不就行了,它存在的道理是啥呢?其实主淌若为了作用上的的区别,character_set_client 主要用来客户端的编码,而 character_set_connection 主淌若为了赋予开垦人员融会语义的目田,比如筹商 SELECT LENGTH('中') 这么的场景,如果取舍 GBK 一个汉字 2 个长度,效果是 2,而如果是 UTF-8 编码,则效果是 3,是以荒谬设定一个 character_set_connection 编码,让路发人员不错笔据需要更目田地界说不同的业务场景 character_set_result: 效果集复返给客户端取舍的编码字符集

清澈了以上各个字符编码集所代表的释义,当今就不错松驰解说起原的问题了,咱们清澈对 MySQL 来说,操作无非便是增编削查,是以主要有以下两个滚动进程

如果是增编削操作,进程为:客户端--->character_set_client--->character_set_connection---->DB

如果是查操作,客户端--->character_set_client--->character_set_connection---->DB---->character_set_result

如果这两个滚动进程对应的每一步都是无损鬈曲,那么效果集就莫得问题的

什么是无损鬈曲

假定咱们要把用编码 A 暗意的字符 X,滚动为编码 B 的暗意体式,而编码 B 的字符聚首并莫得 X 这个字符,那么此时咱们就称这个鬈曲是有损的,如果在 B 的字符集都能找到 A 中的字符,那么便是无损的,是以最通俗的口头便是将每个顺序对应的编码字符集都缔造成相同的,比如都缔造成 UTF-8,这么就确定没问题了。

起原的问题解答

当今回过火来看一下起原的问题,为什么将 DB 数据从公司的测试机导入到个人机器后,如果 SQL 中包含有汉文查询如下 jdbc url 的建立会导致底本通俗复返的效果集失效呢?

jdbc:mysql://10.65.110.9:3306/test?connectTimeout=5000&socketTimeout=20000 

剖判是客户端--->character_set_client--->character_set_connection---->DB---->character_set_result 这个顺序中的效果集发生了有损鬈曲,到底是哪一步呢?

DB 表数据取舍的编码都是 UTF-8,如果只好搞明晰 character_set_client,character_set_connection,character_set_result 这三个编码字符集是啥问题就科罚了,这个问题的谜底得去官网找,来看下官网是怎么说的

The character encoding between client and server is automatically detected upon connection (provided that the Connector/J connection properties characterEncoding and connectionCollation are not set). You specify the encoding on the server using the system variable character_set_server (for more information, see Server Character Set and Collation). The driver automatically uses the encoding specified by the server.

To override the automatically detected encoding on the client side, use the characterEncoding property in the connection URL to the server. Use Java-style names when specifying character encodings. The following table lists MySQL character set names and their corresponding Java-style names:

从中咱们不错看到,如果未缔造 characterEncoding,那么 character_set_client,character_set_connection,character_set_result 这三的编码字符集与 character_set_server 的缔造交流,如果缔造了 characterEncoding,那么这三者的值与 characterEncoding 交流,这便是为什么指定了characterEncoding=utf8后 SQL 能通俗责任的原因了,

那为什么不指定 characterEncoding=utf8 在公司的测试 MySQL 奇迹器中不错通俗责任呢,剖判是缔造了 character_set_server,在哪缔造?在 MySQL 的建立文献 my.cnf 缔造

## my.cnf  [mysqld] character-set-server=utf8 

再来看为什么在个人的测试机中包含有汉文的 SQL 却不获胜呢,因为个人的测试机其时用 docker 搭了一个 MySQL,它的 my.cnf 文献是空的,这种情况下 character-set-server 编码字符集是 latin,于是 character_set_client,character_set_connection,character_set_result 这三者的编码字符集也都为 latin 了,剖判在第一步客户端转 chacacter_set_client 就出现了问题

咱们之前提过在 Java 中统共的字符串都以 unicode 体式存在,而 latin 字符集是不包含汉文的,那么剖判汉文的 unicode 在 latin1 中是找不到对应的字符的,这一步就会发生有损编码,这便是为什么在个人的机器上施行带有汉文的 SQL 会出迥殊的压根原因!

是以问题的根因实践上是因为迁徙不竣工导致的,只迁徙了 DB 数据,但莫得把 my.cnf 这个建立文献也竣工地拷过来!拷过来之后问题就科罚了

转头

清澈了 MySQL 编解码机制,之后再遭遇访佛的问题就相比通俗了,比如乱码,剖判便是上述顺序中的顺序发生了有损编码

本文转载自微信公众号「码海」,不错通过以下二维码热心。转载本文请相干码海公众号。