日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線(xiàn)溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
MySQL客戶(hù)端Ctrl+C,服務(wù)端會(huì)發(fā)生什么?

我們也許有過(guò)這樣的經(jīng)歷:用 mysql? 客戶(hù)端連上數(shù)據(jù)庫(kù),執(zhí)行一條 SQL,結(jié)果遲遲執(zhí)行不完,我們等得不耐煩了,順手就是一個(gè) Ctrl + C。

Ctrl + C 之后,客戶(hù)端會(huì)干什么,服務(wù)端又會(huì)發(fā)生什么?我們一起來(lái)看看。

本文內(nèi)容基于 MySQL 8.0.32 源碼,涉及存儲(chǔ)引擎為 InnoDB。

1、客戶(hù)端會(huì)干什么?

想要觀察 Ctrl + C 時(shí),客戶(hù)端會(huì)干什么,用 mysql 連接數(shù)據(jù)庫(kù)時(shí)可以指定 -v 參數(shù),如下:

mysql -h127.0.0.1 -uroot -v

連上數(shù)據(jù)庫(kù)之后,執(zhí)行一條 SQL(以 UPDATE 為例?)。SQL 執(zhí)行完成之前,在鍵盤(pán)上按下 Ctrl + C,如下:

注意:沒(méi)有使用 begin 顯式開(kāi)啟事務(wù),且系統(tǒng)變量 autocommit 的值為 ON。

mysql> UPDATE t1 SET blob1 = REPEAT("這是 blob2 字段", 10240);
--------------
UPDATE t1 SET blob1 = REPEAT("這是 blob2 字段", 10240)
--------------

-- 客戶(hù)端發(fā)送 KILL QUERY 給服務(wù)端之后
-- 輸出的提示信息
^C^C -- sending "KILL QUERY 11" to server ...

# 服務(wù)端執(zhí)行 KILL QUERY 之后
# 客戶(hù)端自己的輸出信息
^C -- query aborted

-- 服務(wù)端返回給客戶(hù)端的信息
ERROR 1317 (70100): Query execution was interrupted

從以上輸出可以看到,客戶(hù)端 Ctrl + C,實(shí)際上是給服務(wù)端發(fā)出了一條 KILL QUERY 命令。

這和我們手動(dòng)執(zhí)行 KILL QUERY 命令是一樣的,接下來(lái),我們就來(lái)看看服務(wù)端是怎么執(zhí)行 KILL QUERY 命令的。

2、KILL QUERY

在 KILL QUERY 命令之前,客戶(hù)端已經(jīng)發(fā)出了一條 Update SQL,服務(wù)端分配了一個(gè)線(xiàn)程,正在執(zhí)行 Update SQL。

Update SQL 還沒(méi)執(zhí)行完,客戶(hù)端 Ctrl + C 又發(fā)出了 KILL QUERY 命令,服務(wù)端收到命令之后,會(huì)調(diào)度另一個(gè)線(xiàn)程來(lái)執(zhí)行 KILL QUERY 命令。

為了方便介紹,我們把執(zhí)行 Update SQL 的線(xiàn)程稱(chēng)為 Update 線(xiàn)程?,執(zhí)行 KILL QUERY 命令的線(xiàn)程稱(chēng)為 Kill 線(xiàn)程?。注意:MySQL 內(nèi)部是不做這樣區(qū)分的。

KILL QUERY 命令的執(zhí)行流程如下:

第 1 步,Kill 線(xiàn)程根據(jù) query id? 查找 Update 線(xiàn)程。如果沒(méi)有找到?,KILL QUERY 命令執(zhí)行結(jié)束;如果找到了,進(jìn)入第 2 步。

query id? 是 show processlist 執(zhí)行結(jié)果中的 id 字段。

第 2 步,Kill 線(xiàn)程判斷當(dāng)前連接的 MySQL 用戶(hù)是否有權(quán)限干掉 Update 線(xiàn)程。如果沒(méi)有?權(quán)限,KILL QUERY 命令執(zhí)行結(jié)束;如果有權(quán)限,進(jìn)入第 3 步。

第 3 步,判斷 Update 線(xiàn)程是否正在讀寫(xiě)數(shù)據(jù)字典表。

如果不是?,Kill 線(xiàn)程繼續(xù)執(zhí)行第 4 ~ 6 步;如果是,Kill 線(xiàn)程的使命就到此結(jié)束了,接力棒交給 Update 線(xiàn)程。

Update 線(xiàn)程?讀寫(xiě)數(shù)據(jù)字典表結(jié)束,就會(huì)馬上開(kāi)始執(zhí)行 KILL QUERY 命令的第 3 ~ 6 步。

這種情況下,第 3 步會(huì)被執(zhí)行 2 次(Kill 線(xiàn)程和 Update 線(xiàn)程各執(zhí)行一次)。

第 4 步,把 Update 線(xiàn)程的 killed? 屬性設(shè)置為 KILL_QUERY?,此時(shí),Update 線(xiàn)程處于被標(biāo)記為將要被干掉,但是還沒(méi)有被干掉的狀態(tài)。

這一步可以想象成城市建設(shè)過(guò)程中,在要拆遷的房子上寫(xiě)了個(gè)大大的拆字,但是房子還立在那里。

第 5 步,如果 Update 線(xiàn)程正在等待獲取存儲(chǔ)引擎中的鎖,則放棄等待;如果 Update 線(xiàn)程已經(jīng)持有存儲(chǔ)引擎中的鎖,則釋放鎖。

第 6 步,判斷 Update 線(xiàn)程是否持有某個(gè)條件變量?(保存在 current_cond)中。

如果持有,發(fā)送廣播通知正在等待這個(gè)條件變量的其它線(xiàn)程,告訴它們可以繼續(xù)執(zhí)行了。

通過(guò)前面的介紹,我們可以看到:不管是 Kill 線(xiàn)程,還是 Update 線(xiàn)程自己執(zhí)行?第 3 ~ 6 步?,都只是給 Update 線(xiàn)程打上了 KILL_QUERY 標(biāo)記,而沒(méi)有直接把 Update 線(xiàn)程干掉。

Update 線(xiàn)程是怎么被干掉的呢?請(qǐng)繼續(xù)往下看。

3、自己把自己干掉

KILL QUERY 執(zhí)行過(guò)程中,為什么不直接把 Update 線(xiàn)程干掉?

不是不想,而是不能。

因?yàn)榫€(xiàn)程不管執(zhí)行什么操作,都需要進(jìn)行收尾工作,做到有始有終。

如果 Update 線(xiàn)程直接被干掉,就來(lái)不及進(jìn)行收尾工作,例如:已經(jīng)申請(qǐng)的內(nèi)存無(wú)法釋放,會(huì)導(dǎo)致內(nèi)存泄漏。

所以,想要妥善干掉一個(gè)線(xiàn)程,需要即將被干掉的線(xiàn)程主動(dòng)配合 Kill 線(xiàn)程才行。

妥善干掉一個(gè) Update 線(xiàn)程的場(chǎng)景是這樣的:

Kill 線(xiàn)程對(duì) Update 線(xiàn)程說(shuō):我要把你干掉。

Update 線(xiàn)程回答:不勞你動(dòng)手,我自己來(lái)。

MySQL 讓這個(gè)場(chǎng)景變成現(xiàn)實(shí)的方式,是在代碼中的各個(gè)角落進(jìn)行埋點(diǎn),埋點(diǎn)邏輯:判斷當(dāng)前線(xiàn)程是否被打上了 KILL_QUERY 標(biāo)記,如果?是,則中斷正在執(zhí)行的操作,進(jìn)入收尾階段。

舉個(gè)例子:

// sql/sql_update.cc
// 以下代碼處理更新單表的 SQL,例如:
// update t1 set i1 = 100
bool Sql_cmd_update::update_single_table(THD *thd) {
...
while (true) {
// 從存儲(chǔ)引擎讀取一條記錄
error = iterator->Read();
// 如果讀取出錯(cuò)(error)
// 或者 thd->killed 不等于 0(也就是 true)
// 對(duì)應(yīng)本文的場(chǎng)景是:線(xiàn)程被打上了 KILL_QUERY 標(biāo)記
// 直接結(jié)束循環(huán)
if (error || thd->killed) break;
...
}
...
}

從以上代碼可以看到,執(zhí)行 Update 操作過(guò)程中,如果發(fā)現(xiàn)讀取出錯(cuò)(對(duì)應(yīng)本文場(chǎng)景是 Update 線(xiàn)程被打上了 KILL_QUERY 標(biāo)記),直接 break 退出循環(huán),中斷執(zhí)行。

4、回滾

Update 線(xiàn)程執(zhí)行過(guò)程中,事務(wù)有可能已經(jīng)增、刪、改了一些數(shù)據(jù),中斷正在執(zhí)行的操作之后,事務(wù)是需要回滾的。

當(dāng) Update 線(xiàn)程的執(zhí)行流程回到 mysql_execute_command():

int mysql_execute_command(THD *thd, bool first_level) {
...
if ((thd->is_error() && !early_error_on_rep_command) ||
(thd->variables.option_bits & OPTION_MASTER_SQL_ERROR))
trans_/opt/data/workspace_c/mysql8/sql/sql_class.ccrollback_stmt(thd);
else {
/* If commit fails, we should be able to reset the OK status. */
thd->get_stmt_da()->set_overwrite_status(true);
trans_commit_stmt(thd);
thd->get_stmt_da()->set_overwrite_status(false);
}
...
}

從代碼中可以看到,thd->is_error() 返回 true,說(shuō)明事務(wù)執(zhí)行過(guò)程中出現(xiàn)了錯(cuò)誤,對(duì)應(yīng)到本文的場(chǎng)景,就是事務(wù)被 KILL QUERY 中斷了,會(huì)執(zhí)行 trans_rollback_stmt(thd),回滾事務(wù)。

只有在開(kāi)啟組復(fù)制(GROUP REPLICATION)過(guò)程中出現(xiàn)錯(cuò)誤時(shí),early_error_on_rep_command 才有可能被設(shè)置為 true,這里我們先忽略。

到這里,KILL QUERY 就算是基本介紹完了。

之所以說(shuō)基本介紹完了,是因?yàn)檫€留有一點(diǎn)點(diǎn)尾巴。

前面我們介紹過(guò),Update 線(xiàn)程執(zhí)行到埋點(diǎn)的時(shí)候,如果判斷自己已經(jīng)被標(biāo)記為即將被干掉,就會(huì)中斷執(zhí)行。

但是,還有一種很小的可能性,就是 Update 線(xiàn)程執(zhí)行過(guò)程中,已經(jīng)經(jīng)過(guò)了所有埋點(diǎn)之后,才被標(biāo)記為即將被干掉,Update 線(xiàn)程也就沒(méi)有機(jī)會(huì)中斷執(zhí)行了。

這種情況下,就會(huì)進(jìn)入以上代碼中的 else 分支,執(zhí)行 trans_commit_stmt(thd),提交事務(wù)。

鑒于進(jìn)入 else 分支提交事務(wù)的可能性很小,我們可以認(rèn)為只要客戶(hù)端 Ctrl + C,Update 線(xiàn)程就會(huì)中斷執(zhí)行,并回滾事務(wù)。

5、總結(jié)

客戶(hù)端連接上 MySQL 之后,給服務(wù)端發(fā)送一條 SQL,SQL 執(zhí)行完成之前,客戶(hù)端 Ctrl + C,實(shí)際上會(huì)給服務(wù)端發(fā)送一條 KILL QUERY 命令,和我們手動(dòng)執(zhí)行 kill query  的效果是一樣的。

服務(wù)端會(huì)分配一個(gè)空閑線(xiàn)程(Kill 線(xiàn)程)執(zhí)行 kill query 操作,給 Update 線(xiàn)程打上 KILL_QUERY 標(biāo)記。

如果即將被干掉的線(xiàn)程(Update 線(xiàn)程)正在讀寫(xiě)數(shù)據(jù)字典表,它會(huì)從 kill 線(xiàn)程手上接過(guò)接力棒,給自己打上 KILL_QUERY 標(biāo)記。

Update 線(xiàn)程發(fā)現(xiàn)自己被打上了 KILL_QUERY 標(biāo)記,就會(huì)中斷執(zhí)行,在 mysql_execute_command() 方法中,會(huì)回滾事務(wù)。

有一點(diǎn)需要說(shuō)明,前面只是以 Update SQL 為例來(lái)介紹 KILL QUERY,其它 SQL 的 KILL QUERY 流程也是一樣的。?

6、番外篇

前面 1 ~ 5 小節(jié)介紹的是沒(méi)有通過(guò) begin 語(yǔ)句顯式開(kāi)啟事務(wù),并且系統(tǒng)變量 autocommit 的值是 ON 的場(chǎng)景。

如果通過(guò) begin 顯式開(kāi)啟了事務(wù),或者把系統(tǒng)變量 autocommit 的值設(shè)置為 OFF,前面 1 ~ 5 小節(jié)介紹的內(nèi)容也是適用的,但是會(huì)有一點(diǎn)區(qū)別:

4.回滾小節(jié)只能作用于事務(wù)中的一條 SQL,而不會(huì)影響整個(gè)事務(wù)。至于整個(gè)事務(wù)是提交還是回滾,取決于我們會(huì)給服務(wù)端發(fā)送 commit 還是 rollback 語(yǔ)句。

本文轉(zhuǎn)載自微信公眾號(hào)「一樹(shù)一溪」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系一樹(shù)一溪公眾號(hào)。


網(wǎng)站標(biāo)題:MySQL客戶(hù)端Ctrl+C,服務(wù)端會(huì)發(fā)生什么?
路徑分享:http://m.5511xx.com/article/cdiespc.html