前文講到程序員面對事務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黨的感受,剛纔只是讓你漲漲見識。
開心:(-_-)~