早上接到研發(fā)的通知,說(shuō)數(shù)據(jù)庫(kù)有大量的慢查詢(xún),并把調(diào)用的時(shí)間發(fā)過(guò)來(lái),查詢(xún)時(shí)間基本在0.5s以上。通知他們把sql發(fā)過(guò)來(lái),打開(kāi)數(shù)據(jù)庫(kù)連接,執(zhí)行sql,發(fā)現(xiàn)sql執(zhí)行很快,執(zhí)行時(shí)間不到0.1s,讓他們排查下網(wǎng)絡(luò)問(wèn)題。
成都創(chuàng)新互聯(lián)公司是一家專(zhuān)注于成都網(wǎng)站制作、成都做網(wǎng)站與策劃設(shè)計(jì),吳川網(wǎng)站建設(shè)哪家好?成都創(chuàng)新互聯(lián)公司做網(wǎng)站,專(zhuān)注于網(wǎng)站建設(shè)10余年,網(wǎng)設(shè)計(jì)領(lǐng)域的專(zhuān)業(yè)建站公司;建站業(yè)務(wù)涵蓋:吳川等地區(qū)。吳川做網(wǎng)站價(jià)格咨詢(xún):028-86922220
過(guò)了段時(shí)間接到通知,網(wǎng)絡(luò)無(wú)任何問(wèn)題,懷疑是不是DNS問(wèn)題導(dǎo)致,使用本地連接和遠(yuǎn)程連接方式執(zhí)行sql。
time MySQL -S mysql.sock -uxxx-p'xxx' -e "select 1"
time mysql -h xx.xx.xx.xx -P xxxx-u xxx-p'xxx' -e "select 1"
發(fā)現(xiàn)查詢(xún)差不多,大部分的查詢(xún)時(shí)間都需要0.5s左右,少部分在0.1s不到。排除dns的問(wèn)題。同事確認(rèn)數(shù)據(jù)庫(kù)的連接確實(shí)有問(wèn)題。
通過(guò)對(duì)比發(fā)現(xiàn),其他的實(shí)例均沒(méi)有這種情況,在這臺(tái)物理機(jī)上還有一個(gè)其他實(shí)例,訪(fǎng)問(wèn)基本在0.1s不到,只有極少部分達(dá)到0.5s,懷疑是配置參數(shù)問(wèn)題,將兩個(gè)實(shí)例的參數(shù)文件show global status;show global variables;做對(duì)比,沒(méi)有找到明顯異常。暫時(shí)陷入僵局。
由于是數(shù)據(jù)庫(kù)的連接問(wèn)題,領(lǐng)導(dǎo)要求必須解決,我們?cè)俅嗡伎?,因?yàn)榇蜷_(kāi)數(shù)據(jù)庫(kù)的連接執(zhí)行sql很快,但是像上面那樣執(zhí)行sql卻很慢,懷疑是數(shù)據(jù)庫(kù)的線(xiàn)程池的問(wèn)題。查找相關(guān)線(xiàn)程池的資料。
客戶(hù)端連接服務(wù)器線(xiàn)程,對(duì)兩者進(jìn)行映射,且對(duì)應(yīng)關(guān)系不固定 ,服務(wù)器線(xiàn)程負(fù)責(zé)執(zhí)行從客戶(hù)端傳來(lái)的sql
基本單位為線(xiàn)程組,每組包含一個(gè)監(jiān)聽(tīng)線(xiàn)程和執(zhí)行線(xiàn)程(有可能多個(gè)),前者負(fù)責(zé)接受客戶(hù)端傳來(lái)的sql,后者負(fù)責(zé)執(zhí)行;
當(dāng)接受到新sql時(shí),如果此時(shí)沒(méi)有其他sql則由監(jiān)聽(tīng)線(xiàn)程立即執(zhí)行,此期間該組暫時(shí)無(wú)監(jiān)聽(tīng),如果sql執(zhí)行時(shí)間過(guò)長(zhǎng),則線(xiàn)程池為其分配一個(gè)新的監(jiān)聽(tīng)線(xiàn)程;否則排隊(duì)等待;
mysql的線(xiàn)程池(thread pool)默認(rèn)有三種模式:one-thread-per-connection,pool-of-threads,和no-thread
Thread_pool_size為線(xiàn)程組的數(shù)量,默認(rèn)為cpu核數(shù),Threads_running為正常運(yùn)行的線(xiàn)程數(shù)量,查看當(dāng)前庫(kù)的thread情況:
Threads_running明顯超出Thread_pool_size值,由于thread pool最初設(shè)計(jì)的目標(biāo)是保持一定數(shù)量的線(xiàn)程處于“ACTIVE”狀態(tài),具體的實(shí)現(xiàn)方式就是控制thread group的數(shù)量和thread group內(nèi)部處于"ACTIVE"狀態(tài)的thread的數(shù)量??刂苩hread group內(nèi)部的ACTIVE狀態(tài)的數(shù)量,方法就是最大限度地保證處于A(yíng)CTIVE狀態(tài)的線(xiàn)程個(gè)數(shù)是1。很顯然當(dāng)前thread group中有一個(gè)處于WAITING狀態(tài)的thread了,如果再啟用一個(gè)新的線(xiàn)程并且處于A(yíng)CTIVE狀態(tài),剛才的線(xiàn)程由WAITING變?yōu)锳CTIVE狀態(tài)時(shí),此時(shí)將會(huì)有2個(gè)“ACTIVE”狀態(tài)的線(xiàn)程,和最初的目標(biāo)似乎相背,但顯然也不能讓后續(xù)就緒的socket一直等待下去,那應(yīng)該怎么處理?
那么此時(shí)需要一個(gè)權(quán)衡了,提供了這樣的一個(gè)方法:對(duì)正在A(yíng)CTIVE或WAITING狀態(tài)的線(xiàn)程啟用一個(gè)計(jì)數(shù)器,超過(guò)計(jì)數(shù)器后將該thread標(biāo)記為stalled,然后thread group創(chuàng)建新的thread或喚醒sleep的thread處理新的sokcet,這樣將是一個(gè)很好的權(quán)衡。超時(shí)時(shí)間該參數(shù)thread_pool_stall_limit來(lái)決定,默認(rèn)是500ms。
終于找到為什么查詢(xún)時(shí)間經(jīng)常在0.5s的原因了?。≌駣^人心的消息。找到原因就好辦了,mysql5.7該參數(shù)默認(rèn)為6ms,生產(chǎn)是5.6,默認(rèn)為500ms,我們可以將其改為10ms,這樣就強(qiáng)迫被老的sql更快被標(biāo)記為stalled,然后創(chuàng)建新的線(xiàn)程來(lái)處理新連接。
網(wǎng)上也查看了其他的資料,據(jù)稱(chēng)阿里的數(shù)據(jù)庫(kù)的thread_pool_stall_limit參數(shù)值也10ms,我們?cè)俅螠y(cè)試,連接查詢(xún)都在0.1s以?xún)?nèi),沒(méi)有出現(xiàn)忽高忽低的情況了。
第二天研發(fā)反饋,以前半小時(shí)一次的慢查詢(xún)告警消失了。詭異的慢查詢(xún)終于結(jié)束了。