MySQL中怎么刪庫

這篇文章主要介紹“MySQL中怎么刪庫”,在日常操作中,相信很多人在MySQL中怎么刪庫問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對(duì)大家解答”MySQL中怎么刪庫”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!

創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供麻城網(wǎng)站建設(shè)、麻城做網(wǎng)站、麻城網(wǎng)站設(shè)計(jì)、麻城網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計(jì)與制作、麻城企業(yè)網(wǎng)站模板建站服務(wù),十年麻城做網(wǎng)站經(jīng)驗(yàn),不只是建網(wǎng)站,更提供有價(jià)值的思路和整體網(wǎng)絡(luò)服務(wù)。

傳統(tǒng)的高可用架構(gòu)是不能預(yù)防誤刪數(shù)據(jù)的,因?yàn)橹鲙斓囊粋€(gè)drop table命令,會(huì)通過binlog傳給所有從庫和級(jí)聯(lián)從庫,進(jìn)而導(dǎo)致整個(gè)集群的實(shí)例都會(huì)執(zhí)行這個(gè)命令。

雖然我們之前遇到的大多數(shù)的數(shù)據(jù)被刪,都是運(yùn)維同學(xué)或者DBA背鍋的。但實(shí)際上,只要有數(shù)據(jù)操作權(quán)限的同學(xué),都有可能踩到誤刪數(shù)據(jù)這條線。

今天我們就來聊聊誤刪數(shù)據(jù)前后,我們可以做些什么,減少誤刪數(shù)據(jù)的風(fēng)險(xiǎn),和由誤刪數(shù)據(jù)帶來的損失。

為了找到解決誤刪數(shù)據(jù)的更高效的方法,我們需要先對(duì)和MySQL相關(guān)的誤刪數(shù)據(jù),做下分類:

  1. 使用delete語句誤刪數(shù)據(jù)行;

  2. 使用drop table或者truncate table語句誤刪數(shù)據(jù)表;

  3. 使用drop database語句誤刪數(shù)據(jù)庫;

  4. 使用rm命令誤刪整個(gè)MySQL實(shí)例。

誤刪行

如果是使用delete語句誤刪了數(shù)據(jù)行,可以用Flashback工具通過閃回把數(shù)據(jù)恢復(fù)回來。

Flashback恢復(fù)數(shù)據(jù)的原理,是修改binlog的內(nèi)容,拿回原庫重放。而能夠使用這個(gè)方案的前提是,需要確保binlog_format=row 和 binlog_row_image=FULL。

具體恢復(fù)數(shù)據(jù)時(shí),對(duì)單個(gè)事務(wù)做如下處理:

  1. 對(duì)于insert語句,對(duì)應(yīng)的binlog event類型是Write_rows event,把它改成Delete_rows event即可;

  2. 同理,對(duì)于delete語句,也是將Delete_rows event改為Write_rows event;

  3. 而如果是Update_rows的話,binlog里面記錄了數(shù)據(jù)行修改前和修改后的值,對(duì)調(diào)這兩行的位置即可。

如果誤操作不是一個(gè),而是多個(gè),會(huì)怎么樣呢?比如下面三個(gè)事務(wù):

(A)delete ...
(B)insert ...
(C)update ...

現(xiàn)在要把數(shù)據(jù)庫恢復(fù)回這三個(gè)事務(wù)操作之前的狀態(tài),用Flashback工具解析binlog后,寫回主庫的命令是:

(reverse C)update ...
(reverse B)delete ...
(reverse A)insert ...

也就是說,如果誤刪數(shù)據(jù)涉及到了多個(gè)事務(wù)的話,需要將事務(wù)的順序調(diào)過來再執(zhí)行。

需要說明的是,我不建議你直接在主庫上執(zhí)行這些操作。

恢復(fù)數(shù)據(jù)比較安全的做法,是恢復(fù)出一個(gè)備份,或者找一個(gè)從庫作為臨時(shí)庫,在這個(gè)臨時(shí)庫上執(zhí)行這些操作,然后再將確認(rèn)過的臨時(shí)庫的數(shù)據(jù),恢復(fù)回主庫。

為什么要這么做呢?

這是因?yàn)?,一個(gè)在執(zhí)行線上邏輯的主庫,數(shù)據(jù)狀態(tài)的變更往往是有關(guān)聯(lián)的??赡苡捎诎l(fā)現(xiàn)數(shù)據(jù)問題的時(shí)間晚了一點(diǎn)兒,就導(dǎo)致已經(jīng)在之前誤操作的基礎(chǔ)上,業(yè)務(wù)代碼邏輯又繼續(xù)修改了其他數(shù)據(jù)。所以,如果這時(shí)候單獨(dú)恢復(fù)這幾行數(shù)據(jù),而又未經(jīng)確認(rèn)的話,就可能會(huì)出現(xiàn)對(duì)數(shù)據(jù)的二次破壞。

當(dāng)然,我們不止要說誤刪數(shù)據(jù)的事后處理辦法,更重要是要做到事前預(yù)防。我有以下兩個(gè)建議:

  1. 把sql_safe_updates參數(shù)設(shè)置為on。這樣一來,如果我們忘記在delete或者update語句中寫where條件,或者where條件里面沒有包含索引字段的話,這條語句的執(zhí)行就會(huì)報(bào)錯(cuò)。

  2. 代碼上線前,必須經(jīng)過SQL審計(jì)。

你可能會(huì)說,設(shè)置了sql_safe_updates=on,如果我真的要把一個(gè)小表的數(shù)據(jù)全部刪掉,應(yīng)該怎么辦呢?

如果你確定這個(gè)刪除操作沒問題的話,可以在delete語句中加上where條件,比如where id>=0。

但是,delete全表是很慢的,需要生成回滾日志、寫redo、寫binlog。所以,從性能角度考慮,你應(yīng)該優(yōu)先考慮使用truncate table或者drop table命令。

使用delete命令刪除的數(shù)據(jù),你還可以用Flashback來恢復(fù)。而使用truncate /drop table和drop database命令刪除的數(shù)據(jù),就沒辦法通過Flashback來恢復(fù)了。為什么呢?

這是因?yàn)?,即使我們配置了binlog_format=row,執(zhí)行這三個(gè)命令時(shí),記錄的binlog還是statement格式。binlog里面就只有一個(gè)truncate/drop 語句,這些信息是恢復(fù)不出數(shù)據(jù)的。

那么,如果我們真的是使用這幾條命令誤刪數(shù)據(jù)了,又該怎么辦呢?

誤刪庫/表

這種情況下,要想恢復(fù)數(shù)據(jù),就需要使用全量備份,加增量日志的方式了。這個(gè)方案要求線上有定期的全量備份,并且實(shí)時(shí)備份binlog。

在這兩個(gè)條件都具備的情況下,假如有人中午12點(diǎn)誤刪了一個(gè)庫,恢復(fù)數(shù)據(jù)的流程如下:

  1. 取最近一次全量備份,假設(shè)這個(gè)庫是一天一備,上次備份是當(dāng)天0點(diǎn);

  2. 用備份恢復(fù)出一個(gè)臨時(shí)庫;

  3. 從日志備份里面,取出凌晨0點(diǎn)之后的日志;

  4. 把這些日志,除了誤刪除數(shù)據(jù)的語句外,全部應(yīng)用到臨時(shí)庫。

這個(gè)流程的示意圖如下所示:

MySQL中怎么刪庫

圖1 數(shù)據(jù)恢復(fù)流程-mysqlbinlog方法

關(guān)于這個(gè)過程,我需要和你說明如下幾點(diǎn):

  1. 為了加速數(shù)據(jù)恢復(fù),如果這個(gè)臨時(shí)庫上有多個(gè)數(shù)據(jù)庫,你可以在使用mysqlbinlog命令時(shí),加上一個(gè)–database參數(shù),用來指定誤刪表所在的庫。這樣,就避免了在恢復(fù)數(shù)據(jù)時(shí)還要應(yīng)用其他庫日志的情況。

  2. 在應(yīng)用日志的時(shí)候,需要跳過12點(diǎn)誤操作的那個(gè)語句的binlog:

    • 如果原實(shí)例沒有使用GTID模式,只能在應(yīng)用到包含12點(diǎn)的binlog文件的時(shí)候,先用–stop-position參數(shù)執(zhí)行到誤操作之前的日志,然后再用–start-position從誤操作之后的日志繼續(xù)執(zhí)行;

    • 如果實(shí)例使用了GTID模式,就方便多了。假設(shè)誤操作命令的GTID是gtid1,那么只需要執(zhí)行set gtid_next=gtid1;begin;commit; 先把這個(gè)GTID加到臨時(shí)實(shí)例的GTID集合,之后按順序執(zhí)行binlog的時(shí)候,就會(huì)自動(dòng)跳過誤操作的語句。

不過,即使這樣,使用mysqlbinlog方法恢復(fù)數(shù)據(jù)還是不夠快,主要原因有兩個(gè):

  1. 如果是誤刪表,最好就是只恢復(fù)出這張表,也就是只重放這張表的操作,但是mysqlbinlog工具并不能指定只解析一個(gè)表的日志;

  2. 用mysqlbinlog解析出日志應(yīng)用,應(yīng)用日志的過程就只能是單線程。我們前文中介紹的那些并行復(fù)制的方法,在這里都用不上。

一種加速的方法是,在用備份恢復(fù)出臨時(shí)實(shí)例之后,將這個(gè)臨時(shí)實(shí)例設(shè)置成線上備庫的從庫,這樣:

  1. 在start slave之前,先通過執(zhí)行?
    ?change replication filter replicate_do_table = (tbl_name) 命令,就可以讓臨時(shí)庫只同步誤操作的表;

  2. 這樣做也可以用上并行復(fù)制技術(shù),來加速整個(gè)數(shù)據(jù)恢復(fù)過程。

這個(gè)過程的示意圖如下所示。

MySQL中怎么刪庫

圖2 數(shù)據(jù)恢復(fù)流程-master-slave方法

可以看到,圖中binlog備份系統(tǒng)到線上備庫有一條虛線,是指如果由于時(shí)間太久,備庫上已經(jīng)刪除了臨時(shí)實(shí)例需要的binlog的話,我們可以從binlog備份系統(tǒng)中找到需要的binlog,再放回備庫中。

假設(shè),我們發(fā)現(xiàn)當(dāng)前臨時(shí)實(shí)例需要的binlog是從master.000005開始的,但是在備庫上執(zhí)行show binlogs 顯示的最小的binlog文件是master.000007,意味著少了兩個(gè)binlog文件。這時(shí),我們就需要去binlog備份系統(tǒng)中找到這兩個(gè)文件。

把之前刪掉的binlog放回備庫的操作步驟,是這樣的:

  1. 從備份系統(tǒng)下載master.000005和master.000006這兩個(gè)文件,放到備庫的日志目錄下;

  2. 打開日志目錄下的master.index文件,在文件開頭加入兩行,內(nèi)容分別是 “./master.000005”和“./master.000006”;

  3. 重啟備庫,目的是要讓備庫重新識(shí)別這兩個(gè)日志文件;

  4. 現(xiàn)在這個(gè)備庫上就有了臨時(shí)庫需要的所有binlog了,建立主備關(guān)系,就可以正常同步了。

不論是把mysqlbinlog工具解析出的binlog文件應(yīng)用到臨時(shí)庫,還是把臨時(shí)庫接到備庫上,這兩個(gè)方案的共同點(diǎn)是:誤刪庫或者表后,恢復(fù)數(shù)據(jù)的思路主要就是通過備份,再加上應(yīng)用binlog的方式。

也就是說,這兩個(gè)方案都要求備份系統(tǒng)定期備份全量日志,而且需要確保binlog在被從本地刪除之前已經(jīng)做了備份。

但是,一個(gè)系統(tǒng)不可能備份無限的日志,你還需要根據(jù)成本和磁盤空間資源,設(shè)定一個(gè)日志保留的天數(shù)。如果你的DBA團(tuán)隊(duì)告訴你,可以保證把某個(gè)實(shí)例恢復(fù)到半個(gè)月內(nèi)的任意時(shí)間點(diǎn),這就表示備份系統(tǒng)保留的日志時(shí)間就至少是半個(gè)月。

另外,我建議你不論使用上述哪種方式,都要把這個(gè)數(shù)據(jù)恢復(fù)功能做成自動(dòng)化工具,并且經(jīng)常拿出來演練。為什么這么說呢?

這里的原因,主要包括兩個(gè)方面:

  1. 雖然“發(fā)生這種事,大家都不想的”,但是萬一出現(xiàn)了誤刪事件,能夠快速恢復(fù)數(shù)據(jù),將損失降到最小,也應(yīng)該不用跑路了。

  2. 而如果臨時(shí)再手忙腳亂地手動(dòng)操作,最后又誤操作了,對(duì)業(yè)務(wù)造成了二次傷害,那就說不過去了。

延遲復(fù)制備庫

雖然我們可以通過利用并行復(fù)制來加速恢復(fù)數(shù)據(jù)的過程,但是這個(gè)方案仍然存在“恢復(fù)時(shí)間不可控”的問題。

如果一個(gè)庫的備份特別大,或者誤操作的時(shí)間距離上一個(gè)全量備份的時(shí)間較長,比如一周一備的實(shí)例,在備份之后的第6天發(fā)生誤操作,那就需要恢復(fù)6天的日志,這個(gè)恢復(fù)時(shí)間可能是要按天來計(jì)算的。

那么,我們有什么方法可以縮短恢復(fù)數(shù)據(jù)需要的時(shí)間呢?

如果有非常核心的業(yè)務(wù),不允許太長的恢復(fù)時(shí)間,我們可以考慮搭建延遲復(fù)制的備庫。這個(gè)功能是MySQL 5.6版本引入的。

一般的主備復(fù)制結(jié)構(gòu)存在的問題是,如果主庫上有個(gè)表被誤刪了,這個(gè)命令很快也會(huì)被發(fā)給所有從庫,進(jìn)而導(dǎo)致所有從庫的數(shù)據(jù)表也都一起被誤刪了。

延遲復(fù)制的備庫是一種特殊的備庫,通過 CHANGE MASTER TO MASTER_DELAY = N命令,可以指定這個(gè)備庫持續(xù)保持跟主庫有N秒的延遲。

比如你把N設(shè)置為3600,這就代表了如果主庫上有數(shù)據(jù)被誤刪了,并且在1小時(shí)內(nèi)發(fā)現(xiàn)了這個(gè)誤操作命令,這個(gè)命令就還沒有在這個(gè)延遲復(fù)制的備庫執(zhí)行。這時(shí)候到這個(gè)備庫上執(zhí)行stop slave,再通過之前介紹的方法,跳過誤操作命令,就可以恢復(fù)出需要的數(shù)據(jù)。

這樣的話,你就隨時(shí)可以得到一個(gè),只需要最多再追1小時(shí),就可以恢復(fù)出數(shù)據(jù)的臨時(shí)實(shí)例,也就縮短了整個(gè)數(shù)據(jù)恢復(fù)需要的時(shí)間。

預(yù)防誤刪庫/表的方法

雖然常在河邊走,很難不濕鞋,但終究還是可以找到一些方法來避免的。所以這里,我也會(huì)給你一些減少誤刪操作風(fēng)險(xiǎn)的建議。

第一條建議是,賬號(hào)分離。這樣做的目的是,避免寫錯(cuò)命令。比如:

  • 我們只給業(yè)務(wù)開發(fā)同學(xué)DML權(quán)限,而不給truncate/drop權(quán)限。而如果業(yè)務(wù)開發(fā)人員有DDL需求的話,也可以通過開發(fā)管理系統(tǒng)得到支持。

  • 即使是DBA團(tuán)隊(duì)成員,日常也都規(guī)定只使用只讀賬號(hào),必要的時(shí)候才使用有更新權(quán)限的賬號(hào)。

第二條建議是,制定操作規(guī)范。這樣做的目的,是避免寫錯(cuò)要?jiǎng)h除的表名。比如:

  • 在刪除數(shù)據(jù)表之前,必須先對(duì)表做改名操作。然后,觀察一段時(shí)間,確保對(duì)業(yè)務(wù)無影響以后再刪除這張表。

  • 改表名的時(shí)候,要求給表名加固定的后綴(比如加_to_be_deleted),然后刪除表的動(dòng)作必須通過管理系統(tǒng)執(zhí)行。并且,管理系刪除表的時(shí)候,只能刪除固定后綴的表。

rm刪除數(shù)據(jù)

其實(shí),對(duì)于一個(gè)有高可用機(jī)制的MySQL集群來說,最不怕的就是rm刪除數(shù)據(jù)了。只要不是惡意地把整個(gè)集群刪除,而只是刪掉了其中某一個(gè)節(jié)點(diǎn)的數(shù)據(jù)的話,HA系統(tǒng)就會(huì)開始工作,選出一個(gè)新的主庫,從而保證整個(gè)集群的正常工作。

這時(shí),你要做的就是在這個(gè)節(jié)點(diǎn)上把數(shù)據(jù)恢復(fù)回來,再接入整個(gè)集群。

當(dāng)然了,現(xiàn)在不止是DBA有自動(dòng)化系統(tǒng),SA(系統(tǒng)管理員)也有自動(dòng)化系統(tǒng),所以也許一個(gè)批量下線機(jī)器的操作,會(huì)讓你整個(gè)MySQL集群的所有節(jié)點(diǎn)都全軍覆沒。

應(yīng)對(duì)這種情況,我的建議只能是說盡量把你的備份跨機(jī)房,或者最好是跨城市保存。

小結(jié)

今天,我和你討論了誤刪數(shù)據(jù)的幾種可能,以及誤刪后的處理方法。

但,我要強(qiáng)調(diào)的是,預(yù)防遠(yuǎn)比處理的意義來得大。

另外,在MySQL的集群方案中,會(huì)時(shí)不時(shí)地用到備份來恢復(fù)實(shí)例,因此定期檢查備份的有效性也很有必要。

如果你是業(yè)務(wù)開發(fā)同學(xué),你可以用show grants命令查看賬戶的權(quán)限,如果權(quán)限過大,可以建議DBA同學(xué)給你分配權(quán)限低一些的賬號(hào);你也可以評(píng)估業(yè)務(wù)的重要性,和DBA商量備份的周期、是否有必要?jiǎng)?chuàng)建延遲復(fù)制的備庫等等。

數(shù)據(jù)和服務(wù)的可靠性不止是運(yùn)維團(tuán)隊(duì)的工作,最終是各個(gè)環(huán)節(jié)一起保障的結(jié)果。

為什么還有kill 不掉的語句

在MySQL中有兩個(gè)kill命令:一個(gè)是kill query +線程id,表示終止這個(gè)線程中正在執(zhí)行的語句;一個(gè)是kill connection +線程id,這里connection可缺省,表示斷開這個(gè)線程的連接,當(dāng)然如果這個(gè)線程有語句正在執(zhí)行,也是要先停止正在執(zhí)行的語句的。

不知道你在使用MySQL的時(shí)候,有沒有遇到過這樣的現(xiàn)象:使用了kill命令,卻沒能斷開這個(gè)連接。再執(zhí)行show processlist命令,看到這條語句的Command列顯示的是Killed。

你一定會(huì)奇怪,顯示為Killed是什么意思,不是應(yīng)該直接在show processlist的結(jié)果里看不到這個(gè)線程了嗎?

今天,我們就來討論一下這個(gè)問題。其實(shí)大多數(shù)情況下,kill query/connection命令是有效的。比如,執(zhí)行一個(gè)查詢的過程中,發(fā)現(xiàn)執(zhí)行時(shí)間太久,要放棄繼續(xù)查詢,這時(shí)我們就可以用kill query命令,終止這條查詢語句。

還有一種情況是,語句處于鎖等待的時(shí)候,直接使用kill命令也是有效的。我們一起來看下這個(gè)例子:

MySQL中怎么刪庫

                                                                                  圖1 kill query 成功的例子

可以看到,session C 執(zhí)行kill query以后,session B幾乎同時(shí)就提示了語句被中斷。這,就是我們預(yù)期的結(jié)果。

收到kill以后,線程做什么?

但是,這里你要停下來想一下:session B是直接終止掉線程,什么都不管就直接退出嗎?顯然,這是不行的。

當(dāng)對(duì)一個(gè)表做增刪改查操作時(shí),會(huì)在表上加MDL讀鎖。所以,session B雖然處于blocked狀態(tài),但還是拿著一個(gè)MDL讀鎖的。如果線程被kill的時(shí)候,就直接終止,那之后這個(gè)MDL讀鎖就沒機(jī)會(huì)被釋放了。

這樣看來,kill并不是馬上停止的意思,而是告訴執(zhí)行線程說,這條語句已經(jīng)不需要繼續(xù)執(zhí)行了,可以開始“執(zhí)行停止的邏輯了”。

其實(shí),這跟Linux的kill命令類似,kill -N pid并不是讓進(jìn)程直接停止,而是給進(jìn)程發(fā)一個(gè)信號(hào),然后進(jìn)程處理這個(gè)信號(hào),進(jìn)入終止邏輯。只是對(duì)于MySQL的kill命令來說,不需要傳信號(hào)量參數(shù),就只有“停止”這個(gè)命令。

實(shí)現(xiàn)上,當(dāng)用戶執(zhí)行kill query thread_id_B時(shí),MySQL里處理kill命令的線程做了兩件事:

  1. 把session B的運(yùn)行狀態(tài)改成THD::KILL_QUERY(將變量killed賦值為THD::KILL_QUERY);

  2. 給session B的執(zhí)行線程發(fā)一個(gè)信號(hào)。

為什么要發(fā)信號(hào)呢?

因?yàn)橄駡D1的我們例子里面,session B處于鎖等待狀態(tài),如果只是把session B的線程狀態(tài)設(shè)置THD::KILL_QUERY,線程B并不知道這個(gè)狀態(tài)變化,還是會(huì)繼續(xù)等待。發(fā)一個(gè)信號(hào)的目的,就是讓session B退出等待,來處理這個(gè)THD::KILL_QUERY狀態(tài)。

上面的分析中,隱含了這么三層意思:

  1. 一個(gè)語句執(zhí)行過程中有多處“埋點(diǎn)”,在這些“埋點(diǎn)”的地方判斷線程狀態(tài),如果發(fā)現(xiàn)線程狀態(tài)是THD::KILL_QUERY,才開始進(jìn)入語句終止邏輯;

  2. 如果處于等待狀態(tài),必須是一個(gè)可以被喚醒的等待,否則根本不會(huì)執(zhí)行到“埋點(diǎn)”處;

  3. 語句從開始進(jìn)入終止邏輯,到終止邏輯完全完成,是有一個(gè)過程的。

到這里你就知道了,原來不是“說停就停的”。

接下來,我們再看一個(gè)kill不掉的例子,也就是我們在前面提到的 innodb_thread_concurrency 不夠用的例子。

首先,執(zhí)行set global innodb_thread_concurrency=2,將InnoDB的并發(fā)線程上限數(shù)設(shè)置為2;然后,執(zhí)行下面的序列:

MySQL中怎么刪庫

                                                                           圖 3 kill connection之后的效果

這時(shí)候,id=12這個(gè)線程的Commnad列顯示的是Killed。也就是說,客戶端雖然斷開了連接,但實(shí)際上服務(wù)端上這條語句還在執(zhí)行過程中。

為什么在執(zhí)行kill query命令時(shí),這條語句不像第一個(gè)例子的update語句一樣退出呢?

在實(shí)現(xiàn)上,等行鎖時(shí),使用的是pthread_cond_timedwait函數(shù),這個(gè)等待狀態(tài)可以被喚醒。但是,在這個(gè)例子里,12號(hào)線程的等待邏輯是這樣的:每10毫秒判斷一下是否可以進(jìn)入InnoDB執(zhí)行,如果不行,就調(diào)用nanosleep函數(shù)進(jìn)入sleep狀態(tài)。

也就是說,雖然12號(hào)線程的狀態(tài)已經(jīng)被設(shè)置成了KILL_QUERY,但是在這個(gè)等待進(jìn)入InnoDB的循環(huán)過程中,并沒有去判斷線程的狀態(tài),因此根本不會(huì)進(jìn)入終止邏輯階段。

而當(dāng)session E執(zhí)行kill connection 命令時(shí),是這么做的,

  1. 把12號(hào)線程狀態(tài)設(shè)置為KILL_CONNECTION;

  2. 關(guān)掉12號(hào)線程的網(wǎng)絡(luò)連接。因?yàn)橛羞@個(gè)操作,所以你會(huì)看到,這時(shí)候session C收到了斷開連接的提示。

那為什么執(zhí)行show processlist的時(shí)候,會(huì)看到Command列顯示為killed呢?其實(shí),這就是因?yàn)樵趫?zhí)行show processlist的時(shí)候,有一個(gè)特別的邏輯:

如果一個(gè)線程的狀態(tài)是KILL_CONNECTION,就把Command列顯示成Killed。

所以其實(shí),即使是客戶端退出了,這個(gè)線程的狀態(tài)仍然是在等待中。那這個(gè)線程什么時(shí)候會(huì)退出呢?

答案是,只有等到滿足進(jìn)入InnoDB的條件后,session C的查詢語句繼續(xù)執(zhí)行,然后才有可能判斷到線程狀態(tài)已經(jīng)變成了KILL_QUERY或者KILL_CONNECTION,再進(jìn)入終止邏輯階段。

到這里,我們來小結(jié)一下。

這個(gè)例子是kill無效的第一類情況,即:線程沒有執(zhí)行到判斷線程狀態(tài)的邏輯。跟這種情況相同的,還有由于IO壓力過大,讀寫IO的函數(shù)一直無法返回,導(dǎo)致不能及時(shí)判斷線程的狀態(tài)。

另一類情況是,終止邏輯耗時(shí)較長。這時(shí)候,從show processlist結(jié)果上看也是Command=Killed,需要等到終止邏輯完成,語句才算真正完成。這類情況,比較常見的場景有以下幾種:

  1. 超大事務(wù)執(zhí)行期間被kill。這時(shí)候,回滾操作需要對(duì)事務(wù)執(zhí)行期間生成的所有新數(shù)據(jù)版本做回收操作,耗時(shí)很長。

  2. 大查詢回滾。如果查詢過程中生成了比較大的臨時(shí)文件,加上此時(shí)文件系統(tǒng)壓力大,刪除臨時(shí)文件可能需要等待IO資源,導(dǎo)致耗時(shí)較長。

  3. DDL命令執(zhí)行到最后階段,如果被kill,需要?jiǎng)h除中間過程的臨時(shí)文件,也可能受IO資源影響耗時(shí)較久。

之前有人問過我,如果直接在客戶端通過Ctrl+C命令,是不是就可以直接終止線程呢?

答案是,不可以。

這里有一個(gè)誤解,其實(shí)在客戶端的操作只能操作到客戶端的線程,客戶端和服務(wù)端只能通過網(wǎng)絡(luò)交互,是不可能直接操作服務(wù)端線程的。

而由于MySQL是停等協(xié)議,所以這個(gè)線程執(zhí)行的語句還沒有返回的時(shí)候,再往這個(gè)連接里面繼續(xù)發(fā)命令也是沒有用的。實(shí)際上,執(zhí)行Ctrl+C的時(shí)候,是MySQL客戶端另外啟動(dòng)一個(gè)連接,然后發(fā)送一個(gè)kill query 命令。

所以,你可別以為在客戶端執(zhí)行完Ctrl+C就萬事大吉了。因?yàn)?,要kill掉一個(gè)線程,還涉及到后端的很多操作。

另外兩個(gè)關(guān)于客戶端的誤解

在實(shí)際使用中,我也經(jīng)常會(huì)碰到一些同學(xué)對(duì)客戶端的使用有誤解。接下來,我們就來看看兩個(gè)最常見的誤解。

第一個(gè)誤解是:如果庫里面的表特別多,連接就會(huì)很慢。

有些線上的庫,會(huì)包含很多表(我見過最多的一個(gè)庫里有6萬個(gè)表)。這時(shí)候,你就會(huì)發(fā)現(xiàn),每次用客戶端連接都會(huì)卡在下面這個(gè)界面上。

而如果db1這個(gè)庫里表很少的話,連接起來就會(huì)很快,可以很快進(jìn)入輸入命令的狀態(tài)。因此,有同學(xué)會(huì)認(rèn)為是表的數(shù)目影響了連接性能。

從開始你就知道,每個(gè)客戶端在和服務(wù)端建立連接的時(shí)候,需要做的事情就是TCP握手、用戶校驗(yàn)、獲取權(quán)限。但這幾個(gè)操作,顯然跟庫里面表的個(gè)數(shù)無關(guān)。

但實(shí)際上,正如圖中的文字提示所說的,當(dāng)使用默認(rèn)參數(shù)連接的時(shí)候,MySQL客戶端會(huì)提供一個(gè)本地庫名和表名補(bǔ)全的功能。為了實(shí)現(xiàn)這個(gè)功能,客戶端在連接成功后,需要多做一些操作:

  1. 執(zhí)行show databases;

  2. 切到db1庫,執(zhí)行show tables;

  3. 把這兩個(gè)命令的結(jié)果用于構(gòu)建一個(gè)本地的哈希表。

在這些操作中,最花時(shí)間的就是第三步在本地構(gòu)建哈希表的操作。所以,當(dāng)一個(gè)庫中的表個(gè)數(shù)非常多的時(shí)候,這一步就會(huì)花比較長的時(shí)間。

也就是說,我們感知到的連接過程慢,其實(shí)并不是連接慢,也不是服務(wù)端慢,而是客戶端慢。

圖中的提示也說了,如果在連接命令中加上-A,就可以關(guān)掉這個(gè)自動(dòng)補(bǔ)全的功能,然后客戶端就可以快速返回了。

這里自動(dòng)補(bǔ)全的效果就是,你在輸入庫名或者表名的時(shí)候,輸入前綴,可以使用Tab鍵自動(dòng)補(bǔ)全表名或者顯示提示。

實(shí)際使用中,如果你自動(dòng)補(bǔ)全功能用得并不多,我建議你每次使用的時(shí)候都默認(rèn)加-A。

其實(shí)提示里面沒有說,除了加-A以外,加–quick(或者簡寫為-q)參數(shù),也可以跳過這個(gè)階段。但是,這個(gè)–quick是一個(gè)更容易引起誤會(huì)的參數(shù),也是關(guān)于客戶端常見的一個(gè)誤解。

你看到這個(gè)參數(shù),是不是覺得這應(yīng)該是一個(gè)讓服務(wù)端加速的參數(shù)?但實(shí)際上恰恰相反,設(shè)置了這個(gè)參數(shù)可能會(huì)降低服務(wù)端的性能。為什么這么說呢?

MySQL客戶端發(fā)送請(qǐng)求后,接收服務(wù)端返回結(jié)果的方式有兩種:

  1. 一種是本地緩存,也就是在本地開一片內(nèi)存,先把結(jié)果存起來。如果你用API開發(fā),對(duì)應(yīng)的就是mysql_store_result 方法。

  2. 另一種是不緩存,讀一個(gè)處理一個(gè)。如果你用API開發(fā),對(duì)應(yīng)的就是mysql_use_result方法。

MySQL客戶端默認(rèn)采用第一種方式,而如果加上–quick參數(shù),就會(huì)使用第二種不緩存的方式。

采用不緩存的方式時(shí),如果本地處理得慢,就會(huì)導(dǎo)致服務(wù)端發(fā)送結(jié)果被阻塞,因此會(huì)讓服務(wù)端變慢。關(guān)于服務(wù)端的具體行為,我會(huì)在下一篇文章再和你展開說明。

那你會(huì)說,既然這樣,為什么要給這個(gè)參數(shù)取名叫作quick呢?這是因?yàn)槭褂眠@個(gè)參數(shù)可以達(dá)到以下三點(diǎn)效果:

  • 第一點(diǎn),就是前面提到的,跳過表名自動(dòng)補(bǔ)全功能。

  • 第二點(diǎn),mysql_store_result需要申請(qǐng)本地內(nèi)存來緩存查詢結(jié)果,如果查詢結(jié)果太大,會(huì)耗費(fèi)較多的本地內(nèi)存,可能會(huì)影響客戶端本地機(jī)器的性能;

  • 第三點(diǎn),是不會(huì)把執(zhí)行命令記錄到本地的命令歷史文件。

所以你看到了,–quick參數(shù)的意思,是讓客戶端變得更快。

到此,關(guān)于“MySQL中怎么刪庫”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

當(dāng)前名稱:MySQL中怎么刪庫
文章出自:http://muchs.cn/article24/gppgce.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供小程序開發(fā)、搜索引擎優(yōu)化、網(wǎng)站建設(shè)、App開發(fā)、網(wǎng)頁設(shè)計(jì)公司、云服務(wù)器

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)

搜索引擎優(yōu)化