MySQL数据库事务详解 事务的概念 MySQL数据库中操作事务命令 JDBC中使用事务 事务的四大特性(ACID) 事务的隔离级别

 

微信公众号【黄小斜】大厂程序员,互联网行业新知,终身学习践行者。关注后回复「Java」、「Python」、「C++」、「大数据」、「机器学习」、「算法」、「AI」、「Android」、「前端」、「iOS」、「考研」、「BAT」、「校招」、「笔试」、「面试」、「面经」、「计算机基础」、「LeetCode」 等关键字可以获取对应的免费学习资料。 

                     MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别

 

事务指逻辑上的一组操作,组成这组操作的各个单元,要不全部成功,要不全部不成功。 
例如:A向B转账100元,对应于如下两条sql语句:

update from account set money=money+100 where name='b';
update from account set money=money-100 where name='a';
  • 1
  • 2
  • 1
  • 2

数据库默认事务是自动提交的,也就是发一条sql它就执行一条,如果想多条sql放在一个事务中执行,则需要使用如下语句:

start transaction
…
…
commit
  • 1
  • 2
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

数据库开启事务命令:

  • start transaction :开启事务
  • rollback:回滚事务
  • commit:提交事务

MySQL数据库中操作事务命令

编写测试SQL脚本,如下:

/* 创建数据库 */
create database day16;

use day16;

/* 创建账户表 */
create table account 
(
    id int primary key auto_increment,
    name varchar(40),
    money float
) character set utf8 collate utf8_general_ci;

/* 插入测试数据 */
insert into account(name,money) values('aaa',1000);
insert into account(name,money) values('bbb',1000);
insert into account(name,money) values('ccc',1000); 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

下面我们在MySQL数据库中模拟aaa向bbb转帐这个业务场景。

  • 开启事务(start transaction) 
    使用”start transaction”开启MySQL数据库的事务,如下所示: 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    我们首先在数据库中模拟转账失败的场景,首先执行update语句让aaa用户的money减少100块钱,如下图所示: 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    现在假设程序抛出异常,也即该链接断了,代码块没有完成,此时数据库会自动回滚掉此sql语句造成的影响,也就是说这条sql语句没有执行。我们现在就来模拟这种情况,我们关闭当前操作的dos命令行窗口,这样就导致了刚才执行的update语句的数据库的事务没有被提交,那么我们对aaa用户的修改就不算是真正的修改了,下次在查询aaa用户的money时,依然还是之前的1000,如下图所示: 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别

  • 提交事务(commit) 
    下面我们在数据库模拟aaa向bbb转账成功的场景。 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    我们手动提交(commit)数据库事务之后,aaa向bbb转账100块钱的这个业务操作算是真正成功了,aaa账户中少了100,bbb账户中多了100。

  • 回滚事务(rollback) 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    通过手动回滚事务,让所有的操作都失效,这样数据就会回到最初的初始状态!

JDBC中使用事务

当Jdbc程序向数据库获得一个Connection对象时,默认情况下这个Connection对象会自动向数据库提交在它上面发送的SQL语句。若想关闭这种默认提交方式,让多条SQL在一个事务中执行,可使用下列的JDBC控制事务语句:

  • Connection.setAutoCommit(false); //开启事务(start transaction)
  • Connection.rollback(); //回滚事务(rollback)
  • Connection.commit(); //提交事务(commit)

JDBC使用事务范例

在JDBC代码中演示银行转帐案例,使如下转帐操作在同一事务中执行:

update from account set money=money-100 where name=‘aaa’;
update from account set money=money+100 where name=‘bbb’;
  • 1
  • 2
  • 1
  • 2
  • 模拟aaa向bbb转账成功时的业务场景

    public class Demo1 {
    
        /*
         * a--->b转100元
         */
        public static void main(String[] args) throws SQLException {
    
            Connection conn = null;
            PreparedStatement st = null;
            ResultSet rs = null;
    
            try {
                conn = JdbcUtils.getConnection();
                conn.setAutoCommit(false); // 相当于start transaction,开启事务
    
                String sql1 = "update account set money=money-100 where name='aaa'";
                String sql2 = "update account set money=money+100 where name='bbb'";
    
                st = conn.prepareStatement(sql1);
                st.executeUpdate();
    
                st = conn.prepareStatement(sql2);
                st.executeUpdate();
    
                conn.commit();
            } finally {
                JdbcUtils.release(conn, st, rs);
            }
        }
    
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
  • 模拟aaa向bbb转账过程中出现异常导致有一部分SQL执行失败后让数据库自动回滚事务

    public class Demo1 {
    
        /*
         * a--->b转100元
         */
        public static void main(String[] args) throws SQLException {
    
            Connection conn = null;
            PreparedStatement st = null;
            ResultSet rs = null;
    
            try {
                conn = JdbcUtils.getConnection();
                conn.setAutoCommit(false); // 相当于start transaction,开启事务
    
                String sql1 = "update account set money=money-100 where name='aaa'";
                String sql2 = "update account set money=money+100 where name='bbb'";
    
                st = conn.prepareStatement(sql1);
                st.executeUpdate();
    
                int x = 1/0; // 程序运行到这个地方抛异常,后面的代码就不执行,数据库没有收到commit命令
    
                st = conn.prepareStatement(sql2);
                st.executeUpdate();
    
                conn.commit();
            } finally {
                JdbcUtils.release(conn, st, rs);
            }
        }
    
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
  • 模拟aaa向bbb转账过程中出现异常导致有一部分SQL执行失败时手动通知数据库回滚事务

    public class Demo1 {
    
        /*
         * a--->b转100元
         */
        public static void main(String[] args) throws SQLException {
    
            Connection conn = null;
            PreparedStatement st = null;
            ResultSet rs = null;
    
            try {
                conn = JdbcUtils.getConnection();
                conn.setAutoCommit(false); // 相当于start transaction,开启事务
    
                String sql1 = "update account set money=money-100 where name='aaa'";
                String sql2 = "update account set money=money+100 where name='bbb'";
    
                st = conn.prepareStatement(sql1);
                st.executeUpdate();
    
                int x = 1/0; // 程序运行到这个地方抛异常,后面的代码就不执行,数据库没有收到commit命令
    
                st = conn.prepareStatement(sql2);
                st.executeUpdate();
    
                conn.commit();
            } catch (Exception e) {
                e.printStackTrace();
                conn.rollback(); // 捕获到异常之后手动通知数据库执行回滚事务的操作
            } finally {
                JdbcUtils.release(conn, st, rs);
            }
        }
    
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

设置事务回滚点

在开发中,有时候可能需要手动设置事务的回滚点,在JDBC中使用如下的语句设置事务回滚点:

Savepoint sp = conn.setSavepoint();
Conn.rollback(sp);
Conn.commit(); // 回滚后必须通知数据库提交事务
  • 1
  • 2
  • 3
  • 1
  • 2
  • 3

设置事务回滚点范例:

public class Demo2 {

    // 事务回滚点概念
    public static void main(String[] args) throws SQLException {

        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        Savepoint sp = null;

        try {
            conn = JdbcUtils.getConnection(); // MySQL默认的隔离级别——REPEATABLE-READ,并且是严格遵循数据库规范设计的,即支持4种隔离级别
                                              // Oracle默认的隔离级别——Read committed,并且不支持这4种隔离级别,只支持这4种隔离级别中的2种,Read committed和Serializable
            // conn.setTransactionIsolation(); // 相当于设置CMD窗口的隔离级别
            conn.setAutoCommit(false); // 相当于start transaction,开启事务

            // 不符合实际需求
            String sql1 = "update account set money=money-100 where name='aaa'";
            String sql2 = "update account set money=money+100 where name='bbb'";
            String sql3 = "update account set money=money+100 where name='ccc'";

            st = conn.prepareStatement(sql1);
            st.executeUpdate();
            /*
             * 只希望回滚掉这一条sql语句,上面那条sql语句让其执行成功
             * 这时可设置事务回滚点
             */
            sp = conn.setSavepoint();

            st = conn.prepareStatement(sql2);
            st.executeUpdate();

            int x = 1/0; // 程序运行到这个地方抛异常,后面的代码就不执行,数据库没有收到commit命令

            st = conn.prepareStatement(sql3);
            st.executeUpdate();

            conn.commit();
        } catch (Exception e) {
            e.printStackTrace();
            conn.rollback(sp); // 回滚到sp点,sp点上面的sql语句发给数据库执行,由于数据库没收到commit命令,数据库又会自动将这条sql语句的影响回滚掉,所以回滚完,一定要记得commit命令。
            conn.commit(); // 手动回滚后,一定要记得提交事务
        } finally {
            JdbcUtils.release(conn, st, rs);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

事务的四大特性(ACID)

  • 原子性(Atomicity) 
    原子性是指事务是一个不可分割的工作单位,事务中的操作要么全部成功,要么全部失败。比如在同一个事务中的SQL语句,要么全部执行成功,要么全部执行失败。
  • 一致性(Consistency) 
    官网上事务一致性的概念是:事务必须使数据库从一个一致性状态变换到另外一个一致性状态。还要一种说法是事务前后数据的完整性必须保持一致。以转账为例子,A向B转账,假设转账之前这两个用户的钱加起来总共是2000,那么A向B转账之后,不管这两个账户怎么转,A用户的钱和B用户的钱加起来的总额还是2000,这个就是事务的一致性。
  • 隔离性(Isolation) 
    事务的隔离性是多个用户并发访问数据库时,数据库为每一个用户开启的事务,不能被其他事务的操作数据所干扰,多个并发事务之间要相互隔离。
  • 持久性(Durability) 
    持久性是指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响。

事务的隔离级别

事务的四大特性中最麻烦的是隔离性,下面重点介绍一下事务的隔离级别。 
多个线程开启各自事务操作数据库中数据时,数据库系统要负责隔离操作,以保证各个线程在获取数据时的准确性。

事务不考虑隔离性可能会引发的问题

如果事务不考虑隔离性,可能会引发如下问题:

  • 脏读 
    指一个事务读取了另外一个事务未提交的数据。 
    这是非常危险的,假设a向b转帐100元,对应sql语句如下所示:

    1.update account set money=money+100 while name=‘b’;    
    2.update account set money=money-100 while name=‘a’;
    
    • 1
    • 2
    • 3
    • 1
    • 2
    • 3

    当第1条sql执行完,第2条还没执行(a未提交时),如果此时b查询自己的帐户,就会发现自己多了100元钱。如果a等b走后再回滚,b就会损失100元。

  • 不可重复读 
    在一个事务内读取表中的某一行数据,多次读取结果不同。(一个事务读取到了另外一个事务提交的数据) 
    例如银行想查询a帐户余额,第一次查询a帐户为200元,此时a向帐户内存了100元并提交了,银行接着又进行了一次查询,此时a帐户为300元了。银行两次查询不一致,可能就会很困惑,不知道哪次查询是准的。可将例子简化为:读表中某一行数据,例如a账户第一次读为1000,第二次读为1100。 
    不可重复读脏读的区别是,脏读是读取前一事务未提交的脏数据,不可重复读是重新读取了前一事务已提交的数据。 
    很多人认为这种情况就对了,无须困惑,当然是以后面的结果为准了。我们可以考虑这样一种情况,比如银行程序需要将查询结果分别输出到电脑屏幕和写到文件中,结果在一个事务中针对输出的目的地,进行的两次查询不一致,导致文件和屏幕中的结果不一致,银行工作人员就不知道以哪个为准了。
  • 虚读(幻读) 
    虚读(幻读)是指在一个事务内读取到了别的事务插入的数据,导致前后读取不一致。 
    如丙存款100元未提交,这时银行做报表统计account表中所有用户的总额为500元,然后丙提交了,这时银行再统计发现帐户为600元了,造成虚读同样会使银行不知所措,到底以哪个为准。可将例子简化为:读整个表,即表的行数,例如第一次读某个表有3条记录,第二次读该表又有4条记录

数据库共定义了四种隔离级别,应用《高性能mysql》一书中有说明: 
MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别
MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别
MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别

  • Serializable(串行化):可避免脏读、不可重复读、虚读情况的发生。
  • Repeatable read(可重复读):可避免脏读、不可重复读情况的发生。
  • Read committed(读已提交):可避免脏读情况发生。
  • Read uncommitted(读未提交):最低级别,以上情况均无法保证。

总结:在MySQL中,实现了这四种隔离级别,分别有可能产生问题如下所示: 
MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别

下面说说修改事务隔离级别的方法:

  1. 全局修改,修改my.ini(或mysql.ini)配置文件,在最后加上

    
    #可选参数有:READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE.
    
    [mysqld]
    transaction-isolation = REPEATABLE-READ
    • 1
    • 2
    • 3
    • 4
    • 5
    • 1
    • 2
    • 3
    • 4
    • 5

    注意:MySQL默认的隔离级别为REPEATABLE-READ,并且是严格遵循数据库规范设计的,即支持4种隔离级别;Oracle默认的隔离级别为Read committed,并且不支持这4种隔离级别,只支持这4种隔离级别中的2种,Read committed和Serializable

  2. 对当前session修改,在登录mysql客户端后,执行命令:

    set session transaction isolation level read uncommitted; // 设置当前事务隔离级别
    • 1
    • 1

    注意:session是不能掉的,不然你设置不会成功,MySQL的隔离级别还是默认的隔离级别——REPEATABLE-READ,如下所示: 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别

查询当前事务隔离级:

select @@tx_isolation; // 查询当前事务隔离级别
  • 1
  • 1

下面,将利用MySQL的客户端程序,分别测试几种隔离级别。测试数据库为day16,表为account;表如下: 
MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
两个命令行客户端分别为a(黑色背景窗口),b(蓝色背景窗口);不断改变b的隔离级别,在a端修改数据。

  • 将b的隔离级别设置为read uncommitted(未提交读) 
    在a未更新数据之前,b客户端 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    a更新数据,a向b转帐100元 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    此时b查询自己的帐户,就会发现自己多了100元钱,出现了脏读(这个事务读取到了别的事务未提交的数据) 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    如果a等b走后再回滚  
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    此时b查询自己的帐户,发现又少掉了100元钱,两次读取的数据不一样,出现不可重复读现象 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    a提交完事务,再开启一个事务,向表account中新增一条记录 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    此时b再次查询account表,发现表account中多了一条记录,出现幻读现象 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别
  • 将客户端b的事务隔离级别设置为read committed(已提交读) 
    在a未更新数据之前,b客户端 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    a更新数据,a向b转帐100元 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    b查询自己的帐户,金额没有发生任何变化,说明已提交读隔离级别解决了脏读的问题 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    a此刻提交事务 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    b再次查询自己的帐户,发现自己又多了100元钱,这时就发生不可重复读(指这个事务读取到了别的事务提交的数据) 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    a再开启一个事务,向表account中新增一条记录 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    然后b再次查询account表,发现表account中多了一条记录,出现幻读现象 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别
  • 将b的隔离级别设置为repeatable read(可重复读) 
    在a未更新数据之前,b客户端 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    a更新数据,a向b转帐100元 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    b查询自己的帐户,金额没有发生任何变化,这说明repeatable read这种级别可避免脏读 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    a此刻提交事务 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    b再次查询自己的帐户,金额没有发生任何变化,这说明repeatable read这种级别还可以避免不可重复读 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    a再开启一个事务,向表account中新增一条记录 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    然后b再次查询account表,发现表中可能会多出一条ddd的记录(也有可能不会多出一条ddd的记录,我测试时就是这种情况),这就发生了虚读,也就是在这个事务内读取了别的事务插入的数据(幻影数据) 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别
  • 将b的隔离级别设置为可串行化 (Serializable) 
    为可串行化 (Serializable)均可避免脏读、不可重复读、幻读。避免脏读和不可重复读的情况我就不测试了,测试步骤同上,下面我重点讲解可串行化 (Serializable)避免幻读的情况。 
    事务b端 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    事务a端 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    因为此时事务b的隔离级别设置为serializable,开始事务后,并没有提交,所以事务a只能等待。 
    事务b提交事务,事务b端 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    事务a端 
    MySQL数据库事务详解
事务的概念
MySQL数据库中操作事务命令
JDBC中使用事务
事务的四大特性(ACID)
事务的隔离级别 
    serializable完全锁定字段,若一个事务来查询同一份数据就必须等待,直到前一个事务完成并解除锁定为止,是完整的隔离级别,会锁定对应的数据表格,因而会有效率的问题。 
    结论:Serializable隔离级别,虽然可避免所有问题,但性能、效率是最低的,原因是它采取的是锁表的方式,即单线程的方式,即有一个事务来操作这个表了,另外一个事务只能等在外面进不来

下面,将利用Java程序来测试Serializable隔离级别。

public class Demo3 {

    public static void main(String[] args) throws SQLException, InterruptedException {

        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        Savepoint sp = null;

        try {
            conn = JdbcUtils.getConnection();
            conn.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE); // 相当于设置CMD窗口的隔离级别
            conn.setAutoCommit(false);

            String sql = "select * from account";
            conn.prepareStatement(sql).executeQuery();

            // 故意让程序睡眠20秒,睡眠20秒之后事务才结束,程序运行完
            Thread.sleep(1000*20);

            conn.commit();
        } finally {
            JdbcUtils.release(conn, st, rs);
        }
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

程序运行,同时在客户端开启一个事务,插入一条记录,需要等待一段时间才能插入进去。