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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

【MySQL】时区设置引发的卡顿

發(fā)布時間:2024/8/23 数据库 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【MySQL】时区设置引发的卡顿 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

作者:田杰
查詢執(zhí)行時間長引發(fā)應用感知 “卡頓” 的場景在數(shù)據(jù)庫的日常支持和使用中并不少見,但由于時區(qū)設置引發(fā)的 SQL 執(zhí)行“卡頓”仍然是一個有趣的現(xiàn)象,之前沒有具體關注過。
這次客戶的細致與堅持讓我們找到了問題的源頭。

1. 名詞解釋

序列號名詞說明
1CPU 使用率非空閑的 CPU 時間占比。
2User CPU 使用率用戶空間(user-space)應用代碼消耗的 CPU 時間占比。
3Sys CPU 使用率系統(tǒng)空間(sys-space)內(nèi)核代碼消耗 CPU 時間占比。
4FutexLinux 內(nèi)核提供的快速用戶態(tài)鎖/信號量;在無競爭場景完全在用戶空間中運行,但在存在競爭場景會引發(fā)系統(tǒng)調(diào)用。

2. 問題現(xiàn)象

客戶 MySQL 8.0 實例在 2020-03-19 22:03 ~ 22:04 出現(xiàn)大量活躍連接堆積,慢日志中出現(xiàn)大量低成本查詢,并且 CPU 使用率不高但系統(tǒng) SYS CPU 使用率出現(xiàn)異常波動。

3. 問題排查

3.1 OS 層面

我們來考慮一下有哪些因素可能會導致卡頓:
? 物理機 OS 層面波動(通過 IO_WAIT 指標排除)。
? MySQL 自身機制。

3.2 MySQL 層面

排除掉 OS 層面異常類因素,我們開始聚焦在 mysqld 進程調(diào)用棧的分析。
為了更好的分析 MySQL 的行為,阿里數(shù)據(jù)庫提供了扁鵲系統(tǒng)來跟蹤、統(tǒng)計和展示確定時間內(nèi)的進程內(nèi)部方法調(diào)用情況。

我們分析上圖可以看到 40.5% 的 CPU 時間消耗在 Time_zone_system::gmt_sec_to_TIME() 方法的調(diào)用上,就是以下這一段的代碼。

void Time_zone_system::gmt_sec_to_TIME(MYSQL_TIME *tmp, my_time_t t) const {struct tm tmp_tm;time_t tmp_t = (time_t)t;localtime_r(&tmp_t, &tmp_tm);localtime_to_TIME(tmp, &tmp_tm);tmp->time_type = MYSQL_TIMESTAMP_DATETIME;adjust_leap_second(tmp);}

仔細閱讀這段代碼會發(fā)現(xiàn) localtime_to_TIME() 和 adjust_leap_second() 都是簡單的格式轉(zhuǎn)換和計算,并不涉及系統(tǒng)調(diào)用。
而 localtime_r() 會涉及到 glibc 中的 __localtime_r() 方法,代碼如下

/* Return the `struct tm' representation of *T in local time,using *TP to store the result. */struct tm *__localtime_r (t, tp)const time_t *t;struct tm *tp;{return __tz_convert (t, 1, tp);}weak_alias (__localtime_r, localtime_r)

我們繼續(xù)下鉆來看一下 __tz_convert() 的實現(xiàn),代碼如下

/* Return the `struct tm' representation of *TIMER in the local timezone.Use local time if USE_LOCALTIME is nonzero, UTC otherwise. */struct tm *__tz_convert (const time_t *timer, int use_localtime, struct tm *tp){long int leap_correction;int leap_extra_secs;if (timer == NULL){__set_errno (EINVAL);return NULL;} __libc_lock_lock (tzset_lock); /* Update internal database according to current TZ setting.POSIX.1 8.3.7.2 says that localtime_r is not required to set tzname.This is a good idea since this allows at least a bit more parallelism. */ tzset_internal (tp == &_tmbuf && use_localtime, 1); if (__use_tzfile)__tzfile_compute (*timer, use_localtime, &leap_correction,&leap_extra_secs, tp); else{if (! __offtime (timer, 0, tp)) tp = NULL;else __tz_compute (*timer, tp, use_localtime);leap_correction = 0L;leap_extra_secs = 0;} if (tp){if (! use_localtime) {tp->tm_isdst = 0;tp->tm_zone = "GMT";tp->tm_gmtoff = 0L; }if (__offtime (timer, tp->tm_gmtoff - leap_correction, tp))tp->tm_sec += leap_extra_secs;else tp = NULL;} __libc_lock_unlock (tzset_lock); return tp; }

注意到 代碼中有 加鎖 和 解鎖 的操作出現(xiàn),那么現(xiàn)在我們來看一下 __libc_lock_lock() 的定義,代碼如下

#if IS_IN (libc) || IS_IN (libpthread)# ifndef __libc_lock_lock# define __libc_lock_lock(NAME) \({ lll_lock (NAME, LLL_PRIVATE); 0; })# endif#else# undef __libc_lock_lock# define __libc_lock_lock(NAME) \__libc_maybe_call (__pthread_mutex_lock, (&(NAME)), 0)#endif

繼續(xù)追溯 lll_lock() 的實現(xiàn),代碼如下

static inline void __attribute__ ((always_inline)) __lll_lock (int *futex, int private) {int val = atomic_compare_and_exchange_val_24_acq (futex, 1, 0);if (__glibc_unlikely (val != 0)){if (__builtin_constant_p (private) && private == LLL_PRIVATE)__lll_lock_wait_private (futex);else__lll_lock_wait (futex, private);} } #define lll_lock(futex, private) __lll_lock (&(futex), private)

可以看到代碼中使用 atomic_compare_and_exchange_val_24_acq() 嘗試對?futex?加鎖。
而?futex?作為多個 thread 間共享的一塊內(nèi)存區(qū)域在多個 client thread(多個會話/查詢)競爭的場景下會引發(fā)系統(tǒng)調(diào)用而進入系統(tǒng)態(tài),導致 SYS 系統(tǒng)態(tài) CPU 使用率上升。
并且該臨界區(qū)保護的鎖機制限制了時區(qū)轉(zhuǎn)換方法?__tz_convert() 的并發(fā)度,進而出現(xiàn)多個會話/查詢 等待獲取鎖進入臨界區(qū)的情況,當沖突爭搶激烈的場景下引發(fā)卡頓
那么是什么引發(fā)的
Time_zone_system::gmt_sec_to_TIME()?調(diào)用呢,追溯下?Field_timestampf::get_date_internal()?方法,代碼如下

bool Field_timestampf::get_date_internal(MYSQL_TIME *ltime) {THD *thd = table ? table->in_use : current_thd;struct timeval tm;my_timestamp_from_binary(&tm, ptr, dec);if (tm.tv_sec == 0) return true;thd->time_zone()->gmt_sec_to_TIME(ltime, tm);return false; }

該方法中調(diào)用了基類 Time_zone?虛函數(shù) gmt_sec_to_TIME()?來進行帶時區(qū)的秒到時間格式的轉(zhuǎn)換,結(jié)合 Field_timestampf::get_date_internal() 的名稱能夠推斷出查詢中應該涉及了?timestamp?數(shù)據(jù)類型的訪問。
基于上面的推測我們驗證下卡頓的查詢和其數(shù)據(jù)類型

# 慢查詢 SELECT id, ......create_time, update_time, ...... FROM mytab WHERE duid IN (?,?,?,?,? ) and (state in (2, 3) or ptype !=0) # 查詢涉及的表 CREATE TABLE `mytab` (`id` int(11) unsigned NOT NULL AUTO_INCREMENT,`duid` char(32) NOT NULL,......`state` tinyint(2) unsigned NOT NULL DEFAULT '0',`ptype` tinyint(4) NOT NULL DEFAULT '0',`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,`update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,......,PRIMARY KEY (`id`), ) ENGINE=InnoDB

從上面的信息能夠看到?create_time?和?update_time?字段都是?timestamp?數(shù)據(jù)類型,驗證了之前的猜測。

4. 問題解決

在上面分析的基礎上可以看到調(diào)用?Time_zone_system::gmt_sec_to_TIME()?引入的 OS 層面?futex?鎖競爭導致了低成本查詢執(zhí)行卡頓。
為了規(guī)避調(diào)用該方法,可以在實例控制臺將 time_zone 參數(shù)值由?system?調(diào)整為當?shù)貢r區(qū),比如中國東 8 區(qū)時區(qū)?'+8:00'
修改后,會調(diào)用?Time_zone_offset::gmt_sec_to_TIME()?來直接在 MySQL 層面進行計算,避免訪問?glibc?的函數(shù)引發(fā) OS 層面的加解鎖。
修改效果對比(對比執(zhí)行同樣次數(shù)的 timestamp 數(shù)據(jù)類型查詢完成時間)
time_zone='system',需要約 15 分鐘 完成

time_zone='+8:00',需要約 5 分鐘 完成

5. 最佳實踐

高并發(fā)應用如果涉及到高頻次的 timestamp?類型數(shù)據(jù)訪問:
? 如果確實要使用?timestamp?類型,建議控制臺設置?time_zone?參數(shù)為?UTC/GMT 偏移量格式,比如 東8區(qū)?'+8:00',可以有效降低高并發(fā)查詢執(zhí)行開銷,降低響應時間 RT。
? 由于 MySQL 5.7 版本后?Datatime?類型支持?Timestamp?類型的默認值并且支持?on update current_timestamp?屬性,建議使用?Datetime?類型替換?Timestamp?類型。

原文鏈接
本文為云棲社區(qū)原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。

總結(jié)

以上是生活随笔為你收集整理的【MySQL】时区设置引发的卡顿的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

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