mysql中怎么防止数据丢失
前言
放假期間,突然接到老板電話(huà),說(shuō)系統(tǒng)出現(xiàn)了數(shù)據(jù)丟失問(wèn)題,猶如晴天霹靂,內(nèi)心慌得一批已經(jīng)準(zhǔn)備卷鋪蓋回家。
今天主要給大家分享mysql數(shù)據(jù)庫(kù)出現(xiàn)數(shù)據(jù)丟失的幾個(gè)場(chǎng)景以及解決數(shù)據(jù)丟失問(wèn)題的相應(yīng)手段,讓大家以后心里有譜,放假不慌。
一、事務(wù)級(jí)數(shù)據(jù)丟失
說(shuō)明:
1、批量操作數(shù)據(jù)時(shí),由于沒(méi)有添加事務(wù)控制,出現(xiàn)事務(wù)回滾時(shí),出現(xiàn)部分?jǐn)?shù)據(jù)操作成功,部分?jǐn)?shù)據(jù)操作失敗的問(wèn)題。
2、事務(wù)提交成功到數(shù)據(jù)還沒(méi)有刷寫(xiě)到磁盤(pán),出現(xiàn)數(shù)據(jù)庫(kù)宕機(jī)導(dǎo)致的數(shù)據(jù)丟失
范圍: 事務(wù)級(jí)
嚴(yán)重級(jí)別: 低
處理手段:
- 添加事務(wù)控制
- 開(kāi)啟redo log、undo log
- 采用強(qiáng)一致日志刷寫(xiě)策略
1、開(kāi)啟事務(wù)控制
示例:批量保存用戶(hù)信息
@Override@Transactional(rollbackFor = {Exception.class})public boolean saveBatch(Collection<User> entityList, int batchSize) {try {int size = entityList.size();int idxLimit = Math.min(batchSize, size);int i = 1;//保存單批提交的數(shù)據(jù)集合List<User> oneBatchList = new ArrayList<>();for(Iterator<User> var7 = entityList.iterator(); var7.hasNext(); ++i) {User element = var7.next();oneBatchList.add(element);if (i == idxLimit) {userMapper.insertBatchSomeColumn(oneBatchList);//每次提交后需要清空集合數(shù)據(jù)oneBatchList.clear();idxLimit = Math.min(idxLimit + batchSize, size);}}}catch (Exception e){log.error("saveBatch fail",e);return false;}return true;}2、開(kāi)啟redo log、undo log
mysql中只有InnoDB引擎支持事務(wù),而事務(wù)機(jī)制的實(shí)現(xiàn)離不開(kāi)redo log和undo log。
默認(rèn)情況下都是開(kāi)啟的,不需要主動(dòng)調(diào)整。但如果出現(xiàn)事務(wù)級(jí)別的數(shù)據(jù)丟失,就需要檢查相關(guān)配置。
- redo log:重做日志
- undo log:回滾日志
Redo Log(重做日志)是MySQL中非常重要的日志模塊。
官方解釋:重做日志是一種基于磁盤(pán)的數(shù)據(jù)結(jié)構(gòu),用于在崩潰恢復(fù)期間糾正不完整事務(wù)寫(xiě)入的數(shù)據(jù)。
在正常操作期間,重做日志對(duì)由SQL語(yǔ)句或低級(jí)API調(diào)用產(chǎn)生的更改表數(shù)據(jù)的請(qǐng)求進(jìn)行編碼。在初始化期間和接受連接之前,會(huì)自動(dòng)重播在意外關(guān)閉之前未完成更新數(shù)據(jù)文件的修改).
MySQL里經(jīng)常說(shuō)到的WAL技術(shù)(WAL的全稱(chēng)是Write-Ahead Logging),它的關(guān)鍵點(diǎn)就是日志先行(也稱(chēng)為寫(xiě)前日志,實(shí)際寫(xiě)數(shù)據(jù)之前,先把修改的數(shù)據(jù)記錄到日志文之間中。即先寫(xiě)日志,再寫(xiě)磁盤(pán)),其實(shí)很多數(shù)據(jù)庫(kù)軟件設(shè)計(jì)的理念都是日志先行。(但是Redis的AOF(Append Only File)日志正好相反,它是寫(xiě)后日志,“寫(xiě)后”的意思是Redis的先執(zhí)行命令,把數(shù)據(jù)寫(xiě)入內(nèi)存,然后才記錄日志),MySQL中日志先行的這個(gè)“日志”就是Redo Log。
Redo Log是InnoDB引擎特有的日志,而MySQL Server層也有自己的日志,稱(chēng)為binlog(不在我們本文的討論范圍,下一章我們就會(huì)見(jiàn)到它了,拭目以待吧)。正是因?yàn)橛辛薘edo Log,才保證了InnoDB存儲(chǔ)引擎的Crash-safe能力。
Redo Log是物理日志,記錄的是“在某個(gè)數(shù)據(jù)頁(yè)上做了什么修改(做了什么改動(dòng))”。一句話(huà)概括一下,Redo Log是為了保證已提交事務(wù)的ACID特性,同時(shí)能夠提高數(shù)據(jù)庫(kù)性能的技術(shù)。
3、Redo log刷寫(xiě)策略
我們都知道,數(shù)據(jù)只有刷寫(xiě)到磁盤(pán)中才是最安全的,redo log從內(nèi)存到刷寫(xiě)到磁盤(pán)經(jīng)歷了三層結(jié)構(gòu)。
Redo Log的三層結(jié)構(gòu):
簡(jiǎn)單來(lái)說(shuō)一下Redo Log的三層結(jié)構(gòu):
- 粉色部分:是InnoDB一項(xiàng)很重要的內(nèi)存結(jié)構(gòu)(In-Memory Structure),即我們的Log Buffer(日志緩沖區(qū)),這一層,是MySQL應(yīng)用程序用戶(hù)態(tài)控制。
- 黃色部分:操作系統(tǒng)文件系統(tǒng)的緩沖區(qū)(FS Page Cache),這一層,是操作系統(tǒng)OS內(nèi)核態(tài)控制。
- 綠色部分:就是落盤(pán)的物理日志文件。
Redo Log刷寫(xiě)時(shí)機(jī):
Redo log目前有三種刷寫(xiě)策略,即對(duì)應(yīng)可設(shè)置的值可以是0、1或2。
- 策略一:最佳性能(innodb_flush_log_at_trx_commit=0)
處理過(guò)程:每隔一秒,才將Log Buffer中的數(shù)據(jù)批量write入FS Page Cache,同時(shí)MySQL主動(dòng)fsync。
缺點(diǎn):這種策略,如果數(shù)據(jù)庫(kù)奔潰,有一秒的數(shù)據(jù)丟失。 - 策略二:強(qiáng)一致(innodb_flush_log_at_trx_commit=1)
處理過(guò)程:每次事務(wù)提交,都將Log Buffer中的數(shù)據(jù)write入FS Page Cache,同時(shí)MySQL主動(dòng)fsync。這種策略,是InnoDB的默認(rèn)配置,為的是保證事務(wù)ACID特性。
缺點(diǎn):這種策略,性能較其余兩種策略較差。 - 策略三:折衷(innodb_flush_log_at_trx_commit=2)
處理過(guò)程:每次事務(wù)提交,都將Log Buffer中的數(shù)據(jù)write入FS Page Cache;每隔一秒,MySQL主動(dòng)將FS Page Cache中的數(shù)據(jù)批量fsync。
缺點(diǎn):這種策略,如果操作系統(tǒng)奔潰,最多有一秒的數(shù)據(jù)丟失。(因?yàn)镺S也會(huì)fsync,MySQL主動(dòng)fsync的周期是一秒,所以最多丟一秒數(shù)據(jù)。磁盤(pán)IO次數(shù)不確定,因?yàn)椴僮飨到y(tǒng)的fsync頻率并不是MySQL能控制的)
綜上,為了防止事務(wù)級(jí)數(shù)據(jù)丟失,必須設(shè)置redo log的刷寫(xiě)級(jí)別為強(qiáng)一致(innodb_flush_log_at_trx_commit=1)
二、page級(jí)數(shù)據(jù)丟失
說(shuō)明: 由于mysql數(shù)據(jù)頁(yè)page損壞出現(xiàn)的數(shù)據(jù)丟失
范圍: page級(jí)
嚴(yán)重級(jí)別: 中
處理手段:
- 開(kāi)啟Doublewrite Buffer,默認(rèn)開(kāi)啟
Linux文件系統(tǒng)頁(yè)(OS Page)的大小是4KB,MySQL的頁(yè)(Page)大小默認(rèn)是16KB,
所以MySQL將Buffer Pool中一頁(yè)數(shù)據(jù)刷入磁盤(pán),要寫(xiě)4個(gè)文件系統(tǒng)里的頁(yè)(也可以說(shuō)成一個(gè)MySQL數(shù)據(jù)頁(yè)映射4個(gè)系統(tǒng)頁(yè))。
如上圖所示,MySQL里Page 1的頁(yè),物理上對(duì)應(yīng)磁盤(pán)的Page 1、Page 2、Page 3、Page 4四個(gè)頁(yè)。
這個(gè)操作并非原子,如果執(zhí)行到一半斷電,會(huì)不會(huì)出現(xiàn)問(wèn)題呢?
答案:會(huì),這就是所謂的“頁(yè)數(shù)據(jù)損壞”。
MySQL內(nèi)Page 1的頁(yè)準(zhǔn)備刷入磁盤(pán),才刷了3個(gè)文件系統(tǒng)里的頁(yè),掉電了,則會(huì)出現(xiàn):重啟后,MySQL內(nèi)Page 1的頁(yè),物理上對(duì)應(yīng)磁盤(pán)上的Page 1、Page 2、Page 3三個(gè)頁(yè),數(shù)據(jù)完整性被破壞。(Redo Log無(wú)法修復(fù)這類(lèi)“頁(yè)數(shù)據(jù)損壞”的異常,因?yàn)镽edo Log修復(fù)的前提是“頁(yè)數(shù)據(jù)正確”并且Redo日志正常)
針對(duì)上面出現(xiàn)的情況,如何解決這類(lèi)“頁(yè)數(shù)據(jù)損壞”的問(wèn)題呢?
很容易想到的方法是,能有一個(gè)“副本”,對(duì)原來(lái)的頁(yè)進(jìn)行還原,這個(gè)存儲(chǔ)“副本”的地方,就是Doublewrite Buffer。Doublewrite Buffer,它與傳統(tǒng)的“Buffer”又不同,它分為內(nèi)存和磁盤(pán)的兩層架構(gòu)。(傳統(tǒng)的“Buffer”,大部分是內(nèi)存存儲(chǔ);而DWB里的數(shù)據(jù),是需要落地的)
Doublewrite Buffer工作流程:
如上圖所示,當(dāng)有頁(yè)數(shù)據(jù)要刷盤(pán)時(shí):
- 第1步:頁(yè)數(shù)據(jù)先memcopy到DWB的內(nèi)存里;
- 第2步:DWB的內(nèi)存里的數(shù)據(jù)頁(yè),會(huì)先刷到DWB的磁盤(pán)上;
- 第3步:DWB的內(nèi)存里的數(shù)據(jù)頁(yè),再刷到數(shù)據(jù)磁盤(pán)存儲(chǔ).ibd文件上;
備注:DWB內(nèi)存結(jié)構(gòu)由128個(gè)頁(yè)(Page)構(gòu)成,所以容量只有:16KB × 128 = 2MB。
DWB為什么能解決“頁(yè)數(shù)據(jù)損壞”問(wèn)題呢?
假設(shè)步驟2掉電,磁盤(pán)里依然是Page 1、Page 2、Page 3、Page 4的完整數(shù)據(jù)。只要有頁(yè)數(shù)據(jù)完整,就能通過(guò)Redo Log還原數(shù)據(jù);假如步驟3掉電,DWB磁盤(pán)結(jié)構(gòu)里存儲(chǔ)著完整的數(shù)據(jù)。所以,一定不會(huì)出現(xiàn)“頁(yè)數(shù)據(jù)損壞”問(wèn)題。同時(shí)寫(xiě)了DWB磁盤(pán)和Data File,總有一個(gè)地方的數(shù)據(jù)是OK的。
是否開(kāi)啟DoubleWrite
SHOW VARIABLES LIKE 'innodb_doublewrite';三、磁盤(pán)級(jí)數(shù)據(jù)丟失
說(shuō)明: 磁盤(pán)損壞導(dǎo)致的數(shù)據(jù)丟失
范圍: 磁盤(pán)級(jí)
嚴(yán)重級(jí)別: 嚴(yán)重
處理手段:
- 磁盤(pán)矩陣
- 數(shù)據(jù)庫(kù)集群
- 冷熱備份
不要以為數(shù)據(jù)正確完整的寫(xiě)到磁盤(pán)上就萬(wàn)事大吉,在程序整體架構(gòu)的考慮中,除了軟件層面的問(wèn)題,必須還要考慮硬件層面的風(fēng)險(xiǎn)。
比如,由于Mysql存儲(chǔ)數(shù)據(jù)的磁盤(pán)由于故障損壞了,造成了整個(gè)系統(tǒng)的數(shù)據(jù)丟失,這樣的情況將使災(zāi)難性的。那么我們?cè)撊绾螒?yīng)對(duì)磁盤(pán)級(jí)的數(shù)據(jù)丟失風(fēng)險(xiǎn)呢?其核心就是就好數(shù)據(jù)備份。
磁盤(pán)矩陣
核心是利用多塊磁盤(pán)的冗余存儲(chǔ),保障即是一塊磁盤(pán)損壞也不會(huì)出現(xiàn)數(shù)據(jù)丟失。
RAID1 是磁盤(pán)陣列中單位成本最高的一種方式。因?yàn)樗脑硎窃谕疟P(pán)寫(xiě)數(shù)據(jù)的時(shí)候,將同一份數(shù)據(jù)無(wú)差別的寫(xiě)兩份到磁盤(pán),分別寫(xiě)到工作磁盤(pán)和鏡像磁盤(pán),那么它的實(shí)際空間使用率只有50%了,兩塊磁盤(pán)當(dāng)做一塊用,這是一種比較昂貴的方案。
優(yōu)點(diǎn):是簡(jiǎn)單,一般做硬件運(yùn)維的同事都能操作。
缺點(diǎn):成本較高,備份數(shù)據(jù)的精讀較粗,一般是整個(gè)服務(wù)器級(jí)別,往往是財(cái)大氣粗的政府機(jī)關(guān)和銀行在使用。另外不能做到遠(yuǎn)程宰備,如果整機(jī)房都出了問(wèn)題,往往還是會(huì)出現(xiàn)數(shù)據(jù)丟失。
數(shù)據(jù)庫(kù)集群備份Replication
通過(guò)搭建mysql數(shù)據(jù)庫(kù)集群,讓集群中每臺(tái)服務(wù)器保存完整的數(shù)據(jù)庫(kù)信息,這樣即使一臺(tái)服務(wù)器上的數(shù)據(jù)出現(xiàn)了丟失,也能從備份的數(shù)據(jù)庫(kù)中找回。
而且mysql的主備數(shù)據(jù)庫(kù)可以實(shí)現(xiàn)無(wú)感知的切換,宅難恢復(fù)的速度更快。
而且通過(guò)主備數(shù)據(jù)庫(kù),也可以方便實(shí)現(xiàn)數(shù)據(jù)庫(kù)的讀寫(xiě)分離,緩解單臺(tái)數(shù)據(jù)庫(kù)上的讀寫(xiě)壓力。
是目前最主流的數(shù)據(jù)庫(kù)災(zāi)備方式。
優(yōu)點(diǎn):程序級(jí)別的數(shù)據(jù)備份,更節(jié)省成本,能較方便的實(shí)現(xiàn)遠(yuǎn)程備份,宅難恢復(fù)更快。
缺點(diǎn):如果是程序勿操作導(dǎo)致的數(shù)據(jù)丟失,從庫(kù)也會(huì)執(zhí)行相同操作,造成數(shù)據(jù)無(wú)法找回。
冷熱備份
MySQL備份功能在實(shí)際應(yīng)用中的重要程度不需要多說(shuō),在誤刪重要數(shù)據(jù)后或者數(shù)據(jù)庫(kù)被攻擊后,備份數(shù)據(jù)的作用就突顯出來(lái)了。
MySQL備份方式從不同的角度分析有不同的分類(lèi)。 從運(yùn)行狀態(tài)分析,有冷備份和熱備份之分。冷備份一般是在數(shù)據(jù)庫(kù)關(guān)閉或者暫時(shí)不對(duì)外提供服務(wù)時(shí),選擇某一時(shí)間節(jié)點(diǎn)對(duì)完整數(shù)據(jù)庫(kù)進(jìn)行快照備份。熱備份一般是在數(shù)據(jù)庫(kù)運(yùn)行的狀態(tài)下,直接對(duì)數(shù)據(jù)進(jìn)行備份,不影響MySQL對(duì)外提供服務(wù)。這里主要講下這兩種方式的實(shí)現(xiàn)。
直接通過(guò)MySQL自帶的mysqldump工具進(jìn)行備份
-- 數(shù)據(jù)備份 mysqldump -uroot -proot test > /data/backup/test.sql -- 數(shù)據(jù)恢復(fù) mysql -f -uroot -proot test < /data/backup/test.sql一般將備份的數(shù)據(jù)文件存放在其他服務(wù)器上。
總結(jié)
本文主要對(duì)mysql數(shù)據(jù)庫(kù)數(shù)據(jù)丟失的幾種常見(jiàn)場(chǎng)景進(jìn)行了介紹。作為程序開(kāi)發(fā)者可能只需要注意程序開(kāi)發(fā)中過(guò)程中事務(wù)的使用規(guī)范,但是如果作為項(xiàng)目負(fù)責(zé)人、架構(gòu)師等視角來(lái)看,必須把數(shù)據(jù)庫(kù)運(yùn)維的問(wèn)題也考慮在內(nèi),做到軟硬件風(fēng)險(xiǎn)全盤(pán)考慮,防止出現(xiàn)重大事故。
- 事務(wù)級(jí)數(shù)據(jù)丟失:添加事務(wù)控制,開(kāi)啟Redo log并采用強(qiáng)一致(innodb_flush_log_at_trx_commit=1)刷寫(xiě)機(jī)制。
- 數(shù)據(jù)頁(yè)損壞級(jí)別的數(shù)據(jù)丟失:數(shù)據(jù)雙寫(xiě)機(jī)制double wirte
- 如何防止磁盤(pán)級(jí)別的數(shù)據(jù)丟失:磁盤(pán)矩陣,數(shù)據(jù)庫(kù)集群,數(shù)據(jù)庫(kù)冷熱備份。
MySQL各種“Buffer”之Doublewrite Buffer
MySQL各種“Buffer”之Log Buffer
MySQL的冷熱備份
總結(jié)
以上是生活随笔為你收集整理的mysql中怎么防止数据丢失的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 百度云离线下载含有违规内容检测方法分析
- 下一篇: Redis集群之脑裂:一次奇怪的数据丢失