數據庫事務( transaction)是訪問並可能操做各類數據項的一個數據庫操做序列,這些操做要麼所有執行,要麼所有不執行,是一個不可分割的工做單位。事務由事務開始與事務結束之間執行的所有數據庫操做組成。html
簡言之就是,更新、新增、刪除的sql要麼一塊兒成功,要麼一塊兒失敗(回滾)。java
事務的ACID是怎麼是實現的見個人另外一篇文章:Mysql事務中的ACID是怎麼實現的spring
可能會出現髒讀、幻讀、不可重複讀的問題。sql
好比說A事務修改了一條數據,B事務讀取了該條事務,A事務回滾,B事務讀取了錯誤的數據,這叫作髒讀。數據庫
例子:數據庫中張三的工資爲5K,事務B讀取成了8k的行爲叫作髒讀。express
時間 | 事務A | 事務B |
T1 | 開始事務 | |
T2 | 開始事務 | |
T3 | 張三的工資從5K改成8K(update操做) | |
T4 | 讀取到張三的工資爲8k(select操做) | |
T5 | 提交事務 | |
T6 | 事務提交失敗回滾,數據庫中張三的數據變回5Kapache |
例子:張三在一個事務中先後兩次讀取的工資的金額不一樣的狀況叫作不可重複讀。app
時間 | 事務A | 事務B |
T1 | 開始事務 | |
T2 | 開始事務 | |
T3 | 讀取張三的工資爲5K(select操做) | |
T4 | 修改的張三的工資爲8k(update操做) | |
T5 | 提交事務 | |
T6 | 讀取張三的工資爲8K(select操做)less |
是指當事務不是獨立執行時發生的一種現象,例如第一個事務對一個表中的數據進行了修改,這種修改涉及到表中的所有數據行。同時,第二個事務也修改這個表中的數據,這種修改是向表中插入\刪除一行數據。那麼,之後就會發生操做第一個事務的用戶發現表中還有沒有修改的數據行,就好象發生了幻覺同樣。ide
例子:初始數據庫中,有張3、李四工資爲5K的數據
時間 | 事務A | 事務B |
T1 | 開始事務 | |
T2 | 開始事務 | |
T3 | 讀取張三李四的工資爲5K |
|
T4 | 添加王五的工資爲5K的數據(add操做) /刪除李四的工資爲5K的數據(delete操做) |
|
T5 | 提交事務 | |
T6 | 修改工資爲5k的員工工資改成6K(update操做) 操做影響了3條(添加操做)/1條(刪除操做)數據 |
|
T7 | 查詢員工工資, (預期張三李四工資6K) 實際上張三李四王五工資爲6K/張三工資爲6K |
Spring中有一個@Transactional註解/* * Copyright 2002-2016 the original author or authors.
* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //上面就是一些版權說明的內容 package org.springframework.transaction.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Inherited; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.springframework.core.annotation.AliasFor; import org.springframework.transaction.TransactionDefinition; /** * Describes transaction attributes on a method or class. * * <p>This annotation type is generally directly comparable to Spring's * {@link org.springframework.transaction.interceptor.RuleBasedTransactionAttribute} * class, and in fact {@link AnnotationTransactionAttributeSource} will directly * convert the data to the latter class, so that Spring's transaction support code * does not have to know about annotations. If no rules are relevant to the exception, * it will be treated like * {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute} * (rolling back on {@link RuntimeException} and {@link Error} but not on checked * exceptions). * * <p>For specific information about the semantics of this annotation's attributes, * consult the {@link org.springframework.transaction.TransactionDefinition} and * {@link org.springframework.transaction.interceptor.TransactionAttribute} javadocs. * * @author Colin Sampaleanu * @author Juergen Hoeller * @author Sam Brannen * @since 1.2 * @see org.springframework.transaction.interceptor.TransactionAttribute * @see org.springframework.transaction.interceptor.DefaultTransactionAttribute * @see org.springframework.transaction.interceptor.RuleBasedTransactionAttribute */
//上面就是說,這個類的方法主要是處理事務的問題,具體使用到的註解去具體的註解類裏面看
@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Transactional { /** * Alias for {@link #transactionManager}. * @see #transactionManager */
//事務管理器的名字默認是空的,你能夠本身賦值 @AliasFor("transactionManager")//這個註解主要是標籤的做用,具體實現能夠去看他專門的類 String value() default ""; /** * A <em>qualifier</em> value for the specified transaction. * <p>May be used to determine the target transaction manager, * matching the qualifier value (or the bean name) of a specific * {@link org.springframework.transaction.PlatformTransactionManager} * bean definition. * @since 4.2 * @see #value */
//去配置文件或者配置類裏面找上面定義的那個名字的事務管理器的名字,找到匹配的事務管理器 @AliasFor("value") String transactionManager() default ""; /** * The transaction propagation type. * <p>Defaults to {@link Propagation#REQUIRED}. * @see org.springframework.transaction.interceptor.TransactionAttribute#getPropagationBehavior() */
//定義事務傳播方式的方法,默認是Propagation.REQUIRED枚舉
//REQUIRED 支持當前事務,若是不存在則建立一個新事務。 相似於同名的EJB事務屬性,
就是說這個傳播方式下,事務內是能夠嵌套事務的,子事務以主事務管理,
網上說的事務裏面不能嵌套事務操做是片面的,具體是要看事務的傳播類型是怎麼定義的
//SUPPORTS 若是上下文存在事物,則支持事物加入,若是沒有事物,則使用非事物的方式執行。
//MANDATORY 該級別的事物要求上下文中必需要存在事物,不然就會拋出異常
//REQUIRES_NEW 每次都會新建事物,而且上下文的事物掛機,執行當前新建事物完成之後,上下文事物回覆再執行。
//NOT_SUPPORTED 當前級別的特色就是上下文中存在事物,則掛起事物,執行當前邏輯,結束後回覆上下文的事物。
//NEVER 上下文中不能存在事物,一旦有事物,就拋出runtime異常,強制中止執行
//NESTED 若是上下文中存在事物,則嵌套事物執行,若是不存在事物,則新建事物。
//具體詳情見 org.springframework.transaction.TransactionDefinition類
Propagation propagation() default Propagation.REQUIRED; /** * The transaction isolation level. * <p>Defaults to {@link Isolation#DEFAULT}. * @see org.springframework.transaction.interceptor.TransactionAttribute#getIsolationLevel() */
//事務的隔離級別
//TransactionDefinition.ISOLATION_DEFAULT 使用基礎數據存儲的默認隔離級別。 全部其餘級別對應於JDBC隔離級別。
//TransactionDefinition.ISOLATION_READ_UNCOMMITTED 表示髒讀,不可重複讀和幻像讀的常數能夠發生,啥問題都沒解決基本不用
//TransactionDefinition.ISOLATION_READ_COMMITTED 一個常量,指示防止髒讀;不可重複讀取和幻像讀取可能會發生。此級別僅禁止事務,讀取行中未提交的更改
//TransactionDefinition.ISOLATION_REPEATABLE_READ 指示防止髒讀和不可重複讀的常量,可能會發生幻像讀取
//TransactionDefinition.ISOLATION_SERIALIZABLE 一個常數,指示防止髒讀,不可重複讀和幻像讀
//具體詳情見 org.springframework.transaction.TransactionDefinition
Isolation isolation() default Isolation.DEFAULT;
/** * The timeout for this transaction. * <p>Defaults to the default timeout of the underlying transaction system. * @see org.springframework.transaction.interceptor.TransactionAttribute#getTimeout() */
//事務超時時間,默認不設置,默認值爲-1
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT; /** * {@code true} if the transaction is read-only. * <p>Defaults to {@code false}. * <p>This just serves as a hint for the actual transaction subsystem; * it will <i>not necessarily</i> cause failure of write access attempts. * A transaction manager which cannot interpret the read-only hint will * <i>not</i> throw an exception when asked for a read-only transaction * but rather silently ignore the hint. * @see org.springframework.transaction.interceptor.TransactionAttribute#isReadOnly() */
//是否爲只讀事務,只讀事務通常是經過共享鎖,讀寫事務使用排他鎖
boolean readOnly() default false; /** * Defines zero (0) or more exception {@link Class classes}, which must be * subclasses of {@link Throwable}, indicating which exception types must cause * a transaction rollback. * <p>By default, a transaction will be rolling back on {@link RuntimeException} * and {@link Error} but not on checked exceptions (business exceptions). See * {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)} * for a detailed explanation. * <p>This is the preferred way to construct a rollback rule (in contrast to * {@link #rollbackForClassName}), matching the exception class and its subclasses. * <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(Class clazz)}. * @see #rollbackForClassName * @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable) */
//檢查到指定異常後回滾
Class<? extends Throwable>[] rollbackFor() default {}; /** * Defines zero (0) or more exception names (for exceptions which must be a * subclass of {@link Throwable}), indicating which exception types must cause * a transaction rollback. * <p>This can be a substring of a fully qualified class name, with no wildcard * support at present. For example, a value of {@code "ServletException"} would * match {@code javax.servlet.ServletException} and its subclasses. * <p><b>NB:</b> Consider carefully how specific the pattern is and whether * to include package information (which isn't mandatory). For example, * {@code "Exception"} will match nearly anything and will probably hide other * rules. {@code "java.lang.Exception"} would be correct if {@code "Exception"} * were meant to define a rule for all checked exceptions. With more unusual * {@link Exception} names such as {@code "BaseBusinessException"} there is no * need to use a FQN. * <p>Similar to {@link org.springframework.transaction.interceptor.RollbackRuleAttribute#RollbackRuleAttribute(String exceptionName)}. * @see #rollbackFor * @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable) */
//上一個方法中異常類的名字
String[] rollbackForClassName() default {}; /** * Defines zero (0) or more exception {@link Class Classes}, which must be * subclasses of {@link Throwable}, indicating which exception types must * <b>not</b> cause a transaction rollback. * <p>This is the preferred way to construct a rollback rule (in contrast * to {@link #noRollbackForClassName}), matching the exception class and * its subclasses. * <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(Class clazz)}. * @see #noRollbackForClassName * @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable) */
//若是你在聲明異常類中發生的異常,不回滾
Class<? extends Throwable>[] noRollbackFor() default {}; /** * Defines zero (0) or more exception names (for exceptions which must be a * subclass of {@link Throwable}) indicating which exception types must <b>not</b> * cause a transaction rollback. * <p>See the description of {@link #rollbackForClassName} for further * information on how the specified names are treated. * <p>Similar to {@link org.springframework.transaction.interceptor.NoRollbackRuleAttribute#NoRollbackRuleAttribute(String exceptionName)}. * @see #noRollbackFor * @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable) */
//上一個異常類中的類名
String[] noRollbackForClassName() default {}; }
上述主要爲Spring中Transactional中的方法使用介紹,如今具體來介紹一下錯誤讀的問題是怎麼解決的。
此處要引入三個概念,分別是記錄鎖,臨建鎖,間隙鎖
記錄鎖:記錄鎖就是爲某行記錄加鎖,列必須爲惟一索引列或主鍵列,不然加的鎖就會變成臨鍵鎖,
查詢語句必須爲精準匹配 = ,不能爲 >、<、like等,不然也會退化成臨鍵鎖。
如
間隙鎖:間隙鎖基於非惟一索引,它鎖定一段範圍內的索引記錄。好比查詢字段區間爲4-7,即1-5內的記錄行都會被鎖住,五、6 的數據行的會被阻塞,可是 4 和 7 兩條記錄行並不會被鎖住。
臨建鎖:臨鍵鎖能夠理解爲一種特殊的間隙鎖,上面說過了經過臨建鎖能夠解決幻讀的問題。 每一個數據行上的非惟一索引列上都會存在一把臨鍵鎖,當某個事務持有該數據行的臨鍵鎖時,會鎖住一段左開右閉區間的數據。