- MySQL和Oracle比较
MySQL数据库 默认情况下一条SQL就是一个单独事务,事务是自动提交的。
Oracle 数据库 默认情况下事务不是自动提交,所有SQL都将处于一个事务中,你需要手动进行commit提交/rollback回滚。 - 事务使用
2.1 MySQL中使用事务
start transaction 开启事务(所有对数据表增加、修改、删除操作 临时表进行)
rollback 回滚事务 (取消刚才的操作)
commit 提交事务(确认刚才的操作)
SQL语言中只有DML才能被事务管理 insert update delete。
2.2 JDBC中使用事务
Connection.setAutoCommit(false); // 设置不自动提交
Connection.rollback();
Connection.commit();
2.3 事务回滚点
当时事务特别复杂,有些情况不会回滚到事务最开始状态,需要将事务回滚到指定位置。
Savepoint sp = conn.setSavepoint(); 设置回滚点
Conn.rollback(sp); 事务回滚到指定位置
public void demo3() {
// 创建person 表 插入20000条数据,如果插入过程中发生错误,保证插入的数据是1000的整数倍
Connection conn = null;
PreparedStatement stmt = null;
Savepoint savepoint = null;
try {
conn = JDBCUtils.getConnection();// 连接数据库
conn.setAutoCommit(false);// 设置不自动提交
savepoint = conn.setSavepoint();// 设置回滚点
String sql = "insert into person values(?,?)";// 设置预编译的sql语句
stmt = conn.prepareStatement(sql);// 预编译sql
for (int i = 0; i < 20000; i++) {
// 给添加的参数赋值
stmt.setInt(1, i);
stmt.setString(2, "name" + i);
// 添加到批处理
stmt.addBatch();
// 制造错误
if (i == 4699) {
int d = 1 / 0;
}
// 每隔200条向数据库发送一次
if (i % 200 == 0) {
stmt.executeBatch();// 执行批处理
stmt.clearBatch();// 清楚批处理缓存
}
if (i % 1000 == 0) {
savepoint = conn.setSavepoint();// 执行1000条插入之后再次保存回滚点
conn.commit();// 提交
}
}
stmt.executeBatch();// 确保缓存没有内容
} catch (Exception e) {
try {
conn.rollback(savepoint);// 出现错误则回到回滚点
conn.commit();// 再次进行提交
} catch (SQLException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
e.printStackTrace();
} finally {
JDBCUtils.release(stmt, conn);
}
}
3.事务隔离
不考虑事务隔离将引发:脏读、不可重复读、虚读
脏读:一个事务读取另一个事务 未提交数据 —- 是数据库隔离中最重要问题
不可重复读:一个事务读取另一个事务 已提交数据,在一个事务中两次读取结果不同
虚读:一个事务读取另一个事务 插入数据,造成在一个事务中两次读取记录条数不同
* 虚读 不可重复读 区别? 不可重复读读取 update数据 ,虚读读取insert 数据
数据库为了解决三类隔离引发问题:提供四个数据隔离级别
Serializable : 串行处理 —- 解决三类问题
Repeatable read :可以解决 不可重复读、脏读,会发生虚读 ——- MySQL 默认级别
read committed : 可以 解决脏读 ,会发生 不可重复读、虚读 ——– Oracle默认级别
read uncommitted : 会导致三类问题发生
Serializable > Repeatable read > read committed > read uncommitted
数据库隔离问题危害 脏读> 不可重复读 > 虚读
安全级别越高,处理效率越低;安全级别越低,效率高
在数据库中通过
set (session)transaction isolation level 设置事务隔离级别
select @@tx_isolation 查询当前事务隔离级别
4.事务丢失更新
介绍:两个事务同时读取同一条记录,A先修改记录,B也修改记录(B是不知道A修改过),B提交数据后B的修改结果覆盖了A的修改结果。
解决方法: 悲观锁 和乐观锁
4.1 悲观锁
原理:使用数据库内部锁机制,进行table的锁定,在A修改数据时,A就将数据锁定,B此时无法进行修改。无法发生两个事务同时修改,假设丢失更新会发生。
在mysql中默认情况下,当你修改数据,自动为数据加锁(在事务中)。事务和锁和不可分开的,锁一定是在事务中使用 ,当事务关闭锁自动释放。
(1)读锁:读锁(共享锁) 一张表可以添加多个读锁,如果表添加读锁(不是当前事务添加的),该表不可以修改。共享锁容易发生死锁。
select * from account lock in share mode;
(2)写锁:(排它锁) 一张表只能加一个排它锁,排他锁和其它共享锁、排它锁都具有互斥效果 。
select * from account for update ;
可以使用排它锁解决丢失更新问题。
4.2 乐观锁
原理:使用不是数据库锁机制,而是一个特殊标记字段,如果控制字段状态和内容,得知数据是否发生并发访问!假设丢失更新不会发生
* 数据库timestamp 时间戳字段。
通过给数据库设置一个时间戳。比较操作数据的时间,来解决丢失更新问题。