著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
介绍事务的四大原则,并通过实例介绍数据库实现事务的方法,以及使用JDBC实现事务的方法。
微服务系统最大的挑战:
- 数据的并发访问、修改;
- 不同请求之间的数据隔离,一个业务请求修改多个数据,保证都完成或失败;
- 发生异常时的数据回滚。
事务是以一种可靠、一致的方式,访问和操作数据库中的数据的程序单元。
以sql的方式演示数据库事务:
BEGIN TRANSACTION; --开启事务 UPDATE t_user SET amount = amount-100 WHERE username='BatMan'; UPDATE t_user SET amount = amount+100 WHERE username='SuperMan'; COMMIT; --提交事务 ROLLBACK; --回滚事务
- ResourceManager:为事务管理器
- JDBC Driver:事务的具体实现,使用不同的驱动包则代表不同的事务实现方式。
- DB Connection:数据库连接
- Resource:资源(这里指数据库)
以java 程序为例子介绍JDBC事务管理
Connection conn = getConnection(); // 创建数据库连接 conn.setAutoCommit(false); // 设置不自动提交 Statement stmt1 = conn.prepareStatement(updateUser1Sql); stmt1.executeUpdate(); Statement stmt2 = conn.prepareStatement(updateUser2Sql); stmt2.executeUpdate(); conn.commit(); // or conn.rollback(); 提交事务或回滚事务
这里演示一个事务的例子: 以转帐为例,SuperMan给BatMan转账100元,则SuperMan账户-100,BatMan账户+100。
第一段sql
-- 查看全局事务隔离级别,Session事务隔离级别 SELECT @@GLOBAL.tx_isolation, @@SESSION.tx_isolation; BEGIN TRANSACTION; --开启事务 UPDATE t_user SET amount = amount-100 WHERE username='BatMan'; UPDATE t_user SET amount = amount+100 WHERE username='SuperMan'; COMMIT; --提交事务
第二段sql
-- 设置事务隔离级别(不commit也可以读取最新内容,类似于脏读) 1: SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; 2: BEGIN TRANSACTION; --开启事务 3: SELECT * FROM t_user; 4: SELECT * FROM t_user WHERE username='BatMan'; 5: COMMIT; --提交事务
- 以navicat for mysql 工具为例,开启一个查询,执行第一段sql ,但最后一行COMMIT还未执行。这时再开一个查询窗口执行第二段sql,但第2行sql先不执行,从第3行开始执行,此时可以看到结果还是和最初的数据一致,没有增加也没有减少,这就是事务的隔离性:一个事务的执行过程中不能影响到其他事务的执行,即一个事务内部的操作及使用的数据对其他事务是隔离的,并发执行各个事务之间无不干扰。
- 此时全部执行第二段sql,因为设置了事务的隔离级别,所以此时可以看到数据有加有减少,虽然第一段sql没有执行COMMIT操作。但任然看到了修改后的数据。
为了解决类似这种数据脏读问题可以在SELECT 语句
后加 FOR UPDATE
, FOR UPDATE
是一个排它锁,也就是说,其他的事务是可以读取的。但是不能写入或者更新。效果等同于mysql事务级别中的Serializable
。 mysql 默认的事务处理级别是REPEATABLE-READ
,也就是可重复读。