成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

MySQL 編碼不一致竟導致無法命中數據?

數據庫 MySQL
由于組內技術棧轉型,需要使用 Java 重構一個 NodeJS 編寫的業務后臺模塊,模塊包含一個根據名稱模糊查詢觸點標簽的功能,這是一個非常普通的 CRUD 操作,但讓人百思不得其解的是模糊查詢并沒有把數據查出來。

[[402050]]

本文轉載自微信公眾號「碼農私房話」,作者Liew。轉載本文請聯系碼農私房話公眾號。

由于組內技術棧轉型,需要使用 Java 重構一個 NodeJS 編寫的業務后臺模塊,模塊包含一個根據名稱模糊查詢觸點標簽的功能,這是一個非常普通的 CRUD 操作,但讓人百思不得其解的是模糊查詢并沒有把數據查出來。

項目使用的是 MySQL 數據庫,配置的編碼是 utf8,具體表結構語句如下:

  1. CREATE TABLE `t_touch_label` ( 
  2.   `id` int(11) unsigned NOT NULL AUTO_INCREMENT, 
  3.   `label_name` varchar(100) DEFAULT NULL COMMENT '標簽名'
  4.   `state` tinyint(1) DEFAULT '1' COMMENT '是否啟用'
  5.   `merchant_id` int(11) NOT NULL COMMENT '用戶ID'
  6.   `remark` int(11) DEFAULT NULL COMMENT '備注' 
  7.   PRIMARY KEY (`id`) 
  8. ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 

當客戶端與 MySQL 服務器編碼一致時,執行下面語句是能正常查詢到數據的。

  1. select id,label_name from t_touch_label where merchant_id=10086 and label_name like %B輪標簽%; 

但如果未在數據庫 jdbc-url 配置中指定編碼 characterEncoding=utf8 就會導致 like 模糊查詢無法命中數據,這是因為在客戶端操作數據的編碼與 MySQL Server 存儲引擎使用的編碼格式不一致導致的。

問題復現

首先,通過日志定位有問題的 SQL:

  1. select id,label_name from t_touch_label where merchant_id=10086 and label_name like %B輪標簽%; 

并在測試庫上驗證 SQL 是否正常查詢、過濾數據,令我震驚的是一切正常。

接下來本地查看 MyBatis 生成的 SQL 日志,確實查詢出來的是 0 條數據,而且代碼也正常運行沒有報錯,奇了個怪。

于是我產生了一個大膽的猜測:可能是中文參數導致的問題,我把參數改為英文字符會不會就正常呢?

果不其然,當參數輸入為英文字符時,一切都變得如此正常了,同時也驗證了我那“大膽的猜測”是正確的。

按照這個思路,我檢查了 MySQL 服務端:

  • character_set_client:客戶端使用的字符集,可通過 characterEncoding 配置,客戶端未配置 characterEncoding 時,會自動檢測并使用 MySQL 服務器的 character_set_server 變量值。
  • character_set_results:將查詢結果返回給客戶端使用的字符集,包括結果數據(例如列值),結果元數據(例如列名)和錯誤消息。
  • character_set_system:MySQL 服務器存儲元數據的字符集,始終為 utf8。
  • character_sets_dir:字符集的安裝目錄。
  • character_set_connection:該字符集用于不帶字符集描述的常量及用于數字到字符串的轉換,
  • character_set_server:服務器默認字符集編碼。
  • character_set_database:默認數據庫使用的字符集,每當默認數據庫更改時,服務器都會設置此變量,如果沒有默認數據庫,則該變量的值與 character_set_server 相同。
  • character_set_filesystem:文件系統字符集編碼,主要用于解析文件名稱的字符串字面值,如 load data 、load file 以及 select into outfile 等語句。在打開文件之前,文件名稱會從 character_set_client 設定編碼轉換為 character_set_filesystem 指定的編碼,默認值為 binary 二進制,即不進行任何轉換。

在項目中,配置的 MySQL 數據庫連接池:

觀察上面 jdbc-url 項的配置不難發現,在 url 中是沒有配置 characterEncoding 字符集編碼。

于是在數據庫的 jdbc-url 中添加配置 characterEncoding=utf8 后再次使用中文模糊查詢就恢復正常。

由此可見,MySQL 使用 like 模糊查詢時,無法命中數據的問題是由于 MySQL 服務端與客戶端字符集編碼集不一致導致的。

編碼不一致,無法命中數據?

MySQL 在存儲數據與查詢數據時,對數據編解碼流程如下:

  • MySQL Client 根據 jdbc-url 中設定的 characterEncoding 字符編碼(沒有配置則使用 MySQL Server 配置的 character_set_server )轉換成二進制流,并傳輸到 MySQL Server。
  • MySQL Server 收到請求時將請求數據 data 從 character_set_client 轉換為 character_set_connection。
  • 在內部操作數據前將請求數據從 character_set_connection 轉換為內部操作的字符集,其編碼確定順序:
    • 使用表字段的 character set 值。
    • 當上述值不存在,則使用數據表的 default character set 值。
    • 若上述值不存在,則使用數據庫的 default character set 值。
    • 若上述值不存在,則使用 character_set_server 值。
  • 引擎層讀寫存儲文件,涉及內部操作字符集與二進制流之間的相互轉換;
  • 將操作結果從內部操作字符集轉換為 character_set_results 。
  • MySQL Client 接收到數據后,根據本地配置的字符編碼 characterEncoding 渲染查詢結果。

數據文件到存儲引擎的編解碼:執行 select left(name,2) from table 語句時,存儲引擎加載數據文件時讀入的 name 值是 E4B8ADE69687,而 left(name,2) 操作需要對內容進行分詞處理:

  • 如果按照GBK 編碼,該值則分割成E4B8、ADE6、9687 三個字,并返回客戶端的值是 E4B8ADE6;
  • 如果按照 UTF8 編碼,就會分割成E4B8AD、E69687,返回客戶端為 E4B8ADE69687 兩個字。

由此可見,從數據文件讀入數據后,如果不進行編解碼,存儲引擎內部是無法進行字符維度的操作。

MySQL 存取數據亂碼

除了上述編碼不一致導致無法命中數據外,還可能引起存取數據亂碼的問題,例如向數據表字符集為 utf8 插入 utf8 編碼的數據,查詢時設置連接字符集為 utf8。

另外在 MySQL Server 的字符集配置中,character_set_client、character_set_results、character_set_connection 等變量的默認值均為 latin1。

插入操作的數據將經過 latin1 -> latin1 -> utf8 的字符集轉換流程,過程中每個漢字會從原始的 3 個字節變成 6 個字節存儲。

查詢的數據將經過 utf8 -> utf8 的字符集轉換,將保存的 6 個字節返回,產生亂碼。

當單個流程中編碼不一致,且兩個字符集之間無法進行無損編碼轉換,也會出現亂碼。

例如 MySQL Client 使用的編碼是 utf8,而 MySQL Server 的 character_set_client 為 gbk,表的字符集為 utf8,則一定出現亂碼。

客戶端的字符編碼和最終表的字符編碼格式雖然不同,但是只要保證存儲和查詢兩個操作的字符集編碼一致且能無損編碼轉換時,就不會產生亂碼的問題。

避免編碼不一致的措施

1、在建立數據庫、表結構或者數據庫操作時,應盡量顯式指定使用的字符集。而 character_set_client、character_set_result、character_set_connection 等變量值與庫表字段字符集定義相同,不依賴于MySQL 的默認設置,否則升級 MySQL 時可能帶來很大困擾。

2、當數據庫和連接字符集都使用 latin1 時,大部分情況下可以解決亂碼問題,但缺點是無法以字符為單位來進行 SQL 操作。一般情況下將數據庫和連接字符集都置為 utf8 ,可避免出現編碼問題。

3、my.cnf 文件中的 default_character_set 設置只影響命令連接服務器時的連接字符集。

4、對字段進行的 SQL 函數操作時,通常都是以內部操作字符集進行的,不受連接字符集設置的影響。

5、SQL 語句中的字符串會受到連接字符集或 introducer (即在 SQL 中對查詢列直接指定字符集)設置的影響,因此對比較的操作可能產生不同的結果。

 

責任編輯:武曉燕 來源: 碼農私房話
相關推薦

2010-06-02 10:53:28

MySQL版本

2024-05-11 07:37:43

數據Redis策略

2025-04-03 09:51:37

2017-06-20 09:42:52

網絡安全法數據隱私法網絡安全

2013-12-13 14:46:55

OSPFMTU鄰接關系

2022-03-16 15:54:52

MySQL數據format

2018-07-15 08:18:44

緩存數據庫數據

2023-02-24 08:19:59

MySQL索引失效

2024-04-07 09:00:00

MySQL

2022-03-18 10:53:49

數據系統架構

2018-07-08 07:38:28

數據庫緩存數據

2020-07-20 14:06:38

數據庫主從同步服務

2023-09-15 10:29:32

Java接口

2013-03-29 11:16:17

2023-09-13 13:05:01

Java項目

2024-11-18 08:00:00

數據倉庫通用語義層商業智能

2021-04-18 15:01:56

緩存系統數據

2021-01-19 10:39:03

Redis緩存數據

2017-08-25 17:59:41

浮點運算C語言

2023-12-22 10:19:19

數據庫鎖機制
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 亚洲精品久久久久国产 | 91se在线 | 99精品免费视频 | 日本啊v在线 | 久久午夜视频 | 国产一区二区日韩 | 伊人国产精品 | 国产高清在线精品一区二区三区 | 色噜噜狠狠色综合中国 | 日韩欧美在线不卡 | 91在线网站 | 国产一区二区欧美 | 91热在线| 久久99精品国产 | 免费av在线网站 | 日韩另类视频 | 91亚洲精品在线 | 日韩视频中文字幕 | 97av视频在线观看 | 久久综合欧美 | 中文字幕日韩欧美一区二区三区 | 久久久涩| 久久国产精品网站 | 欧美 日韩 在线播放 | 婷婷二区| 一级电影免费看 | 成人精品一区二区三区 | 国产福利视频在线观看 | 国产亚洲久 | 蜜臀久久 | 国产精品日日做人人爱 | 国产在线第一页 | 国产高清视频一区 | 久久九| 亚洲天堂av网 | 国产精品欧美一区二区三区不卡 | 成人免费小视频 | 中文字幕一区在线观看视频 | 亚洲一区视频在线 | 91一区| 国产xxx在线观看 |