MySQL字元集不一致導致性能下降25%,你敢信?

来源:https://www.cnblogs.com/dba-john/archive/2020/03/15/12497686.html
-Advertisement-
Play Games

故事是這樣的: 我在對MySQL進行性能測試時,發現CPU使用率接近100%,其中80%us, 16%sys,3%wa,iostat發現磁碟iops2000以下,avgqu-sz不超過3,%util最高70%,看來瓶頸不在磁碟IO上面,而在CPU上。sys部分使用率有點高。 於是我果斷使用perf  ...


故事是這樣的:

我在對MySQL進行性能測試時,發現CPU使用率接近100%,其中80%us, 16%sys,3%wa,iostat發現磁碟iops2000以下,avgqu-sz不超過3,%util最高70%,看來瓶頸不在磁碟IO上面,而在CPU上。sys部分使用率有點高。

於是我果斷使用perf top查看,赫然排在前面的2個,是my_ismbchar_utf8mb4和my_charpos_mb。

my_ismbchar_utf8mb4顧名思義,很明顯是與字元集相關的;my_charpos_mb暫時不清楚。

 

 

經驗告訴我,這很不正常!通常來說,消耗CPU最多的應該是數據頁相關的操作才對啊。

我快速打開MySQL internal文檔搜索,沒找到有價值的信息。

 

哦,你想要知道這個故事的前情提要?抱歉,我剛剛只說了壓測,按照國際慣例,我這就貼出環境和版本信息:

硬體:8核16GB,200GB SSD,騰訊雲虛擬機
操作系統版本:CentOS release 6.9 (Final)
MySQL版本:5.7.28-log MySQL Community Server (GPL),二進位方式安裝
MySQL參數:innodb_buffer_pool_size = 10752M
          innodb_flush_log_at_trx_commit = 1
          sync_binlog = 1
          character-set-server = utf8mb4
sysbench版本:1.0.19
sysbench參數:sysbench /usr/share/sysbench/oltp_read_write.lua   --tables=3 --table-size=1000000  --mysql-password=*** --mysql-user=root --mysql-socket=/usr/local/mysql5.7.28/mysql.sock --threads=128 --time=1800 run 

server的字元集是utf8mb4,接下來檢查一下db和表的字元集吧:

 

 

 

 

 

 嗯嗯,看起來一切都是那麼的正常……

server, DB, table的字元集都一致,現在只剩下sysbench的嫌疑最大!

可是,要怎麼檢查sysbench已經連接到MySQL的那些會話的字元集設置呢?

我的sysbench命令沒有顯式地指定字元集;show processlist沒有character_set_client信息,information_schema庫和mysql庫裡面也沒有與character_set_client信息。

sysbench --help 也沒有字元集相關的選項和參數;https://github.com/akopytov/sysbench/blob/master/src/drivers/mysql/drv_mysql.c  sysbench源碼中也沒有字元集相關的設置。

看來,sysbench連接MySQL的字元集設置,應該預設是latin1,應該是這裡的字元集設置不一致導致的。

 

BUT,對於技術問題,我不能光靠猜測啊!我一定要刨根問底,查它個水落石出……

 

 

源碼:

吃CPU最多的是my_ismbchar_utf8mb4函數對吧?那就先到源碼中搜它:

在strings/ctype-utf8.c 中定義的:

static uint
my_ismbchar_utf8mb4(const CHARSET_INFO *cs, const char *b, const char *e)
{
  int res= my_valid_mbcharlen_utf8mb4(cs, (const uchar*)b, (const uchar*)e);
  return (res > 1) ? res : 0;
}

它本身沒有複雜的邏輯,只是調用了my_valid_mbcharlen_utf8mb4,然後對返回值res 進行判斷,如果>1,就返回res,否則返回0。

行,那我再看看my_valid_mbcharlen_utf8mb4吧,

static int
my_valid_mbcharlen_utf8mb4(const CHARSET_INFO *cs __attribute__((unused)),
                           const uchar *s, const uchar *e)
{
  uchar c;

  if (s >= e)
    return MY_CS_TOOSMALL;

  c= s[0];
  if (c < 0xf0)
    return my_valid_mbcharlen_utf8mb3(s, e);

  if (c < 0xf5)
  {
    if (s + 4 > e) /* We need 4 characters */
      return MY_CS_TOOSMALL4;

    /*
省略若幹行……
    */

    if (!(IS_CONTINUATION_BYTE(s[1]) &&
          IS_CONTINUATION_BYTE(s[2]) &&
          IS_CONTINUATION_BYTE(s[3]) &&
          (c >= 0xf1 || s[1] >= 0x90) &&
          (c <= 0xf3 || s[1] <= 0x8F)))
      return MY_CS_ILSEQ;

    return 4;
  }

  return MY_CS_ILSEQ;
}

這個函數對輸入的字元進行比對,判斷是utf8mb3還是utf8mb4。utf8mb3?以前沒聽說過啊!上知乎一搜,原來還有這麼一段有趣的歷史 ☜

不過,僅僅看這個函數的代碼,是不會相信它居然會吃掉7%以上的CPU的。我也不信!

好吧,先做個perf record看看:

#第1步,查看mysqld進程的pid
ps -ef | grep mysqld 
#第2步,將mysqld進程相關的cpu
-clock事件及調用堆棧記錄起來,預設保存在perf.data文件中 perf record -e cpu-clock -g -p 14345
#第3步,用perf script工具對perf.data進行解析 perf script
-i perf.data &> perf.unfold
#第4步,下載一個集漂亮、強大於一身的工具: git clone https:
//github.com/brendangregg/FlameGraph.git

#第5步:將perf.unfold中的符號進行摺疊 ./FlameGraph/stackcollapse-perf.pl perf.unfold &> perf.folded
#第6步,生成火焰圖 .
/FlameGraph/flamegraph.pl perf.folded > perf.svg

效果就是這樣的↓  可以看出,my_ismbchar_utf8mb4占比確實最高,達到了7.47%

 

 

 

去跟蹤調用堆棧,可以發現是在sql\sql_lex.cc中的get_text()函數中,調用了巨集use_mb和my_ismbchar來檢查字元集。

這2個巨集同樣都是調用ismbchar() - detects whether the given string is a multi-byte sequence。   utf8mb4中的mb,全稱就是multi-byte

static char *get_text(Lex_input_stream *lip, int pre_skip, int post_skip)
{
  uchar c,sep;
  uint found_escape=0;
  const CHARSET_INFO *cs= lip->m_thd->charset();

  lip->tok_bitmap= 0;
  sep= lip->yyGetLast();                        // String should end with this
  while (! lip->eof())
  {
    c= lip->yyGet();
    lip->tok_bitmap|= c;
    {
      int l;
      if (use_mb(cs) &&
          (l = my_ismbchar(cs,
                           lip->get_ptr() -1,
                           lip->get_end_of_query()))) {
        lip->skip_binary(l-1);
        continue;
      }
    }
    if (c == '\\' &&
        !(lip->m_thd->variables.sql_mode & MODE_NO_BACKSLASH_ESCAPES))
    {                    // Escaped character
      found_escape=1;
      if (lip->eof())
    return 0;
      lip->yySkip();
    }
// 省略若幹行……
  }
  return 0;                    // unexpected end of query
}

 

 

 解決方法:

上面說了一大通,可能有點雲里霧裡,抱歉哈,我能力有限,不能把它解釋得更通俗一些。

簡而言之,就是證明瞭確實是字元集不一致,導致MySQL在語法解析的時候,對每一個用戶輸入的字元(MySQL關鍵字除外),都要進行若幹次字元集檢查,所以才會發生my_ismbchar_utf8mb4吃掉很多CPU資源這樣一個故事 。

要解決就很簡單啦:保持character_set_server  &&  database characterset  &&  table characterset  &&  Client characterset一致!

我就是因為忽略了sysbench的字元集設置,所以才把自己給坑了。

既然sysbench沒有提供字元集相關的選項和參數,那我就把MySQL的字元集統一成latin1來測吧(也可以去修改sysbench的mysql driver源碼,讓它支持設置字元集,但是我不擅長C……)

 

 

最後總結:

調整字元集之前,QPS最高只能壓到73797,統一字元集之後,QPS達到了98272。  73797/98272*100%=75.09%

 

再來看看TPS,調整字元集之前,TPS最高只能壓到3689,統一字元集之後,TPS達到了3689。  73797/4913*100%=75.08%

 

 

 多麼痛的領悟……

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 背景 最近公司系統還原用戶時偶爾會出現部分用戶信息未還原成功的問題,最為開發人員,最頭疼的不是代碼存在bug,而是測試發現了bug,但一旦我去重現,它就不見了。Are you kidding me? 經過漫長的溝通與嘗試,終於發現了端倪,這個問題只有在多人同時操作修改同一用戶信息時才會出現。 哦,那 ...
  • 介紹MySQL Integer類型的幾種分類,以及INT(11)中11數字表達的意思 ...
  • MySQL可以運行在不同的模式下,而且可以在不同的場景下運行不同的模式,這主要取決於系統變數 sql_mode 的值。本文主要介紹一下這個值的查看與設置,主要在Mac系統下。 對於每個模式的意義和作用,網上很容易找到,本文不做介紹。 按作用區域和時間可分為3個級別,分別是會話級別,全局級別,配置(永 ...
  • 在業界廣泛流傳著一句話:數據和特征決定了機器學習的上限,而模型和演算法只是逼近這個上限而已。 由此可見,數據和特征是多麼的重要,而在數據大多數場景下,數據已經就緒,不同人對於同樣的數據處理得到的特征卻千差萬別,最終得到的建模效果也是高低立現。從數據到特征這就要從特征工程說起了... ...
  • 我的表結構 student_info | id |name |profession|score| | | | | | |id|姓名|分數|專業| 按分數段統計 400到500人數,300到400人數 按分數段和專業統計 400到500人數,300到400人數 sql動態拼接生成 輸出sql ...
  • 現在有兩張表,第一張表有學生姓名,語文,數學,英語成績,數據量46萬。第二張表有學生姓名,物理,化學,政治成績,數據量10萬。 1. 找出既參加了table1,也參加了table2的考試的學生的數學和物理成績: SELECT achievement_1.name, achievement_1.mat ...
  • 函數 單行函數:對一行數據進行操作的函數,如字元串函數、數值函數、轉換函數、日期函數等。 聚合函數:同時對多行數據進行操作,如求和函數等。 字元串函數 | 函數 | 說明 | | | | | ASCII(X) | 求字元X的ASCII碼 | | CHR(X) | 求ASCII碼對應的字元 | | L ...
  • 集合運算 INTERSECT(交集),返回兩個查詢共有的記錄。 UNION ALL(並集重覆),返回各個查詢的所有記錄,包括重覆記錄。 UNION(並集不重覆),返回各個查詢的所有記錄,不包括重覆記錄 。 MINUS(補集),返回第一個查詢的記錄減去第二個查詢的記錄之後剩餘的記錄。 內連接 兩張表通 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...