1. 事務概述
1.1 事務概念
事務就是一件事情,這個事情可能有多個單元組成,要求這些單元要麼都成功要麼都不成功。
1.2 Mysql中的事務
(1)Mysql中默認事務處理
在mysql登錄的情況下執行以下命令:showvariables like '%commit%';
autocommint 值是 on,說明開啓自動提交(Oracle中 autocommit 默認就是 off)。mysql數據庫默認是開啓事務的,一條sql一個事務。oracle它默認情況下autocommit是off,就需要手動提交事務。關閉mysql的自動事務處理命令:
set autocommit =off;( set autocommit = 0)
如果設置autocommit 爲 off,意味着以後每條SQL 都會處於同一個事務中,相當於第一條SQL執行前執行了 start transaction。
(2)Mysql中常見事務操作命令
starttransaction:-- 開啓事務一旦手動開啓了事務,事務自動提交失效.
commit;-- 提交事務
rollback;-- 事務回滾
1.3 JDBC中的事務
java.sql.Connection接口中提供了關於事務操作的方法:
setAutoCommit(boolean flag); 參數爲false相當於starttransaction
commit(); 事務提交
rollback(); 事務回滾
設置事務回滾點:
SavepointsetSavepoint(String name)
在當前事務中創建一個具有給定名稱的保存點,並返回表示它的新 Savepoint 對象。
例如: Savepoint sp = conn.setSavepoint();
Conn.rollback(sp);
2. 事務的特性(ACID)
原子性(Atomicity):即表示事務中所有操作是不可再分割的原子單位。事務中所有操作要麼全部執行成功, 要麼全部執行失敗;
一致性(Consistency):事務執行後,數據庫狀態與其它業務規則保持一致。例如轉賬業務,無論事務執行成功與否,參與轉賬的兩個賬號餘額之和應該是不變的;
隔離性(Isolation):指在併發操作中,不同事務之間應該隔離開來,使每個併發中的事務不會相互干擾;
持久性(Durability):指的是一旦事務提交成功,事務中所有的數據操作都必須被持久化到數據庫中,即使提交事務後,數據庫馬上崩潰,在數據庫重啓時,也必須能保證通過某種機制恢復數據。
3. 事務的隔離級別
3.1 不考慮事務隔離性將會產生的問題
多個線程開啓各自事務操作數據庫中數據時,數據庫系統要負責隔離操作,以保證各個線程在獲取數據時的準確性。如果不考慮隔離性,可能會引發如下問題:
(1)髒讀:指一個事務讀取另一個事務未提交的數據
(2)不可重複讀:在一個事務先後兩次讀取發生數據不一致情況,第二次讀取到另一個事務已經提交數據(強調數據更新 update)
(3)虛讀(幻讀) :在一個事務中,第二次讀取發生數據記錄數的不同,讀取到另一個事務已經提交數據(強調數據記錄變化 insert )
3.2事務的4種隔離級別
數據庫內部定義了四種隔離級別,用於解決三種隔離問題
(1)SERIALIZABLE(串行化)
不會出現任何併發問題,因爲它是對同一數據的訪問是串行的,非併發訪問的;
性能最差;
(2)REPEATABLEREAD(可重複讀)
防止髒讀和不可重複讀;(不能處理幻讀)
性能比SERIALIZABLE好
(3)READCOMMITTED(讀已提交數據)
防止髒讀;(不能處理不可重複讀、幻讀)
性能比REPEATABLE READ好
(4)READUNCOMMITTED(讀未提交數據)
可能出現任何事務併發問題
性能最好
mysql數據庫默認的事務隔離級別-----repeatable read級別.
oracle數據默認的事務隔離級別 ----read committed
安全性:serializable> repeatable read > read committed > read uncommitted
性能 :serializable <repeatable read < read committed < read uncommitted
3.3 事務隔離級別的設置
(1)mysql中設置
數據庫默認有事務的隔離級別,mysql 中查看與修改事務的隔離級別
set session transaction isolation level 隔離級別;設置事務隔離級別
select @@tx_isolation; 查詢當前事務隔離級別
(2)jdbc中設置事務隔離級別
在java.sql.Connection接口中提供
con. setTransactionIsolation(int level)
參數可選值如下:
Connection.TRANSACTION_READ_UNCOMMITTED;
Connection.TRANSACTION_READ_COMMITTED;
Connection.TRANSACTION_REPEATABLE_READ;
Connection.TRANSACTION_SERIALIZABLE。
(注意,不能使用 Connection.TRANSACTION_NONE,因爲它指定了不受支持的事務。)
4. ThreadLocal簡介
public classThreadLocal<T>extends Object
該類提供了線程局部(thread-local) 變量。這些變量不同於它們的普通對應物,因爲訪問某個變量(通過其 get 或 set 方法)的每個線程都有自己的局部變量,它獨立於變量的初始化副本。ThreadLocal 實例通常是類中的 private static 字段,它們希望將狀態與某一個線程(例如,用戶 ID 或事務 ID)相關聯。
它的底層是使用了一個Map集合
Map<Thread,Object>
它的key就是當前的線程對象.
set(Object obj) 它就相當於 map.put(Thread.currentThread(),obj);
get()它就相當於 map.get(Thread.currentThread());
ThreadLocal可以理解成是一個Map集合Map<Thread,Object>,set方法是向ThreadLocal中存儲數據,那麼當前的key值就是當前線程對象。get方法是從ThreadLocal中獲取數據,它是根據當前線程對象來獲取值。如果我們是在同一個線程中,只要在任意的一個位置存儲了數據,在其它位置上,就可以獲取到這個數據。
5. 事務的丟失更新問題(lost update )
5.1 事務丟失更新概述
兩個或多個事務更新同一行,但這些事務彼此之間都不知道其它事務進行的修改,因此後一個的更改覆蓋了前一個修改。
5.2丟失更新問題的解決方法
(1)悲觀鎖(PessimisticLocking)-------假設丟失更新一定會發生,利用數據庫內部鎖機制,管理事務
mysql數據庫內部提供的兩種常用鎖機制:共享鎖(讀鎖)和排它鎖(寫鎖),允許一張數據表中數據記錄,添加多個共享鎖,添加共享鎖記錄,對於其他事務可讀不可寫的
一張數據表中數據記錄,只能添加一個排它鎖,在添加排它鎖的數據不能再添加其他共享鎖和排它鎖的,對於其他事物可讀不可寫的。所有數據記錄修改操作,自動爲數據添加排它鎖
添加共享鎖方式:select* from account lock in share mode ;(讀鎖、共享鎖)
添加排它鎖方式:select* from account for update; (寫鎖、排它鎖)
鎖必須在事務中添加 ,如果事務結束了 鎖就釋放了
解決丟失更新:事務在修改記錄過程中,鎖定記錄,別的事務無法併發修改。
(2)樂觀鎖(Optimistic Locking)-------假設丟失更新不會發生,採用程序中添加版本字段解決丟失更新問題。
採用記錄的版本字段,來判斷記錄是否修改過-------------- timestamp【通過時間戳字段】
timestamp 可以自動更新
create table product (
id int,
name varchar(20),
updatetime timestamp
);
insert into product values(1,'小米',null);
update product set name='三星' where id = 1;
timestamp 在插入和修改時都會自動更新爲當前時間
解決丟失更新:在數據表添加版本字段,每次修改過記錄後,版本字段都會更新,如果讀取是版本字段,與修改時版本字段不一致,說明別人進行修改過數據(重改)。
事務總結:
(1)事務的特性:ACID;
(2)事務開始邊界與結束邊界:開始邊界(con.setAutoCommit(false)),結束邊界(con.commit()或con.rollback());
(3)事務的隔離級別: READ_UNCOMMITTED、READ_COMMITTED、REPEATABLE_READ、SERIALIZABLE。多個事務併發執行時才需要考慮併發事務。