tcmalloc解決MySQLd實例引發的cpu過高問題-愛可生

tcmalloc解決MySQLd實例引發的cpu過高問題-愛可生

作者簡介

任坤,現居珠海,先後擔任專職 Oracle 和 MySQL DBA,現在主要負責 MySQL、mongoDB 和 Redis 維護工作。


背景

MySQL 版本:5.6.29,普通主從

OS:CentOS 6.8

最近一段時間線上某實例頻繁報警CPU飆高,每次都捕獲到同一種 SQL,結構如下:

<code>select uid from test_history where cat_id = '99999' and create_time >= '2019-07-12 19:00:00.000' and uid in (......)/<code>

其中uid一次性會傳入上百個。

表結構為

<code>Create Table: CREATE TABLE `test_history` (  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵ID',  `cat_id` varchar(64) NOT NULL,  `uid` varchar(128) NOT NULL,  `msg` varchar(64) NOT NULL',  `create_time` datetime NOT NULL COMMENT '創建時間',  PRIMARY KEY (`id`),  UNIQUE KEY `idx_cat_uid` (`cat_id`,`uid`,`create_time`),  KEY `idx__time` (`create_time`)) ENGINE=InnoDB AUTO_INCREMENT=***** DEFAULT CHARSET=utf8/<code>

SQL 使用到了索引idx_msg_uid_time,單條執行可以秒級完成,但是併發執行會遭遇執行時間過長(超過1個小時)且CPU過高的問題。


診斷思路

mpstat -P ALL 1,查看cpu使用情況,主要消耗在sys即os系統調用上

tcmalloc解決MySQLd實例引發的cpu過高問題-愛可生

perf top,cpu主要消耗在_spin_lock

tcmalloc解決MySQLd實例引發的cpu過高問題-愛可生

生成perf report查看詳細情況

tcmalloc解決MySQLd實例引發的cpu過高問題-愛可生

CPU主要消耗在mutex爭用上,說明有鎖熱點。

採用pt-pmp跟蹤mysqld執行情況,熱點主要集中在mem_heap_alloc和mem_heap_free上。

tcmalloc解決MySQLd實例引發的cpu過高問題-愛可生

Pstack提供更詳細的API調用棧

<code>#0 0x0000003e0caf80cf in __lll_unlock_wake_private () from /lib64/libc.so.6#1 0x0000003e0ca7cf6a in _L_unlock_5936 () from /lib64/libc.so.6#2 0x0000003e0ca78bbc in _int_free () from /lib64/libc.so.6#3 0x000000000097dcb3 in mem_area_free(void*, mem_pool_t*) ()#4 0x000000000097d2d2 in mem_heap_block_free(mem_block_info_t*, mem_block_info_t*) ()#5 0x00000000009e6474 in row_vers_build_for_consistent_read(unsigned char const*, mtr_t*, dict_index_t*, unsigned long**, read_view_t*, mem_block_info_t**, mem_block_info_t*, unsigned char**) ()#6 0x00000000009dce75 in row_search_for_mysql(unsigned char*, unsigned long, row_prebuilt_t*, unsigned long, unsigned long) ()#7 0x0000000000939c95 in ha_innobase::index_read(unsigned char*, unsigned char const*, unsigned int, ha_rkey_function) ()/<code>

Innodb在讀取數據記錄時的API路徑為

<code>row_search_for_mysql --》row_vers_build_for_consistent_read --》mem_heap_create_block_func --》mem_area_alloc --》malloc --》  _L_unlock_10151 --》__lll_unlock_wait_private/<code>


row_vers_build_for_consistent_read會陷入一個死循環,跳出條件是該條記錄不需要快照讀或者已經從undo中找出對應的快照版本,每次循環都會調用mem_heap_alloc/free。

而該表的記錄更改很頻繁,導致其undo history list比較長,搜索快照版本的代價更大,就會頻繁的申請和釋放堆內存。

Linux原生的內存庫函數為ptmalloc,malloc/free調用過多時很容易產生鎖熱點。

當多條 SQL 併發執行時,會最終觸發os層面的spinlock,導致上述情形。


解決方案

將mysqld的內存庫函數替換成tcmalloc,相比ptmalloc,tcmalloc可以更好的支持高併發調用。

修改my.cnf,添加如下參數並重啟

<code>[mysqld_safe]malloc-lib=tcmalloc/<code>

上週五早上7點執行的操作,到現在超過72小時,期間該實例沒有再出現cpu長期飆高的情形。

以下是修改前後cpu使用率對比

tcmalloc解決MySQLd實例引發的cpu過高問題-愛可生

tcmalloc解決MySQLd實例引發的cpu過高問題-愛可生


分享到:


相關文章: