河南 劉建臣
某單位使用Java 開(kāi)發(fā)的數(shù)據(jù)分析系統(tǒng),后臺(tái)使用的是MySQL 數(shù)據(jù)庫(kù),最近出現(xiàn)了無(wú)法查詢和寫(xiě)入數(shù)據(jù)的情況,MySQL 服務(wù)器使用的是CentOS 6.X 系統(tǒng),使用了八核的CPU,內(nèi)存為64GB,按照這樣的配置,運(yùn)行MySQL 是沒(méi)有任何問(wèn)題的。筆者進(jìn)入MySQL 安裝路徑,發(fā)現(xiàn)似乎缺少了某些文件,果然其中的“ibdata1”“ib_logfile0”“ib_logfile1”等文件“不翼而飛”了。
MySQL 默認(rèn)采用InnoDB引擎,上述文件是MySQL 的表空間文件和日志文件,Innodb 存儲(chǔ)類(lèi)型的表的數(shù)據(jù)都放在該共享表空間中。看來(lái)是有人誤刪除了這些文件,是不是這些文件丟失的原因呢?因?yàn)镸ySQL服務(wù)并沒(méi)有重啟,是可以從內(nèi)存文件系統(tǒng)中找回這些文件的。執(zhí)行“netstat-nltp|grep mysqld”命令,可以查到MySQL 的進(jìn)程號(hào),這里為“6831”。執(zhí)行“l(fā)l/proc/6831/fd|grep -e ibdata -e ib_”命令,果然在內(nèi)存文件系統(tǒng)中找到了這些文件,狀態(tài)為“deleted”,說(shuō)明實(shí)際文件已被刪除。因?yàn)閿?shù)據(jù)庫(kù)處于運(yùn)行狀態(tài),在InnoDB 引擎緩存池中有很多數(shù)據(jù)已發(fā)生變動(dòng),但沒(méi)有寫(xiě)入到磁盤(pán)文件中,如果直接恢復(fù)肯定會(huì)造成數(shù)據(jù)丟失。
在MySQL 中執(zhí)行“flush tables with read lock;”命令,禁止針對(duì)數(shù)據(jù)庫(kù)的修改操作。執(zhí)行“show engine innodb statusG;”命令,在返回信息中的“Pages flushed up to”行顯示當(dāng)前最舊的臟頁(yè)數(shù)據(jù)對(duì)應(yīng)的位置,在“Log sequence number”行中顯示當(dāng)前最新數(shù)據(jù)產(chǎn)生的日志序列號(hào)信息。這里兩者數(shù)據(jù)一致,說(shuō)明內(nèi)存中的Dirty Page 數(shù)據(jù)已寫(xiě)入磁盤(pán)。在“Modified db pages”欄中顯示臟數(shù)據(jù)庫(kù)頁(yè)數(shù),該值應(yīng)為0,說(shuō)明Dirty Page 已刷新到磁盤(pán)。
否則,執(zhí)行“set global innodb_max_dirty_pages_pct=0;”命令,設(shè)置Dirty Page 刷新比例。當(dāng)確定Dirty Page 已全部寫(xiě)入磁盤(pán)后,執(zhí)行:
執(zhí)行文件復(fù)制操作,之后執(zhí)行“chown mysql:mysql/data1/mysql/ib*”命令,為上述文件指定用戶權(quán)限,因?yàn)檫\(yùn)行MySQL 的用戶名為“mysql”。執(zhí)行“/etc/init.d/mysqld restart”命令,重啟MySQL 服務(wù)。
進(jìn)入MySQL 運(yùn)行路徑,發(fā)現(xiàn)上述文件已找回,原本以為問(wèn)題解決了,但在對(duì)某個(gè)數(shù)據(jù)表進(jìn)行查詢和寫(xiě)入操作時(shí),依然出現(xiàn)失敗。
在MySQL 中執(zhí)行“show processlist;”命令,發(fā)現(xiàn)有線程正對(duì)“tb_anlydat”的表進(jìn)行檢測(cè)操作,而其他線程在進(jìn)行插入等操作時(shí),出現(xiàn)“Waiting for table level lock”提示,說(shuō)明MySQL 正在對(duì)該表進(jìn)行檢查,因此其他的寫(xiě)入操作處于阻塞狀態(tài)。
執(zhí)行“show create table tb_anlydat;”命令,發(fā)現(xiàn)該表使用的是MyISAM引擎,因?yàn)橥琁nnoDB 引擎相比,MyISAM 引擎具有較快的查詢速度,在實(shí)際中經(jīng)常會(huì)出現(xiàn)混合使用上述引擎的現(xiàn)象,即大部分表使用InnoDB引擎,少量對(duì)查詢性能要求較高的表使用MyISAM 引擎。MyISAM 引擎只支持表級(jí)鎖,如果當(dāng)數(shù)據(jù)庫(kù)出現(xiàn)執(zhí)行時(shí)間較長(zhǎng)的查詢或檢測(cè)時(shí),對(duì)目標(biāo)數(shù)據(jù)表進(jìn)行更新操作,就很容易出現(xiàn)“Waiting for table level lock”之類(lèi)的錯(cuò)誤提示,造成無(wú)法插入數(shù)據(jù)的問(wèn)題。
前端程序就會(huì)因連接失效造成訪問(wèn)請(qǐng)求失敗,連續(xù)執(zhí) 行“show processlist;”命令,可以發(fā)現(xiàn)相關(guān)線程正在對(duì)該表進(jìn)行修復(fù),說(shuō)明MySQL 已發(fā)現(xiàn)該表存在問(wèn)題,在進(jìn)行修復(fù)操作。
根據(jù)以上分析,可以發(fā)現(xiàn)上述問(wèn)題的原因,就在于某些數(shù)據(jù)表存儲(chǔ)問(wèn)題。既然數(shù)據(jù)表無(wú)法插入數(shù)據(jù),那么就需要進(jìn)行修復(fù)。在修復(fù)之前需要將內(nèi)容導(dǎo)出來(lái)。例如執(zhí)行“select * into outfile '/tmp/export.txt' from tb_anlydat;”命令,將該表中數(shù)據(jù)導(dǎo)出,在備用MySQL服務(wù)器上執(zhí)行“LOAD DATA local INFILE '/tmp/export.txt' IGNORE INTO TABLE tb_anlydat;”之類(lèi)的命令,將數(shù)據(jù)導(dǎo)入進(jìn)來(lái),便于對(duì)該數(shù)據(jù)表進(jìn)行修復(fù)。
MyISAM 引擎提供了Check Table、Repair Table和Myisanchk 等工具,可以對(duì)數(shù)據(jù)表進(jìn)行檢測(cè)和修復(fù)操作。例如在MySQL 中執(zhí)行“check table tb_anlydat;”和“repair table tb_anlydat;”命令,對(duì)該表進(jìn)行修復(fù)操作,但很花時(shí)間卻沒(méi)有什么效果。打開(kāi)MySQL 配置文件“my.cnf”,發(fā)現(xiàn)其中存在“myisamrecover=BACKUP,FORCE”行,這表明如果重啟MySQL,就會(huì)自動(dòng)針對(duì)使用MyISAM 的表進(jìn)行備份和檢測(cè)修復(fù)操作。因?yàn)樵摫眢w積達(dá)到幾百兆,讀寫(xiě)比較頻繁,往往會(huì)因?yàn)楦鞣N原因(例如斷電、硬件故障等),造成使用MyISAM 引擎的數(shù)據(jù)表出現(xiàn)故障。
筆者注意到該MySQL 數(shù)據(jù)庫(kù)使用了獨(dú)立的磁盤(pán)來(lái)存儲(chǔ)數(shù)據(jù),執(zhí)行“df -lh”命令,顯示還有約30%的可用空間,不存在磁盤(pán)空間不足問(wèn)題。執(zhí)行“dmesg”命令,在返回信息中發(fā)現(xiàn)和MySQL 對(duì)應(yīng)的磁盤(pán)出現(xiàn)“Medium Error”、“uncovered read erroe”、“critical medium error”等錯(cuò)誤信息,表明該硬盤(pán)分區(qū)出現(xiàn)了壞道等才引發(fā)了以上問(wèn)題。
表面看來(lái),可以在硬盤(pán)上進(jìn)行讀寫(xiě)操作,似乎硬盤(pán)沒(méi)有問(wèn)題,其實(shí)該盤(pán)的某些扇區(qū)已經(jīng)損壞,且該數(shù)據(jù)表恰恰使用了這些問(wèn)題扇區(qū)。問(wèn)題找到了,只能更換新的硬盤(pán),當(dāng)然,需要將所有MySQL數(shù)據(jù)導(dǎo)出保存到備用機(jī)。因?yàn)镸yISAM 引擎容易出問(wèn)題,最好還是使用InnoDB 引擎操作數(shù)據(jù)表。