全面瞭解Mysql(七)事務

  1. 套話
    一般來說數據庫事務需要滿足ACID,A(atomicity)原子性,一個事務必須保證要麼都做,要麼都不做;C(consistency)一致性,一個事務開始和結束數據庫的完整性約束沒有被破壞;I(isolation)隔離性,各個事務之間相互分離彼此不干擾;D(durability)持久性,一旦事務結束,數據結果就是永久性的,不能因爲宕機之類的數據不可恢復。
  2. 事務實現
    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數量。
  3. 事務隔離級別
    1)read uncommitted,瀏覽訪問
    2)read committed 遊標穩定 唯一性約束檢測和外鍵約束檢查。
  1. repeatable read 沒有幻讀默認使用
  2. serializable 隔離 會對每個select後加 lock in share mode
    可以在mysql配置文件的【mysqld】下面添加transaction-isolation = read-committed
    查看當前會話的事務隔離,select @@tx_isolation \G;
    在這裏插入圖片描述
  1. 分佈式事務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;

/**
 * <p>
 * 該文件的作用://TODO
 * </p>
 *
 * @author xuxiake
 * @date 2019/10/10 12:58
 */
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();
        //此處需要填寫ds連接信息
        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. 事務的使用
    1)start transaction、begin顯示地開啓一個事務
    2)commit、rollback結束一個任務
    3)savepoint 10 在事務中創建保存點
    4)release savepoint 10 刪除一個事務保存點
    5)rellback to 10 回滾到某個保存點
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章