1、併發問題的產生 html
多線程/進程同時操做(讀/寫)同一數據 sql
2、併發問題的種類 數據庫
- 丟失更新(lost update)
- 第一類更新丟失(回滾丟失): 當2個事務更新相同的數據源,若是第一個事務被提交,而另一個事務卻被撤銷,那麼會連同第一個事務所作的跟新也被撤銷。也就是說第一個事務作的跟新丟失了。
- 第二類更新丟失(覆蓋丟失): 第二類更新丟失實在實際應用中常常遇到的併發問題,他和不可重複讀本質上是同一類併發問題,一般他被看作不可重複讀的特例。當2個或這個多個事務查詢一樣的記錄而後各自基於最初的查詢結果更新該行時,會形成第二類丟失更新。由於每一個事務都不知道不知道其餘事務的存在,最後一個事務對記錄作的修改將覆蓋其餘事務對該記錄作的已提交的跟新。
- 髒讀(dirty read)
- 髒讀(事務沒提交,提早讀取):髒讀就是指當一個事務正在訪問數據,而且對數據進行了修改,而這種修改尚未提交到數據庫中,這時,另一個事務也訪問這個數據,而後使用了這個數據。
- 不可重複讀(non-repeatable read)
- 不可重複讀(兩次讀的不一致) :是指在一個事務內,屢次讀同一數據。在這個事務尚未結束時,另一個事務也訪問該同一數據。那麼,在第一個事務中的兩次讀數據之間,因爲第二個事務的修改,那麼第一個事務兩次讀到的的數據多是不同的。這樣就發生了在一個事務內兩次讀到的數據是不同的,所以稱爲是不可重複讀。例如,一個編輯人員兩次讀取同一文檔,但在兩次讀取之間,做者重寫了該文檔。當編輯人員第二次讀取文檔時,文檔已更改。原始讀取不可重複。若是隻有在做者所有完成編寫後編輯人員才能夠讀取文檔,則能夠避免該問題。
- 幻讀(phantom read)
- 幻讀(發生在其餘事務插入或刪除後):是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的數據進行了修改,這種修改涉及到表中的所有數據行。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入一行新數據。那麼,之後就會發生操做第一個事務的用戶發現表中還有沒有修改的數據行,就好象發生了幻覺同樣。例如,一個編輯人員更改做者提交的文檔,但當生產部門將其更改內容合併到該文檔的主複本時,發現做者已將未編輯的新材料添加到該文檔中。若是在編輯人員和生產部門完成對原始文檔的處理以前,任何人都不能將新材料添加到文檔中,則能夠避免該問題。
http://msdn.microsoft.com/en-us/library/aa213029(v=sql.80).aspx 編程
http://www.cnblogs.com/pyq228/archive/2012/05/26/2519447.html 安全
3、解決方案 多線程
- 隔離——使數據局部化(即,使用非共享數據、線程獨享),則線程安全
- 識別不變的數據——儘可能使用常量
- 加鎖——對不得不共享的可變數據進行加鎖
4、樂觀鎖策略——衝突檢測 併發
樂觀鎖策略一般是創建在數據的某種版本標記上。爲了檢測「丟失更新」,系統覈對將要更新的數據的版本標記和共享數據的版本標記,若是二者同樣,系統更新數據並更新版本標記,不然,只能放棄本次更新,從頭再來。 工具
5、悲觀鎖策略——衝突避免 測試
讀鎖(共享鎖S):對讀鎖開放,對寫鎖封閉( lock table 表名 in share mode) 大數據
寫鎖(排他說X):對讀鎖和寫鎖都封閉( select * from 表名 where 條件 for update)
6、死鎖——悲觀鎖會致使死鎖
死鎖解除方案:死鎖超時控制(會致使誤傷持鎖時間長的)、死鎖檢測機制
死鎖預防方案:
- 強制在開始時就得到全部可能須要的鎖,此後就不容許再得到更多的鎖。
- 規定每一個人獲取鎖的順序。例如按字母順序。
- 當取不到鎖的時候,自動犧牲。
- 。。。。
7、事務——處理併發問題的主要工具
ACID屬性:原子性(Actomicity)、一致性(Consistency)、隔離性(Isolation)、持久性(Durability)
使用事務時,要防止 鎖升級(lock escalation),若是一個事務鎖住了一個表中的許多行,則數據庫可能沒法處理那麼多鎖,只能將鎖升級到鎖住整個表。
8、事務隔離級別——事務之間用鎖相互隔開
隔離級別就是對事務併發控制的等級。ANSI/ ISO SQL將其分爲串行化(SERIALIZABLE)、可重複讀(REPEATABLE READ)、讀已提交(READ COMMITED)、讀未提交(READ UNCOMMITED)四個等級。爲了實現隔離級別一般數據庫 採用鎖(Lock)。通常在編程的時候只須要設置隔離等級,至於具體採用什麼鎖則由數據庫來設置。
9、JDBC定義的事務隔離級別
- Connection. TRANSACTION_NONE 說明不支持事務。
- Connection. TRANSACTION_READ_UNCOMMITTED 說明在提交前一個事務能夠看到另外一個事務的變化。這樣髒讀、不可重複的讀和虛讀都是容許的。
- Connection. TRANSACTION_READ_COMMITTED 說明讀取未提交的數據是不容許的。這個級別仍然容許不可重複的讀和虛讀產生。
- Connection. TRANSACTION_REPEATABLE_READ 說明事務保證可以再次讀取相同的數據而不會失敗,但虛讀仍然會出現。
- Connection. TRANSACTION_SERIALIZABLE 是最高的事務級別,它防止髒讀、不可重複的讀和虛讀。
10、Spring事務隔離級別的定義和配置
- TransactionDefinition.ISOLATION_DEFAULT 這是一個PlatfromTransactionManager默認的隔離級別,使用數據庫默認的事務隔離級別.另外四個與JDBC的隔離級別相對應
- TransactionDefinition.ISOLATION_READ_UNCOMMITTED 這是事務最低的隔離級別,它充許別外一個事務能夠看到這個事務未提交的數據。這種隔離級別會產生髒讀,不可重複讀和幻像讀.
- TransactionDefinition.ISOLATION_READ_COMMITTED 保證一個事務修改的數據提交後才能被另一個事務讀取。另一個事務不能讀取該事務未提交的數據。這種事務隔離級別能夠避免髒讀出現,可是可能會出現不可重複讀和幻像讀。
- TransactionDefinition.ISOLATION_REPEATABLE_READ 這種事務隔離級別能夠防止髒讀,不可重複讀。可是可能出現幻像讀。它除了保證一個事務不能讀取另外一個事務未提交的數據外,還保證了避免下面的狀況產生(不可重複讀)。
- TransactionDefinition.ISOLATION_SERIALIZABLE 這是花費最高代價可是最可靠的事務隔離級別。事務被處理爲順序執行。除了防止髒讀,不可重複讀外,還避免了幻像讀。
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" isolation="READ_COMMITTED" />
</tx:attributes>
</tx:advice>
11、幾大數據庫的默認事務隔離級別
隔離級別每種數據庫都不同,若是不指定這個屬性的話,就是 default
- SQL Server :Read Commited
- Oracle: Read Commited
- MySQL : Repeatable Read
附錄一:Oracle的鎖
Oracle鎖分爲兩大類:數據鎖(DML鎖)和字典鎖。字典鎖包括語法分析鎖和DDL鎖,有DBMS控制,用戶無權控制。
Oracle 5種數據鎖:共享鎖、排他鎖、行級共享鎖(RS鎖)、行級排他鎖(RX鎖)、共享行級排他鎖(SRX鎖)。加鎖粒度包括行級和表級。
數據鎖相容矩陣:
S X RS RX SRX
S Y N Y N N
X N N N N N
RS Y N Y Y Y
RX N N Y Y N
SRX N N Y N N
通常狀況下,Oracle自行加鎖,用戶也能夠經過lock table等語句進行加鎖。
Oracle默認狀況下,讀數據不加鎖,而是經過回滾段防止髒讀和保證可重複讀。
Oracle具備死鎖檢查功能,週期性檢查系統是否有死鎖,若是存在死鎖,則撤銷執行更新操做次數最少的事務。
附錄二:JDBC事務隔離級別的測試
view plain
- @Test
- public void testDbTransactionIsolation() throws ClassNotFoundException,
- SQLException {
- Connection cn = getConnection();
- System.out.print("默認的事務隔離級別是:");
- printTransactionIsolation(cn);
-
- setTransactionIsolation(cn, Connection.TRANSACTION_NONE,
- "TRANSACTION_NONE");
- setTransactionIsolation(cn, Connection.TRANSACTION_READ_UNCOMMITTED,
- "TRANSACTION_READ_UNCOMMITTED");
- setTransactionIsolation(cn, Connection.TRANSACTION_READ_COMMITTED,
- "TRANSACTION_READ_COMMITTED");
- setTransactionIsolation(cn, Connection.TRANSACTION_REPEATABLE_READ,
- "TRANSACTION_REPEATABLE_READ");
- setTransactionIsolation(cn, Connection.TRANSACTION_SERIALIZABLE,
- "TRANSACTION_SERIALIZABLE");
-
- cn.close();
- }
-
- private void setTransactionIsolation(Connection cn, int level,
- String levelDiscription) {
- try {
- System.out.print("設置事務隔離級別爲:" + levelDiscription + ",");
- cn.setTransactionIsolation(level);
- System.out.print("設置成功,");
- printTransactionIsolation(cn);
- } catch (Exception e) {
- System.out.println("設置失敗:" + e.getMessage());
- }
- }
-
- private void printTransactionIsolation(Connection cn) throws SQLException {
- int level = cn.getTransactionIsolation();
- if (level == Connection.TRANSACTION_NONE)
- System.out.println("TRANSACTION_NONE");
- else if (level == Connection.TRANSACTION_READ_UNCOMMITTED)
- System.out.println("TRANSACTION_READ_UNCOMMITTED");
- else if (level == Connection.TRANSACTION_READ_COMMITTED)
- System.out.println("TRANSACTION_READ_COMMITTED");
- else if (level == Connection.TRANSACTION_REPEATABLE_READ)
- System.out.println("TRANSACTION_REPEATABLE_READ");
- else if (level == Connection.TRANSACTION_SERIALIZABLE)
- System.out.println("TRANSACTION_SERIALIZABLE");
- }