MySQL 連接怎么?;??
多年前開(kāi)發(fā)過(guò)一個(gè)異步發(fā)送訂單短信、郵件通知的??守護(hù)?
?程序,每次程序啟動(dòng)時(shí)會(huì)創(chuàng)建數(shù)據(jù)庫(kù)連接,后續(xù)讀寫數(shù)據(jù)庫(kù)操作就一直復(fù)用這個(gè)連接。
某一天,用戶反饋下單后收不到通知了,我們登錄服務(wù)器看到程序還在運(yùn)行。
經(jīng)過(guò)排查確認(rèn),發(fā)生問(wèn)題的這天,距離上一次有用戶下單超過(guò)了 8 小時(shí),MySQL 服務(wù)端已經(jīng)自動(dòng)斷開(kāi)連接了。
解決這個(gè)問(wèn)題的辦法比較簡(jiǎn)單,程序只要定期給 MySQL 發(fā)送請(qǐng)求,表示自己還活著,MySQL 就不會(huì)觸發(fā)斷開(kāi)連接的操作了,這就是數(shù)據(jù)庫(kù)連接?;畹膽?yīng)用場(chǎng)景。
今天我們來(lái)聊聊數(shù)據(jù)庫(kù)連接?;畹脑砗头绞?。
本文內(nèi)容基于 MySQL 8.0.29 源碼。
正文
1、概述
MySQL 系統(tǒng)變量 wait_timeout,默認(rèn)值是 28800 秒(8 小時(shí)),用于控制客戶端多長(zhǎng)時(shí)間沒(méi)有給 MySQL 發(fā)送請(qǐng)求,MySQL 就自動(dòng)斷開(kāi)連接。
如果我們的業(yè)務(wù)系統(tǒng)不那么閑,能隔三差五的給 MySQL 發(fā)送一些請(qǐng)求,數(shù)據(jù)庫(kù)連接會(huì)一直處于活躍狀態(tài),也就不需要專門?;盍恕?/p>
有一些業(yè)務(wù)系統(tǒng),低峰期可能很長(zhǎng)時(shí)間都不會(huì)有讀寫請(qǐng)求,一旦間隔時(shí)間超過(guò) wait_timeout,數(shù)據(jù)庫(kù)連接就斷開(kāi)了,連接?;钭匀徊豢杀苊?。
接下來(lái)我們聊聊 2 種連接?;罘绞?,以及它們之間有什么不一樣,在這之前,我們先來(lái)看看 wait_timeout 是怎么控制超時(shí)邏輯的。
2、 wait_timeout 超時(shí)邏輯
客戶端和 MySQL 建立連接之后,MySQL 每次開(kāi)始等待客戶端發(fā)送數(shù)據(jù)之前,都會(huì)根據(jù)系統(tǒng)變量 ??wait_timeout?
? 的值設(shè)置最長(zhǎng)等待時(shí)間:
上面代碼中的 net_wait_timeout 就是系統(tǒng)變量 wait_timeout 的化身。
設(shè)置最長(zhǎng)等待時(shí)間之后,接下來(lái)就是安靜的等待了,執(zhí)行等待操作的方法是 vio_io_wait():
如果達(dá)到了最長(zhǎng)等待時(shí)間,客戶端一直沒(méi)有發(fā)送數(shù)據(jù),vio_io_wait() 會(huì)返 0 表示超時(shí)。
然后,程序會(huì)沿著調(diào)用棧一路返回到 net_read_raw_loop() 方法中,設(shè)置返回給客戶端的錯(cuò)誤碼 ER_CLIENT_INTERACTION_TIMEOUT(4031),對(duì)應(yīng)的錯(cuò)誤信息為:
準(zhǔn)備好返回給客戶端的錯(cuò)誤碼和錯(cuò)誤信息之后,就會(huì)進(jìn)行一系列斷開(kāi)連接相關(guān)的操作,最后把錯(cuò)誤碼和錯(cuò)誤信息發(fā)送給客戶端。
如果我們用的是 MySQL 自帶的交互式客戶端 mysql,發(fā)生超時(shí)之后,等下次再執(zhí)行 SQL 語(yǔ)句時(shí),就會(huì)看到這樣的錯(cuò)誤了:
對(duì) MySQL 服務(wù)端主動(dòng)斷開(kāi)連接過(guò)程大概介紹之后,接下來(lái)看看 2 種連接?;罘绞?。
3、ping
站在客戶端的視角看,使用 ping 命令是為了判斷 MySQL 服務(wù)端是否還活著。
換一個(gè)角度,在 MySQL 服務(wù)端看來(lái),一個(gè)客戶端給它發(fā)送了 ping 命令,說(shuō)明這個(gè)客戶端連接還活著,它就不會(huì)把這個(gè)客戶端的連接關(guān)閉。
所以,ping 命令不但可以用于數(shù)據(jù)庫(kù)連接探活,還可以用于保活。
MySQL 沒(méi)有提供 ping 語(yǔ)句,如果想測(cè)試發(fā)送 ping 命令,可以使用 mysqladmin:
在數(shù)據(jù)庫(kù)連接池或者業(yè)務(wù)系統(tǒng)中,通過(guò)程序提供的 API 也能很方便地發(fā)送 ping 命令給 MySQL 服務(wù)端。
在業(yè)務(wù)低峰期,客戶端定時(shí)給 MySQL 服務(wù)端發(fā)送 ping 命令,就能給連接?;盍?。
4、select
另一種連接?;罘绞绞菆?zhí)行 SQL 語(yǔ)句,一般都是 select 語(yǔ)句,可以有各種花樣:
執(zhí)行 select 語(yǔ)句?;睿驼?zhí)行業(yè)務(wù) SQL 沒(méi)什么區(qū)別,這里不展開(kāi)了。
5、兩種保活方式對(duì)比
既然 ping 和 select 都能實(shí)現(xiàn)數(shù)據(jù)庫(kù)連接?;?,那它們之間有什么不一樣?
在MySQL 源碼的實(shí)現(xiàn)中,體現(xiàn)了 2 點(diǎn)區(qū)別:
區(qū)別 1:ping 是命令,我們只能通過(guò) MySQL 提供的 API,或 mysqladmin 這樣的工具發(fā)送 ping 命令給 MySQL 服務(wù)端。
select 是 SQL 語(yǔ)句,通過(guò) MySQL API 或 mysql 交互式客戶端都能執(zhí)行 select 語(yǔ)句。
區(qū)別 2:ping 的執(zhí)行流程比 select 更短,效率更高,通過(guò)對(duì)比兩者的調(diào)用棧,我們能更直觀的看到這一點(diǎn)。
兩種方式都會(huì)響應(yīng)客戶端請(qǐng)求,后面給出的調(diào)用棧中,把這部分省略了。
ping 命令的主要調(diào)用棧如下:
ping 命令的調(diào)用棧很簡(jiǎn)單,連詞法解析、語(yǔ)法解析過(guò)程都不需要,進(jìn)入 dispatch_command() 方法之后,判斷是 ping 命令,就直接給客戶端返回 OK 狀態(tài),整個(gè)流程就結(jié)束了:
接下來(lái)是 select 的調(diào)用棧,以最簡(jiǎn)單的 SELECT 1 為例,主要調(diào)用棧如下:
SELECT 1 的調(diào)用棧比較長(zhǎng),把主要調(diào)用棧都列出來(lái)是為了大家對(duì) SELECT 1 的執(zhí)行過(guò)程有更直觀的了解。
從上面的調(diào)用??梢钥吹剑琒ELECT 1 雖然不需要從表里查詢數(shù)據(jù),但是詞法解析、語(yǔ)法解析、查詢準(zhǔn)備、查詢優(yōu)化、查詢執(zhí)行、事務(wù)提交、記錄慢 SQL 等等這些流程一個(gè)都沒(méi)落下,雖然很多方法進(jìn)去之后,并不需要執(zhí)行復(fù)雜的操作,但是各種 if ... else 判斷是少不了要執(zhí)行的。
SELECT 1 是 select 語(yǔ)句最簡(jiǎn)單的形式了,如果用其它 select 語(yǔ)句?;睿{(diào)用棧只會(huì)更長(zhǎng)。
通過(guò)上面 ping 命令 和 SELECT 1 的調(diào)用棧對(duì)比,相信大家對(duì)這兩種?;罘绞降膱?zhí)行效率已經(jīng)有了直觀的了解。
6. 總結(jié)
本文寫作的初衷就是為了對(duì)比 ping 和 select 兩種數(shù)據(jù)庫(kù)連接?;罘绞降膱?zhí)行效率。
經(jīng)過(guò)前面的介紹,我們就可以得出結(jié)論了:
ping 命令的執(zhí)行效率比 select 語(yǔ)句高,對(duì)于追求極致性能的應(yīng)用來(lái)說(shuō),使用 ping 命令給數(shù)據(jù)庫(kù)連接?;钍歉玫姆绞健?/p>
本文轉(zhuǎn)載自微信公眾號(hào)「一樹(shù)一溪」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系一樹(shù)一溪公眾號(hào)。