- 套話
一般來說數據庫事務需要滿足ACID,A(atomicity)原子性,一個事務必須保證要麼都做,要麼都不做;C(consistency)一致性,一個事務開始和結束數據庫的完整性約束沒有被破壞;I(isolation)隔離性,各個事務之間相互分離彼此不干擾;D(durability)持久性,一旦事務結束,數據結果就是永久性的,不能因爲宕機之類的數據不可恢復。
- 事務實現
mysql實現隔離性是通過鎖,實現原子性和持久性通過redo log來實現,實現一致性是通過undo log來實現。
1)redo log,數據庫運行時不需要隨機讀取redo log文件,其基本都是順序寫,分爲redo log buffer和redo log file,另外將redo log buffer寫入redo log file時首先進入的是文件系統緩存,需要調用fsync才能寫入,但是這個過程很耗時,可以使用參數innodb_flush_log_at_trx_commit來控制刷新策略,默認1事務提交時必調一次,0不進行調用可能還在redo log buffer中,依靠master thread調用,2全部寫入文件系統緩存而不fsync。
redo log 被設計成和磁盤扇區大小一樣爲512字節一塊,這樣可以不用doublewrite技術,每個塊中有三部分組成,塊頭12字節,日誌尾8字節,留給redo log只有492。
log group是針對redo log file而言,一個組可以有多個redo log file,其中保存的也是512的塊結構一樣,每個redo log file前2kb空間存放的是其信息而不是redo log。
當事務提交後怎麼進行修改的生效呢,根據lsn(log sequence number),在重做日誌中存入lsn,每次有事務寫入重做日誌,lsn就會加上寫入字節大小,每個頁的頭部有一個值fil_page_lsn記錄了最後刷新時的lsn,當其小於重做日誌中的lsn時,並且事務提交,證明需要應用修改,每次數據庫啓動時都會讀取重做日誌,比較lsn看看是否需要進行恢復。
2)undo log
undo log是存儲在段中的,共享表空間偏移量爲5的頁記錄了所有的rollback segment header所在頁,每個rollback segment中記錄了1024個undo log段,參數innodb_undo_directory用於設置rollback segment文件所在位置,默認在共享表空間,參數innodb_undo_logs用來設置rollback segment個數,默認128個,參數innodb_undo_tablespaces用來設置組成rollback segment的共有幾個文件。
需要注意的是undo log日誌會產生redo log,undo記錄事務開始時的值用來供查詢,undo log分爲insert undo log和update undo log。insert undo log因爲是插入,對其它事務不可見,所以事務提交時可以直接刪除。當事務提交時,需要將update undo log放入purge列表供purge操作,判斷undo log所在頁是否可重用,分配給下個事務。
查看 undo log數量
3)purge
該線程用來完成delete和update最終操作,當事務提交時,不代表可以立刻刪除,可能還有別的事務在引用這行記錄,innodb中有一張history表,根據事務提交順序將undo log進行鏈接。innodb_purge_batch_size用來設置每次需要清理的undo page數量。
- 事務隔離級別
1)read uncommitted,瀏覽訪問
2)read committed 遊標穩定 唯一性約束檢測和外鍵約束檢查。
- repeatable read 沒有幻讀默認使用
- serializable 隔離 會對每個select後加 lock in share mode
可以在mysql配置文件的【mysqld】下面添加transaction-isolation = read-committed
查看當前會話的事務隔離,select @@tx_isolation \G;
- 分佈式事務XA
在java中使用jta來支持mysql分佈式事務
參數innodb_support_xa控制是否支持xa
package com.test;
import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
import javax.sql.XAConnection;
import javax.transaction.xa.XAException;
import javax.transaction.xa.XAResource;
import javax.transaction.xa.Xid;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
public class XATest {
public static void main(String[] args) {
try {
MysqlXADataSource ds1 = getDataSource();
MysqlXADataSource ds2 = getDataSource();
XAConnection xa1 = ds1.getXAConnection();
XAResource xaResource1 = xa1.getXAResource();
Connection connection1 = xa1.getConnection();
Statement stmt1 = connection1.createStatement();
XAConnection xa2 = ds2.getXAConnection();
XAResource xaResource2 = xa2.getXAResource();
Connection connection2 = xa2.getConnection();
Statement stmt2 = connection2.createStatement();
Xid xid1 = new MyXid(100, new byte[]{0x01}, new byte[]{0x02});
Xid xid2 = new MyXid(100, new byte[]{0x11}, new byte[]{0x12});
xaResource1.start(xid1, XAResource.TMNOFLAGS);
stmt1.execute("");
xaResource1.end(xid1,XAResource.TMSUCCESS);
xaResource2.start(xid2, XAResource.TMNOFLAGS);
stmt2.execute("");
xaResource2.end(xid2,XAResource.TMSUCCESS);
int ret1 = xaResource1.prepare(xid1);
int ret2 = xaResource2.prepare(xid2);
if(ret1 == XAResource.XA_OK && ret2 == XAResource.XA_OK){
xaResource1.commit(xid1, false);
xaResource2.commit(xid2, false);
}
} catch (SQLException | XAException e) {
e.printStackTrace();
}
}
private static MysqlXADataSource getDataSource(){
MysqlXADataSource ds = new MysqlXADataSource();
return ds;
}
static class MyXid implements Xid{
private int formatId;
private byte[] gtrid;
private byte[] bqual;
public MyXid() {
}
MyXid(int formatId, byte[] gtrid, byte[] bqual) {
this.formatId = formatId;
this.gtrid = gtrid;
this.bqual = bqual;
}
@Override
public int getFormatId() {
return this.formatId;
}
@Override
public byte[] getGlobalTransactionId() {
return this.gtrid;
}
@Override
public byte[] getBranchQualifier() {
return this.bqual;
}
}
}
- 事務的使用
1)start transaction、begin顯示地開啓一個事務
2)commit、rollback結束一個任務
3)savepoint 10 在事務中創建保存點
4)release savepoint 10 刪除一個事務保存點
5)rellback to 10 回滾到某個保存點