MySQL字符集帶來的一點存儲影響,你知道多少?
點擊上方“程序員大白”,選擇“星標”公眾號
重磅干貨,第一時間送達
前言
從 Mysql 數(shù)據(jù)庫角度來說,談到存儲就一定離不開字符集,只不過在我們?nèi)粘i_發(fā)中統(tǒng)一的 utf8/utf8mb4 編碼,使我們常常忽略了字符集的影響,本文僅從字符集的角度來談談對 InnoDB 的存儲設計的一點影響,以及 Mysql 是怎么兼容各種字符集的。
過一下字符集
Unicode 作為現(xiàn)在通用的字符集,通常采用兩個字節(jié)表示一個字符,帶來的副作用就是,原本采用 ASCII 字符集只需要一個字節(jié)的,卻變成了 2 個字節(jié),造成了空間浪費,而 UTF-8 編碼規(guī)則,將 Unicode 編碼成 1~4 個字節(jié),ASCII 字符集繼續(xù)保持了 1 個字節(jié)空間,而中文編碼成了三個字節(jié),如下圖。

對存儲帶來了什么影響
先說明下 Mysql 中存在兩種字符集 utf8 和 utf8mb4,以下例子均以 Mysql 的 utf8(1~3個字節(jié))為例。
采用 utf8 編碼的確很不錯,但是也帶來了一個問題,例如我在 Mysql 中定義了一個定長字符類型 char(5):

所謂定長字符類型代表我要給 title 分配 5 個字符大小的空間,可是 utf8 每個字符可能是 1~3 個字節(jié),我該分配多少空間合適呢?
理論上為了兼容,最好應該采用 utf8 的最大 3 個字節(jié)進行分配,也就是 5*3 = 15 個字節(jié)的空間,這樣我不管以后怎么修改這個字段的值,空間都能完美滿足需求,但是如果此時存儲的都是英文,比如 5 個 I,就會足足浪費 10 個字節(jié)的空間,如果這列以后都存英文,那么至少會浪費 2/3 的空間。

在 Mysql5.0 之前的行格式設計中,也就是 Redundant 行格式,char(5) 的確就如上面設計占用了 15 個字節(jié)空間,隨著版本的迭代,后續(xù)出來的 Compact,Dynamic,Compressed 行格式都采用了另一種設計。
在對于 utf8 這類變長編碼規(guī)則的 char 類型,采用同 varchar 類型一樣的存儲方式,就是在前面用一個或兩個字節(jié)表示該列實際占用的字節(jié)數(shù),對應到上圖存儲 5 個 I 的例子,就是 05(實際占用字節(jié)數(shù))+5 個存儲 I 的字節(jié)空間。
當然,更極端點,我只存儲了一個 I,這時 char(5) 就會使用 utf8 的最小字節(jié)數(shù) 1*5(char定義的字符長度)的大小作為最小分配空間,空出的 4 個字節(jié)空間用空格字符填充,也就是說,對于 title 來說,至少會分配 5 個字節(jié)空間。

上面只是對列為 char(5) 的數(shù)據(jù)進行說明,在真實數(shù)據(jù)庫表中,會存在多列 varchar 或 char 類型,由上可知變長編碼規(guī)則的 char 也是按 varchar 處理的,所以這些列的實際占用字節(jié)數(shù)都會逆序存放在行格式首部,被稱為變長字段長度列表,而每列的數(shù)據(jù),則是順序存放在列值中,如下圖,至于變長字段長度列表和 Null 值列表為什么是逆序的,大家有興趣可以去想想。

帶來的更新問題
采用上面的設計,在大部分情況下能省下了很多空間,也提升了查詢效率,但是也帶來了另一個問題,那就是更新,用兩個例子說明下:
將 title 從 1個 I 更新為 5 個 I
這個很好處理,因為 char(5) 最低會分配 5 個字節(jié)空間,更改為 5 個 I,不會產(chǎn)生任何影響,直接更新就好。
將 title 從 5 個 I 更改為 5 個我
5 個我 = 5 * 3 = 15 個字節(jié)空間,而實際記錄只有 5 個字節(jié)空間,空間不足以支撐更新,這時候的更新只能將原數(shù)據(jù)的整行記錄刪除,然后再新分配合適空間供其使用,看似也沒什么,但是這種刪 + 增實際會對頁產(chǎn)生很多變更,這么多變更又要保證它的事務性,也就是記錄 redo , undo 日志,還是挺復雜和麻煩的。
Mysql的字符集轉換機制
一個請求從客戶端到 Mysql 服務器,再到表,再返回給客戶端,中間是經(jīng)過多層字符集轉換的,主要包括下面4層:

假設我們查詢 title 列,并且 Mysql 各種變量以及列字符集采用上面表格的例子,那么轉換如下:
select?title?from?title_demo?where?title?=?'IIIIII'

當然,實際開發(fā)中,我們都會統(tǒng)一均采用 utf8 ,這樣就有效避免了各層字符集轉換帶來的性能影響。
總結
隨著 Mysql 性能的提升,其代碼實現(xiàn)復雜度也顯著提升,為了性能,對一種場景進行區(qū)分各種情況,再對各種情況進行不同的優(yōu)化處理,已經(jīng)不再陌生,所以面對這么多復雜的實現(xiàn),從一個小的切入點,發(fā)現(xiàn)其樂趣,也是挺有意思的。
來源:blog.51cto.com/u_14230003/2476815
推薦閱讀
關于程序員大白
程序員大白是一群哈工大,東北大學,西湖大學和上海交通大學的碩士博士運營維護的號,大家樂于分享高質量文章,喜歡總結知識,歡迎關注[程序員大白],大家一起學習進步!


