基于关系型数据库的事务控制
前文讲到程序员面对事务ACID特性如何实现一筹莫展,于是本文进入程序员穷开心的表演时间。
众所周知,现代关系型数据库天然支持实物ACID,但数据库是怎样实现ACID的呢?
** 数据库实现ACID的核心技术是并发控制和日志技术 **
- 并发控制:保证并发操作的正确性(2PL)
- 日志:保证故障场景下可恢复(Undo/Redo,WAL协议)
现代数据库均基于Write ahead logging实现ACID,也就是预写式日志(WAL)。WAL的中心思想是对数据文件的修改必须是只能发生在这些修改已经记录了日志之后 – 也就是说,在日志记录冲刷到永久存储器之后. 如果我们遵循这个过程,那么我们就不需要在每次事务提交的时候 都把数据页冲刷到磁盘,因为我们知道在出现崩溃的情况下, 我们可以用日志来恢复数据库:任何尚未附加到数据页的记录 都将先从日志记录中重做(这叫向前滚动恢复,也叫做 REDO) 然后那些未提交的事务做的修改将被从数据页中删除 (这叫向后滚动恢复 - UNDO)。
程序员穷开心假装自己会写数据库核心代码:
开始
预写式日志[声明一个事务的唯一标记]
查看李雷是否有一百元
李雷账号pk=1减少100元
韩梅梅账号pk-=2增加100元
预写式日志[标明该事务提交]
结束
穷开心:如果你在一个数据库中,可以直接使用数据库的事务机制保证ACID。
开心:你说的我都不懂,可我用的就是Mysql,也是现代关系型数据库RDBMS。可问题还是存储。
穷开心:(-_-)~
那一定是你使用的姿势不对。
开心:你胡说,我是按照csdn上直接照抄过来的(-_-)~
JAVA代码使用事务的正确姿势
try{
//STEP 2: Register JDBC driver
Class.forName("com.mysql.jdbc.Driver");
//STEP 3: Open a connection
System.out.println("Connecting to database...");
conn = DriverManager.getConnection(DB_URL,USER,PASS);
//STEP 4: Set auto commit as false.
conn.setAutoCommit(false);
//STEP 5: Execute a query to create statment with
// required arguments for RS example.
System.out.println("Creating statement...");
stmt = conn.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
//STEP 6: INSERT a row into Employees table
System.out.println("Inserting one row....");
String SQL = "INSERT INTO Employees " +
"VALUES (106, 28, 'Curry', 'Stephen')";
stmt.executeUpdate(SQL);
//STEP 7: INSERT one more row into Employees table
SQL = "INSERT INTO Employees " +
"VALUES (107, 32, 'Kobe', 'Bryant')";
stmt.executeUpdate(SQL);
//STEP 8: Commit data here.
System.out.println("Commiting data here....");
conn.commit();
//STEP 9: Now list all the available records.
String sql = "SELECT id, first, last, age FROM Employees";
ResultSet rs = stmt.executeQuery(sql);
System.out.println("List result set for reference....");
printRs(rs);
//STEP 10: Clean-up environment
rs.close();
stmt.close();
conn.close();
}catch(SQLException se){
//Handle errors for JDBC
se.printStackTrace();
// If there is an error then rollback the changes.
System.out.println("Rolling back data here....");
try{
if(conn!=null)
conn.rollback();
}catch(SQLException se2){
se2.printStackTrace();
}//end try
}catch(Exception e){
//Handle errors for Class.forName
e.printStackTrace();
}finally{
//finally block used to close resources
try{
if(stmt!=null)
stmt.close();
}catch(SQLException se2){
}// nothing we can do
try{
if(conn!=null)
conn.close();
}catch(SQLException se){
se.printStackTrace();
}//end finally try
}//end try
穷开心:看到没,java中使用数据库事务需要主动声明。默认情况下数据库连接配置是autoCommit=true。即使你不主动commit数据,每个操作都会自动提交。如果你有两个操作,数据库不会保证两个操作在一个事务中。
开心:说得好像有道理。
开心:问题是JDBC是什么年代的东西啊~~~~~,谁还在自己写代码调用DB Connection?
穷开心:JDBC一点都不老土,现代绝大部分Java orm框架都是基于JDBC的,核心实现代码一样是上面这段。当然我能理解你spring boot党的感受,刚才只是让你涨涨见识。
开心:(-_-)~
上一篇: 从一路php面试题说起
下一篇: 如何避免并发情况下的重复提交