首頁

目前文章總數:157 篇

  

最後更新:2024年 12月 07日

0001. Mysql資料庫,字符集要用 utf8mb3 還是 utf8mb4 ???

日期:2023年 09月 02日

標籤: MySQL DataBase

摘要:效能議題


  結論:用utf8mb4
問題討論:探討Mysql官方結論,有沒有必要優化資料表,區分utf8mb4與utf8mb3進行空間優化?
基本介紹:本篇分為4大部分。
第一部分:Mysql官方結論
第二部分:占用空間比較
第三部分:成本價值比較
第四部分:Unicode的發展近況






第一部分:Mysql官方結論

Step 1:官方結論重點

以下是Mysql官網utf8mb3與utf8mb4說明連結
看重點框選的地方,翻譯成中文大意就是:
Mysql官方建議使用utf8mb4,捨棄utf8mb3
並且在未來Mysql 8以後的版本預設用utf8mb4


Step 2:探討官方結論-優缺點

下表是兩者的優缺點
如果要開發多國語言、Emoji、Mysql版本大於8.0.28、未來擴展性等等,都確定用utf8mb4叫好

特性 / 屬性 utf8mb3 utf8mb4
最大字符長度 1-3 字符 1-4 字符
支援字符集 ASCII, 大部分歐洲語言 Unicode 全字符集
支援 Unicode 版本 Unicode 3.0 Unicode 全字符集
支援 Emoji 表情符號
存儲空間效率 較高 較低
國際化支援 有限制 持續支援
數據保存與互操作性 有限 更廣泛
未來擴展性 不可擴展 持續擴展
總體優勢 空間高校、官方放棄 完整字符、官方預設



Step 3:探討官方結論-提出問題

官方雖然建議用utf8mb4,但可以發現utf8mb3有空間優勢
utf8mb3 占用字符集為 1-3 字節表示一個字
utf8mb4 占用字符集為 1-4 字節表示一個字
因此我們可以判斷 utf8mb4 一定比 utf8mb3 多花空間,問題是用utf8mb3可以省多少呢?

第二部分:占用空間比較

Step 1:空間比較前言

空間使用率的比較,我們建立4張表,2種對照
對照組1:表內有30000筆[文字]資料的情況下,各自占用多少空間

資料表名 欄位特性 插入資料量
UseUtf8mb3 2個欄位,包含一個字串欄位 30000筆
UseUtf8mb4 2個欄位,包含一個字串欄位 30000筆


對照組2:表內有30000筆[沒有文字]的情況下,各自占用多少空間

資料表名 欄位特性 插入資料量
UseUtf8mb3_INT 2個欄位,無字串欄位 30000筆
UseUtf8mb4_INT 2個欄位,無字串欄位 30000筆




Step 2:有文字表比較-建表

建立以下兩張表
utf8mb3帶文字:

CREATE TABLE `UseUtf8mb3` (
  `UseUtf8mb3Id` INT NOT NULL AUTO_INCREMENT,  
  `JunkMessage` VARCHAR(1000) NOT NULL COMMENT '',
  PRIMARY KEY (`UseUtf8mb3Id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb3 COMMENT='填充訊息utf8mb3表';



utf8mb4帶文字:

CREATE TABLE `UseUtf8mb4` (
  `UseUtf8mb4Id` INT NOT NULL AUTO_INCREMENT,
  `JunkMessage` VARCHAR(1000) NOT NULL COMMENT '',
  PRIMARY KEY (`UseUtf8mb4Id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='填充訊息utf8mb4表';




Step 3:有文字表比較-塞資料代碼

以下是C#代碼,我們產生500個字的變數,重複塞入30000萬筆資料到2張表


//1. Class Model
public class useUtf8Class
{
    public string JunkMessage { get; set; }
}

//2. 用Dapper 塞資料到 Mysql
public void AddJunkMessages()
{
    var datas = new List<useUtf8Class>();
    string randomString = GenerateRandomString(500);
    //3萬筆
    for (int index = 0; index < 30000; index++)
    {              
        datas.Add(new useUtf8Class() { JunkMessage = randomString });
    }
    var sql = $@"
INSERT UseUtf8mb3(JunkMessage)
VALUE(@JunkMessage);

INSERT UseUtf8mb4(JunkMessage)
VALUE(@JunkMessage);
";
    _unitOfWork.Master.Execute(sql, datas);
}
";



Step 4:有文字表比較-資料表內容

塞入資料後 UseUtf8mb3 的內容如下:


塞入資料後 UseUtf8mb4 的內容如下:


Step 5:有文字表比較-比較空間

UseUtf8mb3 表使用空間為:8.52MB

SELECT ROUND(((data_length + index_length) / 1024 / 1024), 2) AS 'UseUtf8mb3 使用量 (MB)'       
FROM information_schema.TABLES
WHERE table_name = 'UseUtf8mb3'



UseUtf8mb4 表使用空間為:14.52MB

SELECT ROUND(((data_length + index_length) / 1024 / 1024), 2) AS 'UseUtf8mb4 使用量 (MB)'       
FROM information_schema.TABLES
WHERE table_name = 'UseUtf8mb4'




Step 6:有文字表比較-結論

如果表 “都只有字串” 的情況下,空間節省效率為 表使用空間為:8.52 / 14.52 約為58%
但實務上很多資料表的欄位大多都不會只有字串

Step 7:無文字表比較-建表

這次考量 “不用文字” 的狀況下,會有多少空間差異,文字欄位改用Int保存,建立以下兩張表
utf8mb3 無文字欄位:

CREATE TABLE `UseUtf8mb3_INT` (
  `UseUtf8mb3Id` INT NOT NULL AUTO_INCREMENT,  
  `JunkInt` INT NOT NULL COMMENT '',
  PRIMARY KEY (`UseUtf8mb3Id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb3 COMMENT='填充訊息utf8mb3只有INT表';


utf8mb4 無文字欄位:

CREATE TABLE `UseUtf8mb4_INT` (
  `UseUtf8mb4Id` INT NOT NULL AUTO_INCREMENT,
  `JunkInt` INT NOT NULL COMMENT '',
  PRIMARY KEY (`UseUtf8mb4Id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4 COMMENT='填充訊息utf8mb4只有INT表';



Step 8:無文字表比較-塞資料代碼

以下是C#代碼,我們產生數值都是1234567的變數,重複塞入30000萬筆資料


//1. Class Model
public class useUtf8Class
{
    public int JunkInt { get; set; }
}

//2. 用Dapper 塞資料到 Mysql
public void AddJunkMessages()
{
    var datas = new List<useUtf8Class>();
    //3萬筆
    for (int index = 0; index < 30000; index++)
    {              
        datas.Add(new useUtf8Class() { JunkInt = 1234567});
    }
    var sql = $@"
INSERT UseUtf8mb3_INT(JunkInt)
VALUE(@JunkInt);

INSERT UseUtf8mb4_INT(JunkInt)
VALUE(@JunkInt);
";
    _unitOfWork.Master.Execute(sql, datas);
}
";



Step 9:無文字表比較-資料表內容

塞入資料後 UseUtf8mb3_INT 的內容如下:


塞入資料後 UseUtf8mb4_INT 的內容如下:


Step 10:無文字表比較-比較空間

UseUtf8mb3_INT 表使用空間為: 3.52MB

SELECT ROUND(((data_length + index_length) / 1024 / 1024), 2) AS 'UseUtf8mb3 使用量 (MB)'       
FROM information_schema.TABLES
WHERE table_name = 'UseUtf8mb3_INT'



UseUtf8mb4_INT 表使用空間為: 3.52MB

SELECT ROUND(((data_length + index_length) / 1024 / 1024), 2) AS 'UseUtf8mb4 使用量 (MB)'       
FROM information_schema.TABLES
WHERE table_name = 'UseUtf8mb4_INT'




Step 11:無文字表比較-結論

如果表”沒有文字”的情況下,空間節省效率為 0% => 表使用空間為:3.52 / 3.52 = 1

第三部分:成本價值比較

Step 1:計算方式

因此決定能壓縮多少空間就看char、varchar等欄位,其他可以不計
假設資料庫全部都是utf8mb4,改為全都是utf8mb3可以解省的空間簡單計算如下:
使用空間值 * [(文字欄位) / 找出資料庫所有欄位] * (1 - 0.58) = 節省的空間




Step 2:示範計算 - SQL查詢

我們有一個資料庫,是專門存放報表資料的,
下達以下SQL語法,得出以下數值

項目 數量
文字欄位數 1876                   
非文字欄位數 30652
總計 32528
SELECT SUM(CASE WHEN DATA_TYPE IN ('varchar', 'char', 'text') THEN 1 ELSE 0 END) AS text_columns,
       SUM(CASE WHEN DATA_TYPE NOT IN ('varchar', 'char', 'text') THEN 1 ELSE 0 END) AS non_text_columns
  FROM information_schema.COLUMNS
 WHERE TABLE_SCHEMA = 'Report'




Step 3:示範計算 - 節省空間比例

為了便於計算我們將數值調整如下:
依照 => [(文字欄位) / 找出資料庫所有欄位]
計算出 => (2000 / 32000) => 0.0625%
這個範例資料庫如果使用utf8mb3 可以省下6%的空間

項目 數量
文字欄位數 2000                   
非文字欄位數 30000
總計 32000



Step 4:示範計算 - 1G資料節省空間

計算方式,假設資料庫使用空間為 1000MB , 在範例報表庫可以得到節省26.25MB
一個全都是utf8mb4 的報表資料庫,全部轉為utf8mb3,在1G的資料庫中我們可以省26.25MB
※計算值 1000 * 0.0625 * 0.42 = 26.25 MB

使用空間值 * [(文字欄位) / 找出資料庫所有欄位] * (1 - 0.58) = 節省的空間



Step 5:示範計算 - 硬碟價格

目前一顆SSD 1TB的硬碟為1200元,每1MB約 0.12元
因此26.5MB 價格約 3.18元
※findprice 網站,價格由低至高


Step 6:示範計算 - 勞工薪資

目前中華民國勞工薪資最低所得為176/HR NT,月薪26400元
因此如果軟體工程師時薪176,至少要能省1460MB的空間,才有將utf8mb4優化為utf8mb3的價值


Step 7:示範計算 - 價格對照

基於硬碟、人力成本來考量,如果今天公司想要將資料庫所有表utf8mb4優化為utf8mb3
如下圖(範例報表庫換算):
※最低工資時薪換算:表示請員工做這件事的成本,超過這個時間就表示賠錢了

庫總用空間量(全utf8mb4) 庫總用空間量(全utf8mb3) 節省空間 SSD空間換算價格(0.12) 最低工資時薪換算
1 GB 973.75 MB 26.25 MB 3.15 NT 1分鐘
10 GB 9.7375 GB 262.5 MB 31.5 NT 10分鐘
10 TB 9.7375 TB 2.625 GB 315 NT 100分鐘



Step 8:結論 - 請用utf8mb4

我的範例資料庫總共只用了11.18 MB左右,全部預設都utf8mb4就好了。
如果還每張表判斷是否用utf8mb3,必要的用utf8mb4節省空間,根本浪費時間,浪費金錢
優化這個庫將所有table優化為 utf8mb3 執行這項工作,超過10秒就賠錢
※除非海量數據以京為單位、或者大多只使用”文字欄位”的資料庫。
※Mysql官方的建議,已經符合未來趨勢(語系增加、空間低廉),請用utf8mb4



第四部分:Unicode的發展近況


Unicode官網
最近一次更新的版本是2022/09/13 15.0版本
大意是說:新增幾個語言、支援新的表情符號,有持續加新的碼
如果系統要走進國際化(翻譯多國語言),建議聽從官方建議使用utf8mb4,捨棄utf8mb3