大事务带来的问题
說(shuō)到大事務(wù),首先我們要說(shuō)一說(shuō)什么是事務(wù),有兩句話(huà)可以簡(jiǎn)單地描述事務(wù),首先事務(wù)是關(guān)系型數(shù)據(jù)庫(kù)區(qū)分于其他一切文件系統(tǒng)的重要特性之一,比如舉個(gè)例子來(lái)說(shuō),舉個(gè)例子來(lái)說(shuō),這個(gè)文件系統(tǒng)中,如果要保證文件的一致性,而在我們修改一個(gè)文件后,系統(tǒng)突然奔潰了,這樣文件恢復(fù)后很難保證文件的一致了,而數(shù)據(jù)庫(kù)系統(tǒng)中呢,由于使用了事務(wù),所以通常在數(shù)據(jù)庫(kù)服務(wù)器奔潰了以后,我們可以恢復(fù)數(shù)據(jù)庫(kù)的數(shù)據(jù),使其數(shù)據(jù)還是保證一致性,另一個(gè)對(duì)事務(wù)的描述呢,事務(wù)是一組具有原子性的SQL語(yǔ)句,可以是一組簡(jiǎn)單的查詢(xún),也可以是有多個(gè)增刪改查鎖組成的一組SQL語(yǔ)句的集合,但是這一組是具有原子性的,也就是說(shuō),事務(wù)中的SQL要么全部完成,要么全部失敗,從以上的描述我們就可以看出
事務(wù)是要求符合以下特性的,事務(wù)要符合原子性,一致性,隔離性,持久性,下面我們可以分別來(lái)看一下事務(wù)的四個(gè)特性
首先我們來(lái)看看事務(wù)的原子性,一個(gè)事務(wù)呢必須被視為一個(gè)不可分割的最小單元,整個(gè)事務(wù)中的所有操作呢,要么全部成功,要么全部失敗,對(duì)于一個(gè)事務(wù)來(lái)說(shuō),不可能只執(zhí)行其中的一部分,這就是事務(wù)的原子性的定義,在通常情況下,說(shuō)到事務(wù)的原子性
在通常的情況下說(shuō)到事務(wù)的原子性,經(jīng)常會(huì)舉銀行的例子,現(xiàn)在我們也用銀行的例子來(lái)說(shuō)明一下事務(wù)原子性的問(wèn)題,現(xiàn)在銀行卡常常是有多個(gè)用戶(hù)的,比如大多數(shù)有一個(gè)理賠賬戶(hù),活期存款賬戶(hù),如果我們想從理賠賬戶(hù)中轉(zhuǎn)出兩千元到銀行存款賬號(hào),通常我們要進(jìn)行以下幾步操作
通常我們要進(jìn)行以下幾步操作,第一步是檢查理財(cái)賬號(hào)的余額是否高于2000,第二步是從理財(cái)賬戶(hù)中減去2000元,第三步從活期存款賬戶(hù)上增加2000元,以上的三個(gè)步驟要作為一個(gè)整體一起來(lái)完成,要是我們執(zhí)行到第二步系統(tǒng)就奔潰了,如果沒(méi)有事務(wù)原子性,會(huì)發(fā)生什么呢,毫無(wú)疑問(wèn),我們就會(huì)損失2000元,相信這是所有用戶(hù)都不能接受的,如果以上三個(gè)步驟都在一個(gè)事務(wù)中,當(dāng)執(zhí)行到第二步系統(tǒng)奔潰了,在系統(tǒng)恢復(fù)后就會(huì)發(fā)現(xiàn)有沒(méi)有提交的事務(wù),這個(gè)時(shí)候就會(huì)對(duì)操作的事務(wù)回滾
這樣就避免了用戶(hù)的損失,所以事務(wù)的首先的一個(gè)特性呢,要具有原子性,保證事務(wù)的處理過(guò)程中要么全部完成,要么就全部回滾失敗
事務(wù)的第二個(gè)特性就是事務(wù)的一致性,其定義是這樣的,一致性就是事務(wù)將數(shù)據(jù)庫(kù)從一種一致性狀態(tài)轉(zhuǎn)換到另外一種一致性狀態(tài),在事務(wù)的開(kāi)始之前和事務(wù)的結(jié)束之后,數(shù)據(jù)庫(kù)的完整性不應(yīng)該被破壞,概念總是很難以理解,我們還是以上面的銀行轉(zhuǎn)賬的例子來(lái)說(shuō),就是在轉(zhuǎn)賬之前和轉(zhuǎn)賬之后,我們賬戶(hù)的總金額不應(yīng)該有任何的變化,轉(zhuǎn)賬之前在我們的銀行賬號(hào)中,轉(zhuǎn)賬之后余額到活期賬戶(hù)中,但是賬戶(hù)總金額是不應(yīng)該有任何變化的,這個(gè)看似是理所當(dāng)然的事情了,但是在數(shù)據(jù)庫(kù)的文件系統(tǒng)中呢,這一步是很難做到的
那我們接下來(lái)看看事務(wù)的第三個(gè)特性,事務(wù)的隔離性,所謂的隔離性呢,就是要求一個(gè)事務(wù)對(duì)數(shù)據(jù)庫(kù)的數(shù)據(jù),進(jìn)行修改,在未提交完成之前,對(duì)于其他事務(wù)是不可見(jiàn)的,同樣的我們還是以上面的存款例子來(lái)說(shuō)吧,當(dāng)我們執(zhí)行完第二步,在理財(cái)賬戶(hù)中減去2000元之后,如果這個(gè)時(shí)候另外一個(gè)事務(wù),需要對(duì)所有理財(cái)賬號(hào)的余額呢,進(jìn)行匯總,這個(gè)時(shí)候事務(wù)還能夠在我們的理財(cái)賬戶(hù)中看到我們理財(cái)賬號(hào)的兩千塊錢(qián),這是由于我們第一個(gè)事務(wù)減去兩千塊錢(qián),加上事務(wù)還沒(méi)有提交,所以對(duì)于第二個(gè)事務(wù)來(lái)說(shuō)呢,應(yīng)該是不可見(jiàn)的,這就是事務(wù)的隔離性,在SQL的標(biāo)準(zhǔn)中呢,定義了四種隔離性
下面我們依次來(lái)看一下隔離性都有哪些,首先第一種隔離性呢,就是未提交讀,所謂的未提交讀呢,就是在未提交讀的事務(wù)隔離級(jí)別中,事務(wù)對(duì)數(shù)據(jù)進(jìn)行了修改,即使事務(wù)還沒(méi)有被提交,所以其他事務(wù)也是可見(jiàn)的,事務(wù)可以讀取未提交的數(shù)據(jù),也就是被稱(chēng)之為臟讀,未提交的數(shù)據(jù)我們也稱(chēng)之為臟數(shù)據(jù),之前我在使用SQL Server時(shí),經(jīng)常會(huì)建議,在進(jìn)行查詢(xún)的時(shí)候,在老板的SQL中呢,支持非鎖定讀的,所以也是一種沒(méi)有辦法的事情,當(dāng)然在新的SQL中呢,有辦法實(shí)現(xiàn)SQL的讀了,所以在通常的情況下,是不建議使用臟讀隔離級(jí)別的,因?yàn)榭赡軙?huì)造成很多邏輯上的問(wèn)題,隔離級(jí)別稱(chēng)之為已提交讀,相信大家對(duì)于已提交讀已經(jīng)很熟悉了,因?yàn)檫@是大多數(shù)數(shù)據(jù)庫(kù)默認(rèn)的隔離級(jí)別,比如ORACLE,SQL Server,他們隔離事務(wù)都是隔離讀,MYSQL是一個(gè)例外,已提交讀滿(mǎn)足之前的定義,一個(gè)事務(wù)開(kāi)始時(shí),只能看到已提交事務(wù)所做的更改,或者一個(gè)事務(wù)從開(kāi)始到提交前,對(duì)數(shù)據(jù)所做的修改,與其他事務(wù)來(lái)說(shuō)是不可見(jiàn)的,這就是已提交讀,那事務(wù)隔離級(jí)別第三個(gè)就是可重復(fù)讀,該級(jí)別保證了在同一個(gè)事務(wù)中,讀取的結(jié)果是一致的,相比可重復(fù)讀的隔離級(jí)別呢,可提交是不可重復(fù)讀,那么這兩個(gè)級(jí)別到底有什么區(qū)別呢,我們可以通過(guò)一個(gè)簡(jiǎn)單的例子來(lái)演示一下,那現(xiàn)在就進(jìn)入到演示的系統(tǒng)
首先我們可以有兩個(gè)連接,我們?cè)跀?shù)據(jù)庫(kù)中已經(jīng)建立了這樣一張表,你們可以先看一看,這張表很簡(jiǎn)單,就是一張序列表
他具有這么幾個(gè)數(shù)據(jù),現(xiàn)在我們看一下這個(gè)事務(wù)的隔離級(jí)別是什么,大家可以看到這個(gè)事務(wù)隔離級(jí)別是可重復(fù)讀
那么這個(gè)時(shí)候我們啟動(dòng)一個(gè)事務(wù),begin;查詢(xún)所有id小于7的數(shù)字
同時(shí)我們?cè)诘诙€(gè)進(jìn)程中,同樣我們進(jìn)入到這個(gè)數(shù)據(jù)庫(kù)中,我們會(huì)看到這個(gè)表,在這里同樣啟動(dòng)一個(gè)事務(wù),這個(gè)時(shí)候我們對(duì)這個(gè)表t進(jìn)行一個(gè)插入,我們插入2這個(gè)值,對(duì)這個(gè)事務(wù)進(jìn)行提交
我們回到第一個(gè)連接,同樣我們執(zhí)行上面這個(gè)查詢(xún),同樣看到的還是這三個(gè)數(shù)字,看不到已經(jīng)提交的數(shù)字2,如果我們把當(dāng)前的事務(wù)級(jí)別給改一下,比如我們把當(dāng)前的事務(wù)終止掉,設(shè)置一下我們的事務(wù)隔離級(jí)別
設(shè)置成讀已提交,我們看一下剛才的會(huì)變成什么樣子,現(xiàn)在我們已經(jīng)把事務(wù)設(shè)置成讀已提交,事務(wù)隔離級(jí)別已經(jīng)讀已提交了
同樣我們進(jìn)行剛才的實(shí)驗(yàn),同樣我們選出id小于7的,因?yàn)閯偛乓呀?jīng)插入了一個(gè)2,當(dāng)然我們啟動(dòng)了事務(wù)當(dāng)然是可以看到2的
同樣我們?cè)谶B接2中,再插入另外一個(gè)數(shù)字,咱們?cè)倏匆幌?比如再啟動(dòng)一個(gè)事務(wù),我們可以插入4,這個(gè)是在表里沒(méi)有的,插入一個(gè)4,提交這個(gè)事務(wù)
那么我們回到第一個(gè)連接,同樣查詢(xún)id小于7的數(shù)字,那我們就可以看到id為4的數(shù)字了
但是這個(gè)事務(wù)我們還沒(méi)有給提交,所以在讀已提交的情況下呢,我們是不可重復(fù)讀的,因?yàn)樵谕皇聞?wù)中,我們兩個(gè)執(zhí)行相同的SQL,結(jié)果是不一樣的,而在可重復(fù)讀中,雖然同樣提交了一個(gè)事務(wù),但是我們同時(shí)注冊(cè)了一個(gè)結(jié)果呢,是一致的,稱(chēng)之為可重復(fù)讀,這就是可重復(fù)讀和已提交讀的差別,來(lái)看看第四個(gè)隔離級(jí)別,第四個(gè)隔離級(jí)別就是串行化,串行化是最高的隔離級(jí)別,簡(jiǎn)單來(lái)說(shuō)呢,串行化是在讀取的每一行數(shù)據(jù)上都加鎖,所以可能導(dǎo)致大量的鎖超時(shí)和鎖占用問(wèn)題,所以我們?cè)趯?shí)際業(yè)務(wù)中呢,我們很少使用這個(gè)隔離級(jí)別,除非要求嚴(yán)格數(shù)據(jù)一致性,并且是在可以接收并發(fā)前提下,我們才會(huì)考慮使用這種隔離級(jí)別,從上面的介紹我們不難看出,對(duì)于系統(tǒng)隔離級(jí)別,從隔離性來(lái)說(shuō),分別是未提交讀,已提交讀,可重復(fù)讀,和可串行化讀,而并發(fā)性來(lái)說(shuō)呢,跟隔離性來(lái)說(shuō)正好是相反的,并發(fā)性的由高到低排列呢,應(yīng)該是未提交讀的并發(fā)性是最高的,可串行化的并發(fā)性是最低的,大家在我們正常的實(shí)際工作中呢,選擇不同的隔離級(jí)別,而對(duì)于InnoDB來(lái)說(shuō)呢,默認(rèn)的隔離級(jí)別呢是可重復(fù)讀,而不是讀已提交
來(lái)看事務(wù)的最后一個(gè)特性,也就是持久性,一旦事務(wù)提交,就永久的保存在數(shù)據(jù)庫(kù)中,此時(shí)即使系統(tǒng)奔潰,已提交的修改也不會(huì)被丟失,這里說(shuō)的持久性是相對(duì)來(lái)說(shuō)的,只能從數(shù)據(jù)庫(kù)的角度來(lái)說(shuō),事務(wù)的持久性,而不包括一些外部因素,如磁盤(pán)損壞情況,就不是這里事務(wù)的持久性的問(wèn)題,要真正保證在磁盤(pán)損壞的情況下,不丟失數(shù)據(jù)的話(huà),那么只有靠數(shù)據(jù)的備份,或者DB的復(fù)制高可用的架構(gòu),才能做得到,那么了解了事務(wù)之后呢
來(lái)看看什么是大事務(wù),就是運(yùn)行時(shí)間比較長(zhǎng),操作數(shù)據(jù)比較多的事務(wù),舉個(gè)例子來(lái)說(shuō),相信大家都接觸過(guò)余額寶這樣的理財(cái)產(chǎn)品,這種產(chǎn)品的一個(gè)特點(diǎn)呢,就是每天都會(huì)計(jì)算用戶(hù)前一天的理財(cái)收入所得,而如果我們?cè)谝粋€(gè)事務(wù)中,所有的理財(cái)事務(wù)都進(jìn)行計(jì)算,并更新到用戶(hù)的余額中,這數(shù)以?xún)|計(jì)的用戶(hù)的更新可能要幾個(gè)小時(shí),而且一旦中間出現(xiàn)任何的問(wèn)題,會(huì)進(jìn)行回滾,事務(wù)所需要的時(shí)間就更加的不可估量,更新的過(guò)程中會(huì)對(duì)相關(guān)的用戶(hù)進(jìn)行加鎖,造成用戶(hù)不能使用余額相關(guān)的問(wèn)題,像這樣的事務(wù)就稱(chēng)之為大事務(wù),當(dāng)余額寶的計(jì)算和更新,并不是在一個(gè)事務(wù)中來(lái)完成了,所以我們也沒(méi)有遇到像上面的這種情況,但是從上面的例子中,我們不難看出,大事務(wù)的影響主要有以下幾個(gè),首先第一個(gè)是鎖定太多的數(shù)據(jù),造成大量的阻塞和鎖超時(shí),對(duì)于InnoDB的這種事務(wù)引擎來(lái)說(shuō),雖然使用的是行級(jí)鎖,但是在一個(gè)事務(wù)中,為了保證事務(wù)的一致性,通常會(huì)講所有相關(guān)的應(yīng)用都會(huì)加鎖,如果我們涉及到的數(shù)據(jù)比較多,比如下面的例子中幾乎會(huì)涉及到所有用戶(hù)記錄,這樣就會(huì)把所有的記錄全部鎖住,如果有用戶(hù)要使用余額寶支付,這個(gè)時(shí)候就會(huì)產(chǎn)生阻塞,在并發(fā)比較大的情況下呢,直接使用數(shù)據(jù)庫(kù)的服務(wù)器呢,被大量的連接所占滿(mǎn),同時(shí)會(huì)嚴(yán)重的影響數(shù)據(jù)庫(kù)的性能和穩(wěn)定性,而大事務(wù)給我們帶來(lái)的第二個(gè)風(fēng)險(xiǎn)呢,回滾所需要的時(shí)間比較長(zhǎng),并且在回滾中,所有對(duì)鎖定的數(shù)據(jù)仍然被鎖定,那可能回滾所需的時(shí)間可能比我們之前的事務(wù)花的時(shí)間還要長(zhǎng),大事務(wù)給我們帶來(lái)的第三個(gè)就是,容易造成主從延遲,從MYSQL主從復(fù)制來(lái)看,只有在主服務(wù)器執(zhí)行完之后,把日志寫(xiě)入到log中,這個(gè)時(shí)候從服務(wù)器才能通過(guò)log進(jìn)行同步,大家可以想一下,如果主上的事務(wù)執(zhí)行了幾個(gè)小時(shí)后再提交,并寫(xiě)入bin.log,是不是主從之間會(huì)延遲幾個(gè)小時(shí)呢,以上就是大事務(wù)給我們帶來(lái)的幾個(gè)大的問(wèn)題,當(dāng)然實(shí)際上還遠(yuǎn)遠(yuǎn)不止這些,不過(guò)這幾個(gè)風(fēng)險(xiǎn)是比較重要的而已,那么我們要怎么處理大事務(wù)呢
看看如何大事務(wù)進(jìn)行處理,首先避免一次性處理太多的數(shù)據(jù),對(duì)于一次要處理幾百上千的操作呢,我們可以采取分批處理的方法,就比如操作是增刪改查這樣的操作,比如上面的例子中,每天處理的用戶(hù)理財(cái)收入的例子,我們可以分成1萬(wàn)用戶(hù)一批,處理完一批之后再處理另一批,這樣就大大減少事務(wù)的大小,避免了上面的種種問(wèn)題,要把不必要的事務(wù)移出,在平時(shí)的工作中啊,多數(shù)是使用了查詢(xún)的這種操作,而這些查詢(xún)操作呢,本身是可以不在事務(wù)中完成的,對(duì)于這種情況呢,要把查詢(xún)從事務(wù)中移出去,保證事務(wù)中只有必要的寫(xiě)操作,如果可以做到以下兩點(diǎn)呢,我們就可以基本上避免了大事務(wù)的產(chǎn)生了
直觀(guān)的展示了數(shù)據(jù)庫(kù)在繁忙時(shí)的系統(tǒng)狀態(tài),并且簡(jiǎn)單了解了對(duì)性能有影響的一些因素,比如大表大事務(wù)等等,接下來(lái)會(huì)詳細(xì)分析對(duì)性能影響的因素,如果對(duì)MYSQL進(jìn)行優(yōu)化,希望聽(tīng)完完整的內(nèi)容之后呢,可以完成對(duì)MYSQL的性能優(yōu)化,成為其中的高手
?
總結(jié)