全面了解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 回滚到某个保存点
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章