企业Java中事务隔离级别的初学者指南
介紹
基于ACID事務屬性的關系數據庫強一致性模型。 在本文中,我們將闡明對資源本地事務和JTA事務使用不同的事務隔離級別和各種配置模式的背后原因。
隔離和一致性
在關系數據庫系統中,原子性和持久性是嚴格的屬性,而一致性和隔離性或多或少是可配置的。 我們甚至不能將一致性與隔離性分開,因為這兩個屬性始終是相關的。
隔離級別越低,系統獲得的一致性越差。 從最小到最一致,有四個隔離級別:
- 讀未提交
- READ COMMITTED(防止臟讀)
- 可重復讀取(防止臟和不可重復讀取)
- 可序列化(防止臟的,不可重復的讀取和幻像讀取)
盡管最一致的SERIALIZABLE隔離級別是最安全的選擇,但大多數數據庫默認改為READ COMMITTED。 根據阿姆達爾定律 ,為了容納更多的并發事務,我們必須減少數據處理的串行部分。 鎖獲取間隔越短,數據庫可以處理的請求越多。
隔離度
如我們先前所展示的, 應用程序級可重復讀取與樂觀鎖定機制配對,對于防止長時間對話中的更新丟失非常方便。
在高度并發的環境中,樂觀鎖定可能會導致高事務失敗率。 與其他任何排隊機制一樣,悲觀鎖定在提供足夠的鎖定獲取時間間隔時可能會容納更多事務。
數據庫和隔離級別
除MySQL(使用REPEATABLE_READ)外,大多數關系數據庫系統的默認隔離級別為READ_COMMITTED。 所有數據庫都允許您設置默認的事務隔離級別。
通常,數據庫在多個應用程序之間共享,并且每個應用程序都有其自己的特定交易要求。 對于大多數事務,READ_COMMITTED隔離級別是最佳選擇,我們僅應針對特定業務案例覆蓋它。
這種策略被證明是非常有效的,它使我們對所有SQL事務的子集具有更嚴格的隔離級別。
數據源隔離級別
JDBC Connection對象允許我們為在該特定連接上發出的所有事務設置隔離級別。 建立新的數據庫連接是一個資源消耗過程,因此大多數應用程序都使用連接池 DataSource 。 連接池數據源還可以設置默認的事務隔離級別:
- DBCP
- DBCP2
- 光ikaCP
- 骨CP
- Bitronix交易管理器
與全局數據庫隔離級別設置相比,DataSource級別的事務隔離配置更加方便。 每個應用程序可以設置自己的特定并發控制級別。
我們甚至可以定義多個數據源,每個數據源都有一個定義的隔離級別。 這樣,我們可以動態選擇特定的隔離級別的JDBC連接。
休眠隔離級別
因為它必須同時支持資源本地事務和JTA事務,所以Hibernate提供了一種非常靈活的連接提供程序機制。
JTA事務需要XAConnection,并且JTA事務管理器負責提供XA兼容連接。
資源本地事務可以使用資源本地 DataSource ,在這種情況下,Hibernate提供了多個連接提供程序選項:
- 驅動程序管理器連接提供程序(不合并連接,因此僅用于簡單的測試方案)
- C3P0連接提供程序(委派連接以獲取對內部C3P0連接池數據源的調用)
- 數據源連接提供程序(委派連接以獲取對外部數據源的調用。
Hibernate提供了一個稱為hibernate.connection.isolation的事務隔離級別配置,因此我們將檢查所有上述連接提供者在獲得此特定設置后的行為。
為此,我們將要:
唯一不同的是連接提供程序配置。
驅動程序管理器連接提供程序
驅動程序管理器連接提供程序為配置的數據庫驅動程序提供了基本的DataSource包裝器。 您僅應將其用于測試方案,因為它不提供專業的連接池機制。
@Override protected Properties getProperties() {Properties properties = new Properties();properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");//driver settingsproperties.put("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver");properties.put("hibernate.connection.url", "jdbc:hsqldb:mem:test");properties.put("hibernate.connection.username", "sa");properties.put("hibernate.connection.password", "");//isolation levelproperties.setProperty("hibernate.connection.isolation", String.valueOf(Connection.TRANSACTION_SERIALIZABLE));return properties; }測試生成以下輸出:
WARN [main]: o.h.e.j.c.i.DriverManagerConnectionProviderImpl - HHH000402: Using Hibernate built-in connection pool (not for production use!) DEBUG [main]: c.v.h.m.l.t.TransactionIsolationDriverConnectionProviderTest - Transaction isolation level is SERIALIZABLEHibernate會話關聯的JDBC連接正在使用SERIALIZABLE事務隔離級別,因此hibernate.connection.isolation配置適用于此特定的連接提供程序。
C3P0連接提供者
Hibernate還提供了一個內置的C3P0連接提供程序。 像前面的示例一樣,我們只需要提供驅動程序配置設置,然后Hibernate代表我們實例化C3P0連接池。
@Override protected Properties getProperties() {Properties properties = new Properties();properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");//log settingsproperties.put("hibernate.hbm2ddl.auto", "update");properties.put("hibernate.show_sql", "true");//driver settingsproperties.put("hibernate.connection.driver_class", "org.hsqldb.jdbcDriver");properties.put("hibernate.connection.url", "jdbc:hsqldb:mem:test");properties.put("hibernate.connection.username", "sa");properties.put("hibernate.connection.password", "");//c3p0 settingsproperties.put("hibernate.c3p0.min_size", 1);properties.put("hibernate.c3p0.max_size", 5);//isolation levelproperties.setProperty("hibernate.connection.isolation", String.valueOf(Connection.TRANSACTION_SERIALIZABLE));return properties; }測試生成以下輸出:
Dec 19, 2014 11:02:56 PM com.mchange.v2.log.MLog <clinit> INFO: MLog clients using java 1.4+ standard logging. Dec 19, 2014 11:02:56 PM com.mchange.v2.c3p0.C3P0Registry banner INFO: Initializing c3p0-0.9.2.1 [built 20-March-2013 10:47:27 +0000; debug? true; trace: 10] DEBUG [main]: c.v.h.m.l.t.TransactionIsolationInternalC3P0ConnectionProviderTest - Transaction isolation level is SERIALIZABLE因此, hibernate.connection.isolation配置也適用于內部C3P0連接提供程序。
數據源連接提供程序
Hibernate不會強迫您使用特定的連接提供程序機制。 您只需提供一個DataSource,Hibernate就會在請求新的Connection時使用它。 這次,我們將創建一個成熟的DataSource對象,并將其傳遞給hibernate.connection.datasource配置。
@Override protected Properties getProperties() {Properties properties = new Properties();properties.put("hibernate.dialect", "org.hibernate.dialect.HSQLDialect");//log settingsproperties.put("hibernate.hbm2ddl.auto", "update");//data source settingsproperties.put("hibernate.connection.datasource", newDataSource());//isolation levelproperties.setProperty("hibernate.connection.isolation", String.valueOf(Connection.TRANSACTION_SERIALIZABLE));return properties; }protected ProxyDataSource newDataSource() {JDBCDataSource actualDataSource = new JDBCDataSource();actualDataSource.setUrl("jdbc:hsqldb:mem:test");actualDataSource.setUser("sa");actualDataSource.setPassword("");ProxyDataSource proxyDataSource = new ProxyDataSource();proxyDataSource.setDataSource(actualDataSource);proxyDataSource.setListener(new SLF4JQueryLoggingListener());return proxyDataSource; }測試生成以下輸出:
DEBUG [main]: c.v.h.m.l.t.TransactionIsolationExternalDataSourceConnectionProviderTest - Transaction isolation level is READ_COMMITTED這次似乎沒有考慮hibernate.connection.isolation 。 Hibernate不會覆蓋外部數據源,因此此設置在這種情況下是無用的。
如果使用外部數據源(例如,通過JNDI),則需要在外部數據源級別設置事務隔離。
要修復前面的示例,我們只需將外部數據源配置為使用特定的隔離級別:
protected ProxyDataSource newDataSource() {JDBCDataSource actualDataSource = new JDBCDataSource();actualDataSource.setUrl("jdbc:hsqldb:mem:test");actualDataSource.setUser("sa");actualDataSource.setPassword("");Properties properties = new Properties();properties.setProperty("hsqldb.tx_level", "SERIALIZABLE");actualDataSource.setProperties(properties);ProxyDataSource proxyDataSource = new ProxyDataSource();proxyDataSource.setDataSource(actualDataSource);proxyDataSource.setListener(new SLF4JQueryLoggingListener());return proxyDataSource; }生成以下輸出:
DEBUG [main]: c.v.h.m.l.t.TransactionIsolationExternalDataSourceExternalconfgiurationConnectionProviderTest - Transaction isolation level is SERIALIZABLEJava Enterprise事務隔離支持
Hibernate具有內置的Transaction API抽象層 ,可將數據訪問層與事務管理拓撲( 資源本地或JTA)隔離開。 雖然我們只能使用Hibernate事務抽象來開發應用程序,但將這種責任委托給中間件技術( JEE或Spring )更為常見。
Java企業版
JTA(Java事務API規范)定義了應如何由符合JEE的應用服務器管理事務。 在客戶端,我們可以使用TransactionAttribute批注來劃分事務邊界。 盡管我們可以選擇正確的事務傳播設置,但對于隔離級別我們不能這樣做。
JTA不支持事務范圍的隔離級別,因此我們必須訴諸于供應商特定的配置才能為XA DataSource提供特定的事務隔離設置。
彈簧
Spring @Transactional批注用于定義事務邊界。 與JEE相對,此批注允許我們配置:
- 隔離級別
- 異常類型回滾策略
- 傳播
- 只讀
- 超時
正如我將在本文稍后演示的那樣,隔離級別設置僅可用于資源本地事務。 因為JTA不支持事務范圍的隔離級別,所以Spring提供了IsolationLevelDataSourceRouter來克服使用應用程序服務器JTA DataSources時的這一缺點。
因為大多數DataSource實現只能采用默認的事務隔離級別,所以我們可以有多個這樣的DataSource,每個為特定的事務隔離級別提供連接。
邏輯事務(例如@Transactional )隔離級別設置由IsolationLevelDataSourceRouter自省,因此將連接獲取請求委托給特定的DataSource實現,該實現可以為具有相同事務隔離級別設置的JDBC連接提供服務。
因此,即使在JTA環境中,事務隔離路由器也可以提供獨立于供應商的解決方案,以基于每個事務覆蓋默認數據庫隔離級別。
Spring事務范圍的隔離級別
接下來,我將測試Spring事務管理對資源本地事務和JTA事務的支持。
為此,我將介紹一個事務性業務邏輯服務Bean:
@Service public class StoreServiceImpl implements StoreService {protected final Logger LOGGER = LoggerFactory.getLogger(getClass());@PersistenceContext(unitName = "persistenceUnit")private EntityManager entityManager;@Override@Transactional(isolation = Isolation.SERIALIZABLE)public void purchase(Long productId) { Session session = (Session) entityManager.getDelegate();session.doWork(new Work() {@Overridepublic void execute(Connection connection) throws SQLException {LOGGER.debug("Transaction isolation level is {}", Environment.isolationLevelToString(connection.getTransactionIsolation()));}});} }Spring框架提供了一個事務管理抽象,它將應用程序邏輯代碼與基礎事務特定的配置分離。 Spring事務管理器只是實際資源本地或JTA事務管理器的基礎。
從本地資源到XA事務的遷移只是一個配置細節,而實際的業務邏輯代碼則保持不變。 如果沒有額外的事務管理抽象層和跨領域AOP支持,這將是不可能的。
接下來,我們將測試各種特定的事務管理器如何支持事務范圍隔離級別覆蓋。
JPA交易經理
首先,我們將測試JPA事務管理器:
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"><property name="entityManagerFactory" ref="entityManagerFactory" /></bean>當調用我們的業務邏輯服務時,這是我們得到的:
DEBUG [main]: c.v.s.i.StoreServiceImpl - Transaction isolation level is SERIALIZABLEJPA事務管理器只能使用一個數據源,因此它只能發出資源本地事務。 在這種情況下,Spring事務管理器能夠覆蓋默認的DataSource隔離級別(在本例中為READ COMMITTED)。
JTA交易經理
現在,讓我們看看切換到JTA事務時會發生什么。 如前所述,Spring僅提供邏輯事務管理器,這意味著我們還必須提供物理JTA事務管理器。
傳統上,企業應用服務器(例如Wildfly和WebLogic )負責提供符合JTA的事務管理器。 如今,還有各種各樣的獨立JTA事務管理器:
- 比特龍
- Atomikos
- 紅帽納拉亞納(RedHat Narayana)
在此測試中,我們將使用Bitronix:
<bean id="jtaTransactionManager" factory-method="getTransactionManager"class="bitronix.tm.TransactionManagerServices" depends-on="btmConfig, dataSource"destroy-method="shutdown"/><bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"><property name="transactionManager" ref="jtaTransactionManager"/><property name="userTransaction" ref="jtaTransactionManager"/> </bean>運行先前的測試時,我們得到以下異常:
org.springframework.transaction.InvalidIsolationLevelException: JtaTransactionManager does not support custom isolation levels by default - switch 'allowCustomIsolationLevels' to 'true'因此,讓我們啟用自定義隔離級別設置并重新運行測試:
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager"><property name="transactionManager" ref="jtaTransactionManager"/><property name="userTransaction" ref="jtaTransactionManager"/><property name="allowCustomIsolationLevels" value="true"/> </bean>該測試為我們提供了以下輸出:
DEBUG [main]: c.v.s.i.StoreServiceImpl - Transaction isolation level is READ_COMMITTED即使有了這種額外的配置,事務范圍的隔離級別也不會傳播到底層數據庫連接,因為這是JTA事務管理器的默認行為。
對于WebLogic,Spring提供了WebLogicJtaTransactionManager來解決此限制,正如我們在以下Spring源代碼片段中所看到的:
// Specify isolation level, if any, through corresponding WebLogic transaction property. if (this.weblogicTransactionManagerAvailable) {if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {try {Transaction tx = getTransactionManager().getTransaction();Integer isolationLevel = definition.getIsolationLevel();/*weblogic.transaction.Transaction wtx = (weblogic.transaction.Transaction) tx;wtx.setProperty(ISOLATION_LEVEL_KEY, isolationLevel);*/this.setPropertyMethod.invoke(tx, ISOLATION_LEVEL_KEY, isolationLevel);}catch (InvocationTargetException ex) {throw new TransactionSystemException("WebLogic's Transaction.setProperty(String, Serializable) method failed", ex.getTargetException());}catch (Exception ex) {throw new TransactionSystemException("Could not invoke WebLogic's Transaction.setProperty(String, Serializable) method", ex);}} } else {applyIsolationLevel(txObject, definition.getIsolationLevel()); }結論
事務管理絕對不是一件容易的事,并且有了所有可用的框架和抽象層,它確實變得比人們想象的要復雜。
因為數據完整性對于大多數業務應用程序非常重要,所以唯一的選擇是掌握當前的項目數據層框架堆棧。
- Hibernate和JPA可用的代碼。
翻譯自: https://www.javacodegeeks.com/2014/12/a-beginners-guide-to-transaction-isolation-levels-in-enterprise-java.html
總結
以上是生活随笔為你收集整理的企业Java中事务隔离级别的初学者指南的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: RabbitMQ –使用Spring集成
- 下一篇: 书评:精通Lambda:多核世界中的Ja