数据库事务

Transaction

  • 指的是一组操作,里面包含许多单一的逻辑
  • 只要有一个逻辑没执行成功,都算失败,都会回滚
  • 用于确保逻辑执行成功

命令行操作事务

注:

cmd如果提示:'mysql'不是内部或外部命令,也不是可运行的程序或批处理文件

需要配置环境变量path

如果忘记Mysql目录,可以使用show variables like "basedir";查询

开启事务

 start transaction;

提交或者回滚事务

commit:提交事务, 数据将会写到磁盘上的数据库

rollback:数据回滚,回到最初的状态

关闭自动提交

show variables like '%commit%';

set autocommit=off;

命令行演示事务过程:

代码方式操作事务

代码里面的事务,主要是针对连接来操作的

  1. 通过conn.setAutoCommit(false )来关闭自动提交的设置

  2. 提交事务 conn.commit();

  3. 回滚事务 conn.rollback();

 1 public class TestDemo {
 2     @Test
 3     public void test() {
 4         try {
 5             Connection conn = DBUtils.getConn();
 6             //关闭自动提交
 7             conn.setAutoCommit(false);
 8             String sql = "update person set name = ? where id = ?";
 9             PreparedStatement ps = conn.prepareStatement(sql);
10             ps.setString(1, "admin");
11             ps.setInt(2, 3);
12             
13             int result = ps.executeUpdate();
14             System.out.println(result);
15             if(result>0) {
16                 conn.commit();
17             }else {
18                 conn.rollback();
19             }
20         } catch (SQLException e) {
21             e.printStackTrace();
22         }
23     }
24 }

事务的特性

  1.  原子性
    • 事务中包含的逻辑不可分割  
  2. 一致性
    • 事务执行前后的数据完整性  
  3. 隔离性
    • 事务在执行期间不受其他事务影响  
  4. 持久性
    • 事务执行成功后,数据应该持久保存在磁盘上  

事务的安全隐患

不考虑事务的隔离级别,会出现以下问题

脏读

  A事务读取B事务未提交更改数据

不可重复读

  A事务读取B事务已提交更改数据,导致前后两次读取结果不一致的问题

幻读

  A事务读取B事务已提交新增数据,会引发幻读问题

注意:

  不可重复读和幻读的区别是:

    前者是指读到了已经提交的事务的更改数据(修改或删除),后者是指读到了其他已经提交事务的新增数据

  对于这两种问题解决采用不同的办法:

    防止读到更改数据,只需对操作的数据添加行级锁,防止操作中的数据发生变化

    防止读到新增数据,往往需要添加表级锁,将整张表锁定,防止新增数据(oracle采用多版本数据的方式实现)

丢失更新 

丢失更新就是两个不同的事务(或者Java程序线程)在某一时刻对同一数据进行读取后,先后进行修改。导致第一次操作数据丢失

时间点 事务A 事务B
1 start transaction;  
2   start transaction;
3 update操作1  
4   update操作2
5 commit  
6   commit


解决方法:

  • 使用悲观锁(排它锁)。丢失更新可以使用写锁(排它锁)进行控制。因为排它锁添加到某个表的时候,事务未经提交,其他的事务根本没法获取修改权,因此排它锁可以用来控制丢失更新。需要说明的是有时候,当知道某一行会发生并发修改的时候,可以把锁定的范围缩小。例如使用select * from table_name t wheret.id='1' for update; 这样能够比较好地把控上锁的粒度,这种基于行级上锁的方法叫"行级锁"。

     

  • 使用乐观锁。乐观锁的原理是:认为事务不一定会产生丢失更新,让事务进行并发修改,不对事务进行锁定。发现并发修改某行数据时,乐观锁抛出异常。让用户解决。可以通过给数据表添加自增的version字段或时间戳timestamp。进行数据修改时,数据库会检测version字段或者时间戳是否与原来的一致。若不一致,抛出异常。

     


直接使用锁机制管理是很复杂的,基于锁机制,数据库给用户提供了不同的事务隔离级别,只要设置了事务隔离级别,数据库就会分析事务中的sql语句然后自动选择合适的锁

事务隔离级别

事务隔离级别

脏读

不可重复读

幻读

第一类丢失更新

第二类丢失更新

READ UNCOMMITTED

允许

允许

允许

不允许

允许

READ COMMITTED

不允许

允许

允许

不允许

允许

REPEATABLE READ

不允许

不允许

允许

不允许

不允许

SERIALIZABLE

不允许

不允许

不允许

不允许

不允许

READ UNCOMMITTED(读未提交)

脏读

 

READ COMMITTED(读已提交)

不可重复读

REPEATABLE READ(重复读)

幻读

参考MySQL官方文档 -- 一致性非阻塞读

The snapshot of the database state applies to SELECT statements within a transaction, not necessarily to DML statements. If you insert or modify some rows and then commit that transaction, a DELETE or UPDATE statement issued from another concurrent REPEATABLE READ transaction could affect those just-committed rows, even though the session could not query them. If a transaction does update or delete rows committed by a different transaction, those changes do become visible to the current transaction. 
翻译为:

数据库状态的快照适用于事务中的SELECT语句, 而不一定适用于所有DML语句。 如果您插入或修改某些行, 然后提交该事务, 则从另一个并发REPEATABLE READ事务发出的DELETE或UPDATE语句就可能会影响那些刚刚提交的行, 即使该事务无法查询它们。 如果事务更新或删除由不同事务提交的行, 则这些更改对当前事务变得可见。

串行化(SERIALIZABLE) 

隔离性的最高隔离级别SERIALIZABLE也可以解决幻读, 但该隔离级别在实际中很少使用

串行化

开启两个事务,先开启A,再开启B;B必须等待A执行commit或者rollback之后才能执行。

按效率划分,从高到低

读未提交 > 读已提交 > 可重复读 > 可串行化

按拦截程度 ,从高到底

可串行化 > 可重复读 > 读已提交 > 读未提交

 

原文地址:https://www.cnblogs.com/qf123/p/10078035.html