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