数据库事务

简介

        一组要么同时执行成功,要么同时失败的SQL语句。是数据库操作的一个不能分割的执行单元。

        数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。 事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性。事务是数据库运行中的逻辑工作单位,由DBMS中的事务管理子系统负责事务的处理。

        事务开始于:

        连接到数据库上,并执行一条DML语句insert、update或delete

        前一个事务结束后,又输入了另一条DML语句

        事务结束于:

        执行commit或rollback语句。

        执行一条DDL语句,例如create table语句,在这种情况下,会自动执行commit语句。

        执行一条DCL语句,例如grant语句,在这种情况下,会自动执行commit。

        断开与数据库的连接。

        执行了一条DML语句,该语句却失败了,在这种情况中,会为这个无效的DML语句执行rollback语句。


事务的四大特性

        ACID

        Atomicity(原子性):表示一个事务内的所有操作是一个整体,要么全部成功,要么全部失败

        Consistency(一致性):表示一个事务内有一个操作失败时,所有的更改过的数据都必须回滚到修改前状态

        Isolation(隔离性):事务查看数据时数据所处的状态,要么是另一并发事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看中间状态的数据。

        Durability(持久性):持久性事务完成之后,它对于系统的影响是永久性的。

  • 案例:
// 正常转账
    public static void main(String[] args) throws Exception{
        Connection conn = DbUtils.getConnection();
        Statement stat = conn.createStatement();

        //转出(账户1)
        stat.executeUpdate("update account set money = money-1000 where id=1;");
        //转入(账户2)
        stat.executeUpdate("update account set money = money+1000 where id=2;");

        stat.close();
        conn.close();
    }
//转入转出都正常执行
// 转账时发生异常
    public static void main(String[] args) throws Exception{
        Connection conn = DbUtils.getConnection();
        Statement stat = conn.createStatement();

        //转出(账户1)
        stat.executeUpdate("update account set money = money-1000 where id=1;");

        //模拟断电 --> java.lang.ArithmeticException: / by zero
        int i = 10/0;

        //转入(账户2)
        stat.executeUpdate("update account set money = money+1000 where id=2;");

        stat.close();
        conn.close();
    }
//转出执行,转入因异常不执行,此时就出现了重大数据错误
//用事务处理异常
    public static void main(String[] args){
        Connection conn =null;
        Statement stat = null;
        try {
            conn = DbUtils.getConnection();
            //开启事务(阻止事务的自动提交)
            conn.setAutoCommit(false);

            stat = conn.createStatement();
            //转出
            stat.executeUpdate("update account set money = money-1000 where id=1;");

            //断电
            int i = 10/0;

            //转入
            stat.executeUpdate("update account set money = money+1000 where id=2;");

            //提交
            conn.commit();
        } catch (Exception e) {
            e.getMessage();
            try {
                conn.rollback();
                conn.commit();//可加也不不加
                System.out.println("转账失败");
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        }finally {
            DbUtils.closeAll(conn, stat, null);
        }
    }
//转入转出要么都执行,要么都不执行,即相当于在应用层面达到事务的一致性

        ACID中的一致性是指完整性约束不被破坏,完整性包含实体完整性(主属性不为空)、参照完整性(外键必须存在原表中)、用户自定义的完整性。

  •  Mysql支持的事务语句 :
#开启事务
START TRANSACTION;     # connection.setAutoCommit(false);
UPDATE account SET money=money-1000 WHERE id=1;
UPDATE account SET money=money+1000 WHERE id=2;
#提交事务
COMMIT;#connection.commit();
#回滚
ROLLBACK; #connection.rollback();

 事务隔离级别

        SQL标准定义了4类隔离级别,包括了一些具体规则,用来限定事务内外的哪些改变是可见的,哪些是不可见的。低级别的隔离级一般支持更高的并发处理,并拥有更低的系统开销。

        Read Uncommitted(读取未提交内容):在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)。

        Read Committed(读取提交内容):这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别及以下隔离级别出现不可重复读(Nonrepeatable Read)问题,因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果。

        Repeatable Read 可重读:这是MySQL的默认事务隔离级别,它确保同一事务的多个实例在并发读取数据时,会看到同样的数据行。不过理论上,这会导致另一个棘手的问题:幻读(Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻读” 行。InnoDB和Falcon存储引擎通过多版本并发控制(MVCC,Multiversion Concurrency Control)机制(至少在该RR级别)解决了部分(读取时)该问题。

        Serializable 可串行化 :这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争。效率最低的。

oracle:只支持了  读提交(默认)  和   序列化读
mysql:都支持了   可重复读(默认)

隔离级别 低 -> 高:并发性降低,安全性提高
                信息源共享的数据越来越少,造成事务彼此阻塞的几率越来越大

        这四种隔离级别采取不同的锁类型来实现。

​#修改事务的隔离级别:
SET [SESSION|GLOBAL] TRANSACTION ISOLATION LEVEL 
                        [READ UNCOMMITTED|READ COMMITTED|REPEATABLE READ|SERIALIZABLE];
#查看事务隔离级别
SELECT @@tx_isolation;
事务并发时的安全问题:
      1>脏读:一个事务中 读到了其他事务中 未提交的数据
      2>不可重复读:一个事务中 多次读取相同的数据行,但是,结果不一致
      3>幻影读:一个事务中 多次读取同一张表,但是,数据行数不一致;
               查询时没有某数据,但是操作时,却提示存在此数据。(在某列唯一情况下事务2读取数据,此时事务1插入一行数据,事务2也想插入此行数据时失败)
  • 读未提交 --> 脏读(Drity Read):

        某个事务已更新一份数据未提交前,另一个事务在此时读取了同一份数据

# A方 买 张三
SELECT @@tx_isolation;
START TRANSACTION;
UPDATE account SET money=money-2000 WHERE id=1;
UPDATE account SET money=money+2000 WHERE id=2;
COMMIT;
ROLLBACK;

# B方  卖  李四
#(修改隔离级别)
SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
#查看账户数据(脏读)
SELECT * FROM account;
#发货
  • 读提交 --> 不可重复读(Non-repeatable read):

        在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。  

#修改隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL READ COMMITTED;
SELECT * FROM account;

#统计所有账户总数据(不可重复读)
START TRANSACTION;
	SELECT SUM(money) FROM account;
	SELECT SUM(money) FROM account;
	SELECT SUM(money) FROM account;
COMMIT;	
  •  可重读(Repeatable Read) --> 幻读:

        在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。但是InnoDB存储引擎通过多版本并发控制机制解决了该问题。

#再次修改隔离级别
SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;

#统计所有账户总数据(可重读)
START TRANSACTION;
	SELECT SUM(money) FROM account;
	SELECT SUM(money) FROM account;
	SELECT SUM(money) FROM account;
COMMIT;	
  •  可串行化(Serializable) :

        悲观锁,强制事务排序,使之不可能相互冲突,从而解决幻读问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值