事務實現,redo,undo,鎖

2022-03-11 20:39:56 字數 4454 閱讀 7570

事務(transaction)是資料庫區別於檔案系統的重要特性之一。在檔案系統中,如果你正在寫檔案,但是作業系統突然崩潰了,這個檔案就很有可能被破壞。當然,有一些機制可以把檔案恢復到某個時間點。不過,如果需要保證兩個檔案同步,這些檔案系統可能就顯得無能為力了。如當你需要更新兩個檔案時,更新完乙個檔案後,在更新完第二個檔案之前系統重啟了,你就會有兩個不同步的檔案。

這正是資料庫系統引入事務的主要目的:事務會把資料庫從一種一致狀態轉換為另一種一致狀態。在資料庫提交工作時,可以確保其要麼所有修改都已經儲存了,要麼所有修改都不儲存。

事務是資料庫區別於檔案系統的重要特性之一。事務用來保證資料庫的完整性——要麼都做修改,要麼都不做。

事務有嚴格的定義,它必須同時滿足四個特性。

原子性(atomicity):原子性是指整個資料庫事務是不可分割的工作單位。只有使事務中所有的資料庫操作執行都成功,才算整個事務成功。如果事務中任何乙個sql語句執行失敗,那麼已經執行成功的sql語句也必須撤銷,資料庫狀態應該退回到執行事務前的狀態。

隔離性(isolation):乙個事務的影響在該事務提交前對其他事務都不可見——這通過鎖來實現。

永續性(durability):事務一旦提交,其結果就是永久性的。即使發生宕機等故障,資料庫也能將資料恢復。

隔離性由鎖得以實現。原子性、一致性、永續性通過資料庫的redo和undo來完成。 

在innodb儲存引擎中,事務日誌通過重做(redo)日誌檔案和innodb儲存引擎的日誌緩衝(innodb log buffer)來實現。

當開始乙個事務時,會記錄該事務的乙個lsn(log sequence number,日誌序列號);

當事務執行時,會往innodb儲存引擎的日誌緩衝裡插入事務日誌;

當事務提交時,必須將innodb儲存引擎的日誌緩衝寫入磁碟(預設的實現,即innodb_flush_log_at_trx_commit=1)。也就是在寫資料前,需要先寫日誌。這種方式稱為預寫日誌方式(write-ahead logging,wal)。

innodb儲存引擎通過預寫日誌的方式來保證事務的完整性。這意味著磁碟上儲存的資料頁和記憶體緩衝池中的頁是不同步的,對於記憶體緩衝池中頁的修改,先是寫入重做日誌檔案,然後再寫入磁碟,因此是一種非同步的方式。可以通過命令show engine innodb status來觀察當前磁碟和日誌的「差距」:

create table z(a int,primary key(a)) engine=innodb;

create procedure load_test(count int)

begin

declare i int unsigned default 0;

start transaction;

while i<count do

insert into z select i;

set i=i+1;

end while;

commit;

end;

首先建立一張表z,然後建立乙個往表z中匯入資料的儲存過程load_test。通過命令show engine innodb status觀察當前的重做日誌情況:

show engine innodb status\g

log sequence number表示當前的lsn

log flushed up to表示重新整理到重做日誌檔案的lsn

last checkpoint at表示重新整理到磁碟的lsn。

因為當前沒有任何操作,所以這三者的值是一樣的。

接著開始匯入10 000條記錄:

call load_test(10000);

show engine innodb status\g

這次show engine innodb status的結果就不同了,log sequence number的lsn為113047672789,log flushed up to的lsn為113047672789,last checkpoint at的lsn為113047174608,可以把log flushed up to和last checkpoint at的差值498 181(~486.5k)理解為重做日誌產生的增量(以位元組為單位)。

雖然在上面的例子中,log sequence number和log flushed up to的值是相等的,但是在實際的生產環境中,該值有可能是不同的。因為在乙個事務中從日誌緩衝重新整理到重做日誌檔案,並不只是在事務提交時發生,每秒都會有從日誌緩衝重新整理到重做日誌檔案的動作。下面是乙個生產環境下重做日誌的資訊:

show engine innodb status\g

---log

---log sequence number 203318213447

log flushed up to 203318213326

last checkpoint at 203252831194

1 pending log writes,0 pending chkp writes

103447 log i/o's done,7.00 log i/o's/second

可以看到,在生產環境下log sequence number、log flushed up to、last checkpoint at三個值可能是不同的。

重做日誌記錄了事務的行為,可以很好地通過其進行「重做」。但是事務有時還需要撤銷,這時就需要undo。undo與redo正好相反,對於資料庫進行修改時,資料庫不但會產生redo,而且還會產生一定量的undo,即使你執行的事務或語句由於某種原因失敗了,或者如果你用一條rollback語句請求回滾,就可以利用這些undo資訊將資料回滾到修改之前的樣子。與redo不同的是,redo存放在重做日誌檔案中,undo存放在資料庫內部的乙個特殊段(segment)中,這稱為undo段(undo segment),undo段位於共享表空間內。可以通過py_innodb_page_info.py工具,來檢視當前共享表空間中undo的數量:

python py_innodb_page_info.py /usr/local/mysql/data/ibdata1

total number of page:46208:

insert buffer free list:13093

insert buffer bitmap:3

system page:5

transaction system page:1

freshly allocated page:4579

undo log page:2222

file segment inode:6

b-tree node:26296

file space header:1

擴充套件描述頁:2

可以看到,當前的共享表空間ibdata1內有2222個undo頁。

我們通常對於undo有這樣的誤解:undo用於將資料庫物理地恢復到執行語句或事務之前樣子——但事實並非如此。資料庫只是邏輯地恢復到原來的樣子,所有修改都被邏輯地取消,但是資料結構本身在回滾之後可能大不相同,因為在多使用者併發系統中,可能會有數

十、數百甚至數千個併發事務。資料庫的主要任務就是協調對於資料記錄的併發訪問。如乙個事務在修改當前乙個頁中某幾條記錄,但同時還有別的事務在對同乙個頁中另幾條記錄進行修改。因此,不能將乙個頁回滾到事務開始的樣子,因為這樣會影響其他事務正在進行的工作。

例如:我們的事務執行了乙個insert 10萬條記錄的sql語句,這條語句可能會導致分配乙個新的段,即表空間會增大。如果我們執行rollback時,會將插入的事務進行回滾,但是表空間的大小並不會因此而收縮。因此,當innodb儲存引擎回滾時,它實際上做的是與先前相反的工作。對於每個insert,innodb儲存引擎會完成乙個delete;對於每個delete,innodb儲存引擎會執行乙個insert;對於每個update,innodb儲存引擎則會執行乙個相反的update,將修改前的行放回去。

oracle和microsoft sql server資料庫都有內部的資料字典來觀察當前undo的資訊;innodb儲存引擎在這方面做得還是不夠的,所以dba只能通過原理和經驗來進行判斷。我寫過乙個補丁(patch)來擴充套件show engine innodb status命令的顯示結果,可以用來檢視當前記憶體緩衝池中undo頁的數量,如下**所示。

show engine innodb status\g

可以看到,當前記憶體緩衝中有1個undo頁。

接著我們開啟乙個事務,執行插入10萬條記錄的操作,需要注意的是,這並不進行提交操作:

create table t like order_line;

insert into t select * from order_line limit 100000;

之後在另乙個會話中執行命令show engine innodb status,可以看到之前的會話產生的undo量:

show engine innodb status\g

可以看到,此時undo頁的數量變成了129,也就是說,剛才的乙個事務大致產生了129個undo頁。另外,即使對insert的事務進行了提交,我們在一段時間內還是可以看到記憶體中有129個undo頁。這是因為,對於undo頁的**是在master thread中進行的,master thread也不是每次**所有的undo頁。

Javaweb 事務實現

為什麼需要事務 需要 有一張銀行賬戶表,a使用者給b使用者轉賬 a賬戶減少,b賬戶增加,但是a操作完之後斷電了?解決方案 a減少錢,但是不要立即修改資料表,b收到錢之後,同時修改資料表 什麼是事務 事務 transaction,一串行要發生的連續的操作。事務的特點 連續的操作要麼全部成功,要麼全部失...

Spring事務實現方式

1.一種是使用xml實現事務 開發基本不用 transactional的所有可選屬性 propagation 用於設定事務傳播屬性。該屬性型別為 propagation 列舉,預設值為 propagation.required。isolation 用於設定事務的隔離級別。該屬性型別為 isolati...

spring事務實現原理

如果你用過spring aop,那麼理解註解事務就十分簡單了。事務註解本質上實在事務方法加入乙個around切面,在方法開始前開始事務,在丟擲異常後回滾事務。使用簡單偽 可以簡單理解為 dowithtransaction catch exception ex spring transaction有各...