Transaction
- 指的是一组操作,里面包含许多单一的逻辑
- 只要有一个逻辑没执行成功,都算失败,都会回滚
- 用于确保逻辑执行成功
命令行操作事务
注:
cmd如果提示:'mysql'不是内部或外部命令,也不是可运行的程序或批处理文件
需要配置环境变量path
如果忘记Mysql目录,可以使用show variables like "basedir";查询
开启事务
start transaction;
提交或者回滚事务
commit:提交事务, 数据将会写到磁盘上的数据库
rollback:数据回滚,回到最初的状态
关闭自动提交
show variables like '%commit%';
set autocommit=off;
命令行演示事务过程:
代码方式操作事务
代码里面的事务,主要是针对连接来操作的
-
通过conn.setAutoCommit(false )来关闭自动提交的设置
-
提交事务 conn.commit();
-
回滚事务 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 }
事务的特性
- 原子性
- 事务中包含的逻辑不可分割
- 一致性
- 事务执行前后的数据完整性
- 隔离性
- 事务在执行期间不受其他事务影响
- 持久性
- 事务执行成功后,数据应该持久保存在磁盘上
事务的安全隐患
不考虑事务的隔离级别,会出现以下问题
读
脏读
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(重复读)
幻读
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之后才能执行。
按效率划分,从高到低
读未提交 > 读已提交 > 可重复读 > 可串行化
按拦截程度 ,从高到底
可串行化 > 可重复读 > 读已提交 > 读未提交