一、什么是資料庫事務?
資料庫事務( transaction)是訪問并可能操作各種資料項的一個資料庫操作序列,這些操作要么全部執行,要么全部不執行,是一個不可分割的作業單位,事務由事務開始與事務結束之間執行的全部資料庫操作組成,
簡言之就是,更新、新增、洗掉的sql要么一起成功,要么一起失敗(回滾),
事務的ACID是怎么是實作的見我的另一篇文章:Mysql事務中的ACID是怎么實作的
二、使用資料庫的事務可能出現什么問題?
可能會出現臟讀、幻讀、不可重復讀的問題,
1.什么是臟讀?
比如說A事務修改了一條資料,B事務讀取了該條事務,A事務回滾,B事務讀取了錯誤的資料,這叫做臟讀,
例子:資料庫中張三的工資為5K,事務B讀取成了8k的行為叫做臟讀,
| 時間 | 事務A | 事務B |
| T1 | 開始事務 | |
| T2 | 開始事務 | |
| T3 | 張三的工資從5K改為8K(update操作) | |
| T4 | 讀取到張三的工資為8k(select操作) | |
| T5 | 提交事務 | |
| T6 |
事務提交失敗回滾,資料庫中張三的資料變回5K |
2.什么是不可重復讀?
例子:張三在一個事務中前后兩次讀取的工資的金額不同的情況叫做不可重復讀,
| 時間 | 事務A | 事務B |
| T1 | 開始事務 | |
| T2 | 開始事務 | |
| T3 | 讀取張三的工資為5K(select操作) | |
| T4 | 修改的張三的工資為8k(update操作) | |
| T5 | 提交事務 | |
| T6 |
讀取張三的工資為8K(select操作) |
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是如何完成事務的,臟讀幻讀不可重復讀是怎么解決的?
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內的記錄行都會被鎖住,5、6 的資料行的會被阻塞,但是 4 和 7 兩條記錄行并不會被鎖住,

臨建鎖:臨鍵鎖可以理解為一種特殊的間隙鎖,上面說過了通過臨建鎖可以解決幻讀的問題, 每個資料行上的非唯一索引列上都會存在一把臨鍵鎖,當某個事務持有該資料行的臨鍵鎖時,會鎖住一段左開右閉區間的資料,
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/253941.html
標籤:其他
