日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > 数据库 >内容正文

数据库

mysql utf8mb4 造成慢_mysql使用utf8mb4经验吐血总结

發(fā)布時(shí)間:2024/7/23 数据库 53 豆豆
生活随笔 收集整理的這篇文章主要介紹了 mysql utf8mb4 造成慢_mysql使用utf8mb4经验吐血总结 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

1. utf8 與 utf8mb4 異同

1

2

3

4

The character set named utf8 uses a maximum of three bytes per character and contains only BMP characters. The utf8mb4 character set uses a maximum of four bytes per character supports supplementary characters:

- For a BMP character, utf8 and utf8mb4 have identical storage characteristics: same code values, same encoding, same length.

- For a supplementary character, utf8 cannot store the character at all, whereas utf8mb4 requires four bytes to store it. Because utf8 cannot store the character at all, you have no supplementary characters in utf8 columns and need not worry about converting characters or losing data when upgrading utf8 data from older versions of MySQL.

MySQL在 5.5.3 之后增加了?utf8mb4?字符編碼,mb4即 most bytes 4。簡(jiǎn)單說(shuō) utf8mb4 是 utf8 的超集并完全兼容utf8,能夠用四個(gè)字節(jié)存儲(chǔ)更多的字符。

但拋開(kāi)數(shù)據(jù)庫(kù),標(biāo)準(zhǔn)的 UTF-8 字符集編碼是可以用 1~4 個(gè)字節(jié)去編碼21位字符,這幾乎包含了是世界上所有能看見(jiàn)的語(yǔ)言了。然而在MySQL里實(shí)現(xiàn)的utf8最長(zhǎng)使用3個(gè)字節(jié),也就是只支持到了 Unicode 中的?基本多文本平面(U+0000至U+FFFF),包含了控制符、拉丁文,中、日、韓等絕大多數(shù)國(guó)際字符,但并不是所有,最常見(jiàn)的就算現(xiàn)在手機(jī)端常用的表情字符 emoji和一些不常用的漢字,如 “墅” ,這些需要四個(gè)字節(jié)才能編碼出來(lái)。

注:QQ里面的內(nèi)置的表情不算,它是通過(guò)特殊映射到的一個(gè)gif圖片。一般輸入法自帶的就是。

也就是當(dāng)你的數(shù)據(jù)庫(kù)里要求能夠存入這些表情或?qū)捵址麜r(shí),可以把字段定義為 utf8mb4,同時(shí)要注意連接字符集也要設(shè)置為utf8mb4,否則在?嚴(yán)格模式?下會(huì)出現(xiàn)?Incorrect string value: /xF0/xA1/x8B/xBE/xE5/xA2… for column 'name'這樣的錯(cuò)誤,非嚴(yán)格模式下此后的數(shù)據(jù)會(huì)被截?cái)唷?/p>

提示:另外一種能夠存儲(chǔ)emoji的方式是,不關(guān)心數(shù)據(jù)庫(kù)表字符集,只要連接字符集使用 latin1,但相信我,你絕對(duì)不想這個(gè)干,一是這種字符集混用管理極不規(guī)范,二是存儲(chǔ)空間被放大(讀者可以想下為什么)。

2. utf8mb4_unicode_ci 與 utf8mb4_general_ci 如何選擇

字符除了需要存儲(chǔ),還需要排序或比較大小,涉及到與編碼字符集對(duì)應(yīng)的 排序字符集(collation)。ut8mb4對(duì)應(yīng)的排序字符集常用的有?utf8mb4_unicode_ci、utf8mb4_general_ci,到底采用哪個(gè)在 stackoverflow 上有個(gè)討論,What’s the difference between utf8_general_ci and utf8_unicode_ci

主要從排序準(zhǔn)確性和性能兩方面看:

準(zhǔn)確性

utf8mb4_unicode_ci?是基于標(biāo)準(zhǔn)的Unicode來(lái)排序和比較,能夠在各種語(yǔ)言之間精確排序

utf8mb4_general_ci?沒(méi)有實(shí)現(xiàn)Unicode排序規(guī)則,在遇到某些特殊語(yǔ)言或字符是,排序結(jié)果可能不是所期望的。

但是在絕大多數(shù)情況下,這種特殊字符的順序一定要那么精確嗎。比如Unicode把?、?當(dāng)成ss和OE來(lái)看;而general會(huì)把它們當(dāng)成s、e,再如àá??ā?各自都與?A?相等。

性能

utf8mb4_general_ci?在比較和排序的時(shí)候更快

utf8mb4_unicode_ci?在特殊情況下,Unicode排序規(guī)則為了能夠處理特殊字符的情況,實(shí)現(xiàn)了略微復(fù)雜的排序算法。

但是在絕大多數(shù)情況下,不會(huì)發(fā)生此類(lèi)復(fù)雜比較。general理論上比Unicode可能快些,但相比現(xiàn)在的CPU來(lái)說(shuō),它遠(yuǎn)遠(yuǎn)不足以成為考慮性能的因素,索引涉及、SQL設(shè)計(jì)才是。?我個(gè)人推薦是?utf8mb4_unicode_ci,將來(lái) 8.0 里也極有可能使用變?yōu)槟J(rèn)的規(guī)則。相比選擇哪一種collation,使用者應(yīng)該更關(guān)心字符集與排序規(guī)則在db里要統(tǒng)一就好。

這也從另一個(gè)角度告訴我們,不要可能產(chǎn)生亂碼的字段作為主鍵或唯一索引。我遇到過(guò)一例,以 url 來(lái)作為唯一索引,但是它記錄的有可能是亂碼,導(dǎo)致后來(lái)想把它們修復(fù)就特別麻煩。

3. 怎么從utf8轉(zhuǎn)換為utf8mb4

3.1 “偽”轉(zhuǎn)換

如果你的表定義和連接字符集都是utf8,那么直接在你的表上執(zhí)行

1

ALTER TABLE tbl_name CONVERT TO CHARACTER SET utf8mb4;

則能夠該表上所有的列的character類(lèi)型變成 utf8mb4,表定義的默認(rèn)字符集也會(huì)修改。連接的時(shí)候需要使用set names utf8mb4便可以插入四字節(jié)字符。(如果依然使用 utf8 連接,只要不出現(xiàn)四字節(jié)字符則完全沒(méi)問(wèn)題)。

上面的 convert 有兩個(gè)問(wèn)題,一是它不能ONLINE,也就是執(zhí)行之后全表禁止修改,有關(guān)這方面的討論見(jiàn)?mysql 5.6 原生Online DDL解析;二是,它可能會(huì)自動(dòng)該表字段類(lèi)型定義,如?VARCHAR 被轉(zhuǎn)成 MEDIUMTEXT,可以通過(guò) MODIFY 指定類(lèi)型為原類(lèi)型。

另外?ALTER TABLE tbl_name DEFAULT CHARACTER SET utf8mb4?這樣的語(yǔ)句就不要隨便執(zhí)行了,特別是當(dāng)表原本不是utf8時(shí),除非表是空的或者你確認(rèn)表里只有拉丁字符,否則正常和亂的就混在一起了。

最重要的是,你連接時(shí)使用的latin1字符集寫(xiě)入了歷史數(shù)據(jù),表定義是latin1或utf8,不要期望通過(guò)?ALTER ... CONVERT ...?能夠讓你達(dá)到用utf8讀取歷史中文數(shù)據(jù)的目的,沒(méi)卵用,老老實(shí)實(shí)做邏輯dump。所以我才叫它“偽”轉(zhuǎn)換

3.2 character-set-server

一旦你決定使用utf8mb4,強(qiáng)烈建議你要修改服務(wù)端?character-set-server=utf8mb4,不同的語(yǔ)言對(duì)它的處理方法不一樣,c++, php, python可以設(shè)置character-set,但java驅(qū)動(dòng)依賴(lài)于 character-set-server 選項(xiàng),后面有介紹。

同時(shí)還要謹(jǐn)慎一些特殊選項(xiàng),如?遇到騰訊云CDB連接字符集設(shè)置一個(gè)坑。個(gè)人不建議設(shè)置全局?init_connect。

4. key 768 long 錯(cuò)誤

字符集從utf8轉(zhuǎn)到utf8mb4之后,最容易引起的就是索引鍵超長(zhǎng)的問(wèn)題。

對(duì)于表行格式是?COMPACT或?REDUNDANT,InnoDB有單個(gè)索引最大字節(jié)數(shù) 768 的限制,而字段定義的是能存儲(chǔ)的字符數(shù),比如?VARCHAR(200)?代表能夠存200個(gè)漢字,索引定義是字符集類(lèi)型最大長(zhǎng)度算的,即 utf8 maxbytes=3, utf8mb4 maxbytes=4,算下來(lái)utf8和utf8mb4兩種情況的索引長(zhǎng)度分別為600 bytes和800bytes,后者超過(guò)了768,導(dǎo)致出錯(cuò):Error 1071: Specified key was too long; max key length is 767 bytes。

COMPRESSED和DYNAMIC格式不受限制,但也依然不建議索引太長(zhǎng),太浪費(fèi)空間和cpu搜索資源。

如果已有定義超過(guò)這個(gè)長(zhǎng)度的,可加上前綴索引,如果暫不能加上前綴索引(像唯一索引),可把該字段的字符集改回utf8或latin1。

但是,(?敲黑板啦,很重要),要防止出現(xiàn)?Illegal mix of collations (utf8_general_ci,IMPLICIT) and (utf8mb4_general_ci,COERCIBLE) for operation '='?錯(cuò)誤:連接字符集使用utf8mb4,但 SELECT/UPDATE where條件有utf8類(lèi)型的列,且條件右邊存在不屬于utf8字符,就會(huì)觸發(fā)該異常。表示踩過(guò)這個(gè)坑。

再多加一個(gè)友好提示:EXPLAIN 結(jié)果里面的 key_len 指的搜索索引長(zhǎng)度,單位是bytes,而且是以字符集支持的單字符最大字節(jié)數(shù)算的,這也是為什么 INDEX_LENGTH 膨脹厲害的一個(gè)原因。

5. C/C++ 內(nèi)存空間分配問(wèn)題

這是我們這邊的開(kāi)發(fā)遇到的一個(gè)棘手的問(wèn)題。C或C++連接MySQL使用的是linux系統(tǒng)上的 libmysqlclient 動(dòng)態(tài)庫(kù),程序獲取到數(shù)據(jù)之后根據(jù)自定義的一個(gè)網(wǎng)絡(luò)協(xié)議,按照mysql字段定義的固定字節(jié)數(shù)來(lái)傳輸數(shù)據(jù)。從utf8轉(zhuǎn)utf8mb4之后,c++里面針對(duì)character單字符內(nèi)存空間分配,從3個(gè)增加到4個(gè),引起異常。

這個(gè)問(wèn)題其實(shí)是想說(shuō)明,使用utf8mb4之后,官方建議盡量用 varchar 代替 char,這樣可以減少固定存儲(chǔ)空間浪費(fèi)(關(guān)于char與varchar的選擇,可參考?這里)。但開(kāi)發(fā)設(shè)計(jì)表時(shí) varchar 的大小不能隨意加大,它雖然是變長(zhǎng)的,但客戶(hù)端在定義變量來(lái)獲取數(shù)據(jù)時(shí),是以定義的為準(zhǔn),而非實(shí)際長(zhǎng)度。按需分配,避免程序使用過(guò)多的內(nèi)存。

6. java驅(qū)動(dòng)使用

Java語(yǔ)言里面所實(shí)現(xiàn)的UTF-8編碼就是支持4字節(jié)的,所以不需要配置?mb4?這樣的字眼,但如果從MySQL讀寫(xiě)emoji,MySQL驅(qū)動(dòng)版本要在 5.1.13 及以上版本,數(shù)據(jù)庫(kù)連接依然是?characterEncoding=UTF-8?。

但還沒(méi)完,遇到一個(gè)大坑。官方手冊(cè)?里還有這么一段話(huà):

1

2

3

4

Connector/J did not support utf8mb4 for servers 5.5.2 and newer.

Connector/J now auto-detects servers configured with character_set_server=utf8mb4 or treats the Java encoding utf-8 passed

using characterEncoding=... as utf8mb4 in the SET NAMES= calls it makes when establishing the connection. (Bug #54175)

意思是,java驅(qū)動(dòng)會(huì)自動(dòng)檢測(cè)服務(wù)端?character_set_server?的配置,如果為utf8mb4,驅(qū)動(dòng)在建立連接的時(shí)候設(shè)置?SET NAMES utf8mb4。然而其他語(yǔ)言沒(méi)有依賴(lài)于這樣的特性。

7. 主從復(fù)制報(bào)錯(cuò)

這個(gè)問(wèn)題沒(méi)有遇到,只是看官方文檔有提到,曾經(jīng)也看到過(guò)類(lèi)似的技術(shù)文章。

大概就是從庫(kù)的版本比主庫(kù)的版本低,導(dǎo)致有些字符集不支持;或者人工修改了從庫(kù)上的表或字段的字符集定義,都有可能引起異常。

8. join 查詢(xún)問(wèn)題

這個(gè)問(wèn)題是之前在姜承堯老師公眾號(hào)看到的一篇文章?MySQL表字段字符集不同導(dǎo)致的索引失效問(wèn)題,自己也驗(yàn)證了一下,的確會(huì)有問(wèn)題:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

CREATE TABLE t1 (

f_id varchar(20) NOT NULL,

f_action char(25) NOT NULL DEFAULT '' COMMENT '',

PRIMARY KEY (`f_id`),

) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

CREATE TABLE t1_copy_mb4 (

f_id varchar(20) CHARACTER SET utf8mb4 NOT NULL,

f_action char(25) NOT NULL DEFAULT '' COMMENT '',

PRIMARY KEY (`f_id`),

) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

1.

EXPLAIN extended select * from t1 INNER JOIN t1_copy_mb4 t2 on t1.f_id=t2.f_id where t1.f_id='421036';

2.

EXPLAIN extended select * from t1 INNER JOIN t1_copy_mb4 t2 on t1.f_id=t2.f_id where t2.f_id='421036';

對(duì)應(yīng)上面1,2 的截圖:

其中 2 的warnings 有convert:

(convert(t1.f_id?using utf8mb4) = ‘421036’)

官網(wǎng)能找到這一點(diǎn)解釋的還是開(kāi)頭那個(gè)地址:

1

2

3

4

Similarly, the following comparison in the WHERE clause works according to the collation of utf8mb4_col:

SELECT * FROM utf8_tbl, utf8mb4_tbl

WHERE utf8_tbl.utf8_col = utf8mb4_tbl.utf8mb4_col;

只是索引失效發(fā)生在utf8mb4列 在條件左邊。(關(guān)于MySQL的隱式類(lèi)型轉(zhuǎn)換,見(jiàn)這里)。

9. 參考

總結(jié)

以上是生活随笔為你收集整理的mysql utf8mb4 造成慢_mysql使用utf8mb4经验吐血总结的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。