事务

事务

数据库中执行sql语句的最小工作单元, 不能拆分,执行同一业务的sql语句可以保证全部成功或全部失败

为什么使用事务?

如果不使用事务,客户端会自动提交,多次操作数据库做某一件事的时候(如:转账)会出现部分成功部分失败,则数据会出现异常.使用事务,把多次操作数据库的sql合并到一次提交中,这样就能保证同时成功或失败

事务的ACID特性 (面试)

  • Atomicity: 原子性 事务中所有操作是不可再分割的原子单位。事务中所有操作要么全部执行成功,要么全部执行失败。
  • Consistency: 一致性 事务执行后,数据库状态与其它业务规则保持一致。如转账业务,无论事务执行成功与否,参与转账的两个账户金额之和在事务前后应该是保持不变的。
  • Isolation: 隔离性,隔离性是指在并发操作中,不同事务之间应该隔离开来,使每个并发中的事务不会相互干扰。也就是说,在事中务查看数据更新时,数据所处的状态要么是另一事务修改它之前的状态,要么是另一事务修改它之后的状态,事务不会查看到中间状态的数据。
  • Durability: 持久性,一旦事务提交成功,事务中所有的数据操作都必须被持久化到数据库中,即使提交事务后,数据库马上崩溃,在数据库重启时,也必须能保证通过某种机制恢复数据。

使用事务的过程

  1. 开启事务
  2. 执行多次sql (在内存中执行)
  3. 手动提交或(回滚/中断)

开启事务的三种方式

  1. set autocommit=0;关闭自动提交

    查看客户端自定提交的状态:

    show variables like '%autocommit%';
    

    关闭自动提交 仅对当前窗口生效

    set autocommit=0;
    

    打开自动提交

    set autocommit=1;
    
  2. start transaction; (开始事务)

  3. begin (开始事务)

案例 验证转账流程:

  1. 创建表

    create table person(
        id int primary key auto_increment,
        name varchar(10),
        money int
    );
    
  2. 插入数据

    insert into person values
    (null, '超人', 200),
    (null, '蝙蝠侠', 10000);
    
  3. 关闭自动提交 (3种方式都可以)

    -- start transaction;
    -- begin;
    set autocommit=0;
    
  4. 转账

    update person set money=500 where id=1; -- (+300)
    update person set money=9700 where id=2; -- (-300)
    
  5. 打开新窗口 验证是否转账成功?数据没变 因为两次操作都是在内存中操作 并未提交

  6. 手动提交

    commit;
    

数据回滚

只能在事务中使用

执行rollback会将数据回滚到上次提交的点

rollback;

使用quit是中断事务, 和rollback效果一样

设置回滚点

只能在事务中使用

  1. 保存回滚点: savepoint s1; s1是回滚点的名字
  2. 回滚到指定的回滚点: rollback to s1;

验证回滚点:

  1. 先将回滚点500改成600
  2. 保存回滚点 s1
  3. 将超人从600改成700
  4. 回滚到s1 如果超人的钱 为600则说明成功!

总结

  1. show variables like '%autocommit%'; 查看自动提交状态

  2. set autocommit=0/1; 关闭或开启自动提交, 0是关闭, 1是开启

  3. start transaction; 开始事务

  4. begin (开始事务)

  5. rollback 回滚在上次提交的点,只能在事务中使用

  6. quit 中断事务,只能在事务中使用

  7. savepoint s1; 自定义一个回滚点, 名字为s1,只能在事务中使用

  8. rollback to s1; 回滚带名字为s1的回滚点,只能在事务中使用

  9. commit; 提交事务,只能在事务中使用

扩展-事务并发读问题

多个事务对相同的数据同时进行操作,这叫做事务并发。

在事务并发时,如果没有采取必要的隔离措施,可能会导致各种并发问题,破坏数据的完整性等。这些问题中,其中有三类是读问题,分别是:脏读、不可重复读、幻读。

  1. 脏读(dirty read):在一个事务中,读取到另一个事务未提交更新的数据,即读取到了脏数据;

    例如:A给B转账100元但未提交事务,在B查询后,A做了回滚操作,那么B查询到了A未提交的数据,就称之为脏读。

    提示:需要将数据库的事务隔离级别设置为最低,才能够看到脏读现象
    事务1:开启事务; A - 100 = 900; B + 100 = 1100; (没有提交事务)
    事务2:开启事务; 查询B账户的金额 1100, 这个过程叫做脏读, 1100就是一个脏数据
    
  2. 不可重复读(unrepeatable read):对同一记录的两次读取结果不一致,因为在两次查询期间,有另一事务对该记录做了修改(是针对修改操作)

    例如:在事务1中,前后两次查询A账户的金额,在两次查询之间,另一事物2对A账户的金额做了修改(并且也提交了事务),此种情况可能会导致事务1中,前后两次查询的结果不一致。这就是不可重复读。

    事务1:开启事务---
        第一次读取A账户的金额:1000
        第二次读取A账户的金额:900
    事务2:开启事务---
        A账户 - 100 = 900;
        提交事务---
    
  3. 幻读(虚读)(phantom read):对同一张表的两次查询结果不一致,因为在两次查询期间,有另一事务进行了插入或者是删除操作(是针对插入或删除操作);

    事务1:开启事务---
        select * from acc where id=3;//不存在id为3的记录
        insert into acc value(3,'C',2000);
        select * from acc where id=3;//存在id为3的记录
    事务2:开启事务---
        insert into acc value(3,'C',2000);
        提交事务---
    

注意:mysql默认的是不允许出现脏读和不可重复读,所以在下面演示之前需要设置mysql允许出现脏读、不可重复读等。

set tx_isolation='read-uncommitted'; -- 设置mysql的事务隔离级别

扩展-事务隔离级别

事务隔离级别分四个等级,在相同数据环境下,对数据执行相同的操作,设置不同的隔离级别,可能导致不同的结果。不同事务隔离级别能够解决的数据并发问题的能力也是不同的。

查询当前事务隔离级别:

select @@tx_isolation;

设置隔离级别:

set tx_isolation='级别参数';

级别参数可取值4中, 如下:

  1. READ UNCOMMITTED(读未提交数据)

    安全性最差,可能出现任何事务并发问题(比如脏读、不可以重复读、幻读等)

    但性能最好(不使用!!)

  2. READ COMMITTED(读已提交数据)(Oracle默认)

    安全性较差

    性能较好

    可以防止脏读,但不能防止不可重复读,也不能防止幻读;

  3. REPEATABLE READ(可重复读)(MySQL默认)

    安全性较高

    性能较差

    可以防止脏读不可重复读,但不能防止幻读问题;

  4. SERIALIZABLE(串行化)

    安全性最高,不会出现任何并发问题,因为它对同一数据的访问是串行的,非并发访问;

    性能最差;(不使用!!)

    MySQL的默认隔离级别为REPEATABLE READ,即可以防止脏读和不可重复读

隔离级别总结

MySQL查询当前的事务隔离级别

select @@tx_isolation;
  1. set tx_isolation='read-uncommitted'; 

安全性最差,容易出现脏读不可重复读幻读,但性能最高

  1. set tx_isolation='read-committed';

    安全性一般,可防止脏读,不能防止不可重复读幻读

  2. set tx_isolation='repeatable-read';

    安全性较好,可防止脏读不可重复读,但不能防止幻读

  3. set tx_isolation='serialiable';

    安全性最好,可以防止一切事务并发问题,但是性能最差。

JDBC设置事务隔离界别

JDBC中通过Connection提供的方法设置事务隔离级别:

Connection.setTransactionIsolation(int level)

leve参数可选值如下:

Connection.TRANSACTION_READ_UNCOMMITTED 1(读未提交数据)
Connection.TRANSACTION_READ_COMMITTED 2(读已提交数据)
Connection.TRANSACTION_REPEATABLE_READ 4(可重复读)
Connection.TRANSACTION_SERIALIZABLE 8(串行化)
Connection.TRANSACTION_NONE 0(不使用事务)

提示:在开发中,一般情况下不需要修改事务隔离级别

posted @ 2020-05-31 14:00  zpk-aaron  阅读(199)  评论(0编辑  收藏  举报