jinfeng_wang

          G-G-S,D-D-U!

          BlogJava 首頁 新隨筆 聯(lián)系 聚合 管理
            400 Posts :: 0 Stories :: 296 Comments :: 0 Trackbacks

           

          5.1.4 理解事務(wù)隔離級別

           

          問題

          The ANSI SQL standard defines the standard transaction isolation levels in terms of which of these phenomena are permissible:

          ANSI SQL分別針對以下可能出現(xiàn)的問題,定義了不同的標準事務(wù)隔離級別。

           

          Lost update—Two transactions both update a row and then the second transaction aborts, causing both changes to be lost. This occurs in systems that don’t implement any locking. The concurrent transactions aren’t isolated.

          更新丟失-兩個事務(wù)都同時更新一行數(shù)據(jù),但是第二個事務(wù)卻中途失敗退出,導(dǎo)致對數(shù)據(jù)的兩個修改都失效了。這是因為系統(tǒng)沒有執(zhí)行任何的鎖操作,因此并發(fā)事務(wù)并沒有被隔離開來。

           

          Dirty read—One transaction reads changes made by another transaction that hasn’t yet been committed. This is very dangerous, because those changes might later be rolled back.

          臟讀取-一個事務(wù)開始讀取了某行數(shù)據(jù),但是另外一個事務(wù)已經(jīng)更新了此數(shù)據(jù)但沒有能夠及時提交。這是相當危險的,因為很可能所有的操作都可能會被回滾。

           

          Unrepeatable read—A transaction reads a row twice and reads different state each time. For example, another transaction may have written to the row, and committed, between the two reads.

          不可重復(fù)讀-一個事務(wù)對同一行數(shù)據(jù)重復(fù)讀取兩次,但是卻得到了不同的結(jié)果。例如,在兩次讀取的中途,有另外一個事務(wù)對該行數(shù)據(jù)進行了修改,并提交。

           

          Second lost updates problem—A special case of an unrepeatable read. Imagine that two concurrent transactions both read a row, one writes to it and commits, and then the second writes to it and commits. The changes made by the first writer are lost.

          兩次更新問題-無法重復(fù)讀取的特例。有兩個并發(fā)事務(wù)同時讀取同一行數(shù)據(jù),然后其中一個對它進行修改提交,而另一個也是修改提交。這就會造成第一次寫操作失效。

           

          Phantom read—A transaction executes a query twice, and the second result set includes rows that weren’t visible in the first result set. (It need not necessarily be exactly the same query.) This situation is caused by another transaction inserting new rows between the execution of the two queries.

          讀取幻影數(shù)據(jù)-事務(wù)在操作過程中進行兩次查詢,第二次查詢的結(jié)果包含了第一次查詢中未出現(xiàn)的數(shù)據(jù)(這里并不要求兩次查詢的sql語句相同)。這是因為在兩次查詢過程中有另外一個事務(wù)插入數(shù)據(jù)造成的。

           

          事務(wù)隔離級別

           

          The standard isolation levels are defined by the ANSI SQL standard but aren’t particular to SQL databases. JTA defines the same isolation levels, and you’ll use these levels to declare your desired transaction isolation later:

          雖然ANSI SQL定義了標準的事務(wù)隔離級別,但是不聽的SQL數(shù)據(jù)庫的實現(xiàn)則是不同的。JTA定義了同樣的事務(wù)隔離級別,你可以在按照你的要求使用它們:

           

          Read uncommitted—Permits dirty reads but not lost updates. One transaction may not write to a row if another uncommitted transaction has already written to it. Any transaction may read any row, however. This isolation level may be implemented using exclusive write locks.

          未授權(quán)讀取-允許臟讀取,但不允許更新丟失。如果一個事務(wù)已經(jīng)開始寫數(shù)據(jù),則另外一個數(shù)據(jù)則不允許同時寫。但允許其他事務(wù)讀此行數(shù)據(jù)。該隔離級別可以通過“排他寫鎖”實現(xiàn)。

           

          Read committed—Permits unrepeatable reads but not dirty reads. This may be achieved using momentary shared read locks and exclusive write locks. Reading transactions don’t block other transactions from accessing a row. However, an uncommitted writing transaction blocks all other transactions from accessing the row.

          授權(quán)讀取-允許不可重復(fù)讀取,但不允許臟讀取。這可以通過“瞬間共享讀鎖”和“排他寫鎖”實現(xiàn)。讀取數(shù)據(jù)的事務(wù)允許其他事務(wù)繼續(xù)訪問該行數(shù)據(jù),但是未提交的寫事務(wù)將會禁止其他事務(wù)訪問該行。

           

          Repeatable read—Permits neither unrepeatable reads nor dirty reads. Phantom reads may occur. This may be achieved using shared read locks and exclusive write locks. Reading transactions block writing transactions (but not other reading transactions), and writing transactions block all other transactions.

          可重復(fù)讀取-禁止不可重復(fù)讀取和臟讀取,但是有時可能出現(xiàn)幻影數(shù)據(jù)。這可以通過“共享讀鎖”和“排他寫鎖”實現(xiàn)。讀取數(shù)據(jù)的事務(wù)將會禁止寫事務(wù)(但允許讀事務(wù)),寫事務(wù)則禁止任何其他事務(wù)。

           

          Serializable—Provides the strictest transaction isolation. It emulates serial transaction execution, as if transactions had been executed one after another, serially, rather than concurrently. Serializability may not be implemented using only row-level locks; there must be another mechanism that prevents a newly inserted row from becoming visible to a transaction that has already executed a query that would return the row.

          序列化-提供嚴格的事務(wù)隔離。它要求事務(wù)序列化執(zhí)行,事務(wù)只能一個接著一個的執(zhí)行,但不能并發(fā)執(zhí)行。如果僅僅通過“行級鎖”是無法實現(xiàn)事務(wù)序列化的,必須通過其他機制保證新插入的數(shù)據(jù)不會被剛執(zhí)行查詢操作的事務(wù)訪問到。

           

          5.1.5 選擇正確的事務(wù)隔離級別

          First, you eliminate the read uncommitted isolation level. It’s extremely dangerous to use one transaction’s uncommitted changes in a different transaction. The rollback or failure of one transaction would affect other concurrent transactions. Rollback of the first transaction could bring other transactions down with it, or perhaps even cause them to leave the database in an inconsistent state. It’s possible that changes made by a transaction that ends up being rolled back could be committed anyway, since they could be read and then propagated by another transaction that is successful!

          首先,必須排除“未授權(quán)讀取”,因為在多個事務(wù)之間使用它將會是非常危險的。事務(wù)的回滾操作或失敗將會影響到其他并發(fā)事務(wù)。第一個事務(wù)的回滾將會完全將其他事務(wù)的操作清除,甚至使數(shù)據(jù)庫處在一個不一致的狀態(tài)。很可能一個已回滾為結(jié)束的事務(wù)對數(shù)據(jù)的修改最后卻修改提交了,因為“未授權(quán)讀取”允許其他事務(wù)讀取數(shù)據(jù),最后整個錯誤狀態(tài)在其他事務(wù)之間傳播開來。

           

          Second, most applications don’t need serializable isolation (phantom reads aren’t usually a problem), and this isolation level tends to scale poorly. Few existing applications use serializable isolation in production; rather, they use pessimistic locks, which effectively forces a serialized execution of operations in certain situations.

          其次,絕大部分應(yīng)用都無需使用“序列化”隔離(一般來說,讀取幻影數(shù)據(jù)并不是一個問題),此隔離級別也難以測量。目前使用序列化隔離的應(yīng)用中,一般都使用的悲觀鎖,這樣強行使得所有事務(wù)都序列化執(zhí)行。

           

          This leaves you a choice between read committed and repeatable read. Let’s first consider repeatable read. This isolation level eliminates the possibility that one transaction could overwrite changes made by another concurrent transaction (the second lost updates problem) if all data access is performed in a single atomic database transaction. This is an important issue, but using repeatable read isn’t the only way to resolve it.

          剩下的也就是在“授權(quán)讀取”和“可重復(fù)讀取”之間選擇了。我們先考慮可重復(fù)讀取。如果所有的數(shù)據(jù)訪問都是在統(tǒng)一的原子數(shù)據(jù)庫事務(wù)中,此隔離級別將消除一個事務(wù)在另外一個并發(fā)事務(wù)過程中覆蓋數(shù)據(jù)的可能性(第二個事務(wù)更新丟失問題)。這是一個非常重要的問題,但是使用可重復(fù)讀取并不是解決問題的唯一途徑。

           

          Let’s assume you’re using versioned data, something that Hibernate can do for you automatically. The combination of the (mandatory) Hibernate first-level session cache and versioning already gives you most of the features of repeatable read isolation. In particular, versioning prevents the second lost update problem, and the first-level session cache ensures that the state of the persistent instances loaded by one transaction is isolated from changes made by other transactions. So, read committed isolation for all database transactions would be acceptable if you use versioned data.

          讓我們假設(shè)你使用了“版本數(shù)據(jù)”,hibernate會為你自動使用版本數(shù)據(jù)。Hibernate的一級session緩存和版本數(shù)據(jù)已經(jīng)喂你提供了“可重復(fù)讀取隔離” 絕大部分的特性。特別是,版本數(shù)據(jù)可以防止二次更新丟失的問題,一級session緩存可以保證持久載入數(shù)據(jù)的狀態(tài)與其他事務(wù)對數(shù)據(jù)的修改隔離開來,因此如果你使用對所有的數(shù)據(jù)庫事務(wù)采用授權(quán)讀取隔離和版本數(shù)據(jù)是行得通的。

           

          Repeatable read provides a bit more reproducibility for query result sets (only for the duration of the database transaction), but since phantom reads are still possible, there isn’t much value in that. (It’s also not common for web applications to query the same table twice in a single database transaction.)

          “可重復(fù)讀取”為數(shù)據(jù)庫查詢提供了更好的效率(僅對那些長時間的數(shù)據(jù)庫事務(wù)),但是由于幻影讀取依然存在,因此并沒必要使用它(對于Web應(yīng)用來說,一般也很少在一個數(shù)據(jù)庫事務(wù)中對同一個表查詢兩次)。

           

          You also have to consider the (optional) second-level Hibernate cache. It can provide the same transaction isolation as the underlying database transaction, but it might even weaken isolation. If you’re heavily using a cache concurrency strategy for the second-level cache that doesn’t preserve repeatable read semantics (for example, the read-write and especially the nonstrict-read-write strategies, both discussed later in this chapter), the choice for a default isolation level is easy: You can’t achieve repeatable read anyway, so there’s no point slowing down the database. On the other hand, you might not be using second-level caching for critical classes, or you might be using a fully transactional cache that provides repeatable read isolation. Should you use repeatable read in this case? You can if you like, but it’s probably not worth the performance cost.

          你也可以同時考慮選擇使用hibernate的二級緩存,它可以如同底層的數(shù)據(jù)庫事務(wù)一樣提供相同的事務(wù)隔離,但是它可能弱化隔離。假如你大量在二級緩存使用緩存并發(fā)策略,它并不提供重復(fù)讀取語義(例如,后面章節(jié)中將要討論的讀寫,特別是非嚴格讀寫),很容易可以選擇默認的隔離級別:因為你無論如何都無法實現(xiàn)“可重復(fù)讀取”,因此就更無必要拖慢數(shù)據(jù)庫了。另一方面,你可能對關(guān)鍵類不采用二級緩存,或者采用一個完全的事務(wù)緩存,提供“可重復(fù)讀取隔離”。那么在你的業(yè)務(wù)中需要使用到“可重復(fù)讀取”嗎?如果你喜歡,你當然可以那樣做,但更多的時候并沒有必要花費這個代價。

           

          5.1.6  hibernate中設(shè)置隔離級別l

          Hibernate will then set this isolation level on every JDBC connection obtained from a connection pool before starting a transaction. The sensible values for this option are as follows (you can also find them as constants in java.sql.Connection):

          1—Read uncommitted isolation

          2—Read committed isolation

          4—Repeatable read isolation

          8—Serializable isolation

          Note that Hibernate never changes the isolation level of connections obtained from a datasource provided by the application server in a managed environment. You may change the default isolation using the configuration of your application server.

          Hibernte將會在啟動事務(wù)之前,為從數(shù)據(jù)庫鏈接池中取得的每一個connection設(shè)置隔離級別。其常量值如下(你可以在java.sql.connection中常量中找到):

          1-未授權(quán)讀取隔離

          2-授權(quán)讀取隔離

          4-可重復(fù)讀取隔離

          8-序列化隔離

          注意:hibernate不會對從應(yīng)用服務(wù)器管理的數(shù)據(jù)源取得的數(shù)據(jù)庫連接的隔離級別進行設(shè)置。你需要在你的應(yīng)用服務(wù)器進行配置,修改其默認的隔離級別。

           

           

          5.1.7 使用悲觀鎖

          Locking is a mechanism that prevents concurrent access to a particular item of data. When one transaction holds a lock on an item, no concurrent transaction can read and/or modify this item. A lock might be just a momentary lock, held while the item is being read, or it might be held until the completion of the transaction. A pessimistic lock is a lock that is acquired when an item of data is read and that is held until transaction completion.

          鎖是用來防止并發(fā)訪問同一數(shù)據(jù)的機制。當一個事務(wù)對一個數(shù)據(jù)擁有了鎖,那么其他并發(fā)事務(wù)則無法對它進行讀或者寫。這里的鎖可以是瞬時的鎖,僅當在數(shù)據(jù)被讀取時存在,也可以時長期的,直到整個事務(wù)結(jié)束時釋放。悲觀鎖是指:它在數(shù)據(jù)被讀取時獲取,在事務(wù)結(jié)束時釋放。

           

          In read-committed mode (our preferred transaction isolation level), the database never acquires pessimistic locks unless explicitly requested by the application. Usually, pessimistic locks aren’t the most scalable approach to concurrency. However, in certain special circumstances, they may be used to prevent database-level deadlocks, which result in transaction failure. Some databases (Oracle and PostgreSQL, for example) provide the SQL SELECT...FOR UPDATE syntax to allow the use of explicit pessimistic locks. You can check the Hibernate Dialects to find out if your database supports this feature. If your database isn’t supported, Hibernate will always execute a normal SELECT without the FOR UPDATE clause.

          在“授權(quán)讀取”模式下(我們推薦的事務(wù)隔離級別),數(shù)據(jù)庫將永不使用悲觀鎖,除非應(yīng)用程序特別的顯式生氣。通常,悲觀鎖是不適合大規(guī)模并發(fā)操作的。但是在某些特殊場合下,需要使用悲觀鎖,以防止那些會引起整個事務(wù)失敗的數(shù)據(jù)庫死鎖的發(fā)生。有些數(shù)據(jù)庫(例如OraclePostgreSQL)可以提供“sql select … for update”這樣的語法,讓用戶顯式的使用悲觀鎖。你 可以去查看hibernatedialect的文檔,確認你的數(shù)據(jù)庫是否支持此特性。如果你的數(shù)據(jù)庫不支持,那么hibernate就會僅僅執(zhí)行普通的select語句,而不帶有for update字段。

           

          The Hibernate LockMode class lets you request a pessimistic lock on a particular item. In addition, you can use the LockMode to force Hibernate to bypass the cache layer or to execute a simple version check. You’ll see the benefit of these operations when we discuss versioning and caching.

          Hibernatelockmode類允許你為某個數(shù)據(jù)申請悲觀鎖,此外,你可以使用lockmode強制hibernate穿越其緩存層、或者執(zhí)行簡單的版本校驗。從我們對版本數(shù)據(jù)和緩存的討論中了解這些操作的作用。

           

          Let’s see how to use LockMode. If you have a transaction that looks like this

          Transaction tx = session.beginTransaction();

          Category cat = (Category) session.get(Category.class, catId);

          cat.setName("New Name");

          tx.commit();

          then you can obtain a pessimistic lock as follows:

          Transaction tx = session.beginTransaction();

          Category cat =

          (Category) session.get(Category.class, catId, LockMode.UPGRADE);

          cat.setName("New Name");

          tx.commit();

          With this mode, Hibernate will load the Category using a SELECT...FOR UPDATE, thus locking the retrieved rows in the database until they’re released when the transaction ends.

          讓我們先看看如何使用lockmode。如果你有一個事務(wù)是這樣的:

          Transaction tx = session.beginTransaction();

          Category cat = (Category) session.get(Category.class, catId);

          cat.setName("New Name");

          tx.commit();

          那么你可以通過如下方法,獲得悲觀鎖。

          Transaction tx = session.beginTransaction();

          Category cat =

          (Category) session.get(Category.class, catId, LockMode.UPGRADE);

          cat.setName("New Name");

          tx.commit();

          使用lockmode.UPGRADEhibernate將會在載入Category時使用“select… for update”并鎖定數(shù)據(jù),然后直到事務(wù)結(jié)束時,才會釋放鎖。

           

           

          Hibernate defines several lock modes:

          LockMode.NONE—Don’t go to the database unless the object isn’t in either cache.

          LockMode.READ—Bypass both levels of the cache, and perform a version check to verify that the object in memory is the same version that currently exists in the database.

          LockMode.UPDGRADE—Bypass both levels of the cache, do a version check (if applicable), and obtain a database-level pessimistic upgrade lock, if that is supported.

          LockMode.UPDGRADE_NOWAIT—The same as UPGRADE, but use a SELECT...FOR UPDATE NOWAIT on Oracle. This disables waiting for concurrent lock releases, thus throwing a locking exception immediately if the lock can’t be obtained.

                  LockMode.WRITE—Is obtained automatically when Hibernate has written to a row in the current transaction (this is an internal mode; you can’t specify it explicitly).

          Hibernate定義了以下幾種lockmode

            LockMode.NONE-不訪問數(shù)據(jù)庫,除非該對象不在緩存中。

            LockMode.READ-穿越兩層緩存,進行版本校驗,以保證內(nèi)存中的對象和數(shù)據(jù)庫中的版本相同。

            LockMode.UPGRADE-穿越兩次緩存,進行數(shù)據(jù)版本校驗,如果可以,則對數(shù)據(jù)庫級別加上悲觀更新鎖。

            LockMode.UPGRADE_NOWAIT-和UPGRADE相同,但是對Oracle使用select … for update no wait語句。如果當前數(shù)據(jù)被鎖,則無需等待,直接拋出異常。

            LockMode.WRITE-當hibernate在當前事務(wù)中寫一行數(shù)據(jù)時,會自動使用它(這是內(nèi)部模式,你不該顯式的去使用)。

           

          By default, load() and get() use LockMode.NONE. LockMode.READ is most useful with

          Session.lock() and a detached object. For example:

          Item item = ... ;

          Bid bid = new Bid();

          item.addBid(bid);

          ...

          Transaction tx = session.beginTransaction();

          session.lock(item, LockMode.READ);

          tx.commit();

          This code performs a version check on the detached Item instance to verify that the database row wasn’t updated by another transaction since it was retrieved, before saving the new Bid by cascade (assuming that the association from Item to Bid has cascading enabled).

          默認情況下,load()get()使用LockMode.NONE,而LockMode.READ則更多的被session.lock和分離對象使用。例如:

          Item item = ... ;

          Bid bid = new Bid();

          item.addBid(bid);

          ...

          Transaction tx = session.beginTransaction();

          session.lock(item, LockMode.READ);

          tx.commit();

          這里的代碼在對新的bid級聯(lián)保存之前(假設(shè)在配置中允許級聯(lián)),對分離的Item實例進行了版本檢查,以校驗數(shù)據(jù)庫的數(shù)據(jù)從上次獲得以來未被其他事務(wù)更新。

           

          By specifying an explicit LockMode other than LockMode.NONE, you force Hibernate to bypass both levels of the cache and go all the way to the database. We think that most of the time caching is more useful than pessimistic locking, so we don’t use an explicit LockMode unless we really need it. Our advice is that if you have a professional DBA on your project, let the DBA decide which transactions require pessimistic locking once the application is up and running. This decision should depend on subtle details of the interactions between different transactions and can’t be guessed up front.

          通過顯式的聲明LockMode,而不是采用默認的LockMode.NONE,你可以強迫hibernate穿越兩層緩存,然后像直接訪問數(shù)據(jù)庫那樣行事。但是我們認為,在絕大部分情況下,緩存要比悲觀鎖有用的多,因此除非真的需要,我們并不建議顯式使用LockMode。我們的建議是,如果你的項目有一個專職的DBA,那么讓DBA在系統(tǒng)允許時決定哪個事務(wù)該采用悲觀鎖。這完全依賴于多個事務(wù)之間的底層細節(jié),而不該在簡單的前臺猜測。

           

           

          posted on 2005-04-07 14:03 jinfeng_wang 閱讀(3233) 評論(0)  編輯  收藏 所屬分類: hibernate
          主站蜘蛛池模板: 宝山区| 昭平县| 兴文县| 甘孜县| 邯郸市| 桂平市| 汤原县| 株洲县| 永年县| 黄浦区| 山阳县| 福安市| 阳山县| 钦州市| 若尔盖县| 罗城| 新巴尔虎左旗| 新昌县| 横山县| 子洲县| 香港 | 广宁县| 龙川县| 桃源县| 新宁县| 漳平市| 宜黄县| 明水县| 塘沽区| 弥勒县| 湖口县| 彰化县| 稻城县| 澜沧| 封丘县| 聂拉木县| 阳高县| 德化县| 灵川县| 施秉县| 轮台县|