<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # 13.2\. 事務隔離 SQL標準定義了四個級別的事務隔離。最嚴格的是串行化,它是通過標準來定義的,也就是說, 保證一組可序列化事務的并發執行以產生同樣順序依次運行它們的同一效果。 其他三個層次是通過現象術語被定義,導致并發事務之間的相互作用,這不應該發生在每個級別中。 標準定義歸因于序列化的定義,這些現象不可能在這一水平上(這毫不奇怪--如果事務的影響必須與已運行的一個保持一致,你怎么能看到通過相互作用引起的現象呢? 各個級別不希望發生的現象是: 臟讀 一個事務讀取了另一個未提交事務寫入的數據。 不可重復讀 一個事務重新讀取前面讀取過的數據,發現該數據已經被另一個已提交事務修改。 幻讀 一個事務重新執行一個查詢,返回一套符合查詢條件的行,發現這些行因為其它最近提交的事務而發生了改變。 這四種隔離級別和對應的行為在表[Table 13-1](#calibre_link-1174)里描述。 **Table 13-1\. 標準SQL事務隔離級別** | 隔離級別 | 臟讀 | 不可重復讀 | 幻讀 | | --- | --- | --- | --- | | 讀未提交 | 可能 | 可能 | 可能 | | 讀已提交 | 不可能 | 可能 | 可能 | | 可重復讀 | 不可能 | 不可能 | 可能 | | 可串行化 | 不可能 | 不可能 | 不可能 | 在PostgreSQL里,你可以請求四種可能的事務隔離級別中的任意一種。但是在內部, 實際上只有三種獨立的隔離級別,分別對應讀已提交,可重復讀和可串行化。如果你選擇了讀未提交的級別, 實際上你用的是讀已提交,在重復讀的PostgreSQL執行時,幻讀是不可能的, 所以實際的隔離級別可能比你選擇的更嚴格。這是 SQL 標準允許的:四種隔離級別只定義了哪種現像不能發生, 但是沒有定義那種現像一定發生。PostgreSQL只提供兩種隔離級別的原因是, 這是把標準的隔離級別與多版本并發控制架構映射相關的唯一合理方法。可用的隔離級別的行為在下面小節里描述。 要設置一個事務的隔離級別,使用[SET TRANSACTION](#calibre_link-507)命令。 > **Important:** 一些PostgreSQL數據類型和函數關于事務行為有特定的規則。 尤其是,序列變化(因此列數通過`serial`聲明)對于所有其他的事務是立即可見的, 如果事務改變終止,則不進行回退。參見[Section 9.16](#calibre_link-700)和[Section 8.1.4](#calibre_link-1175)。 ## 13.2.1\. 讀已提交隔離級別 _讀已提交_是PostgreSQL里的缺省隔離級別。當一個事務運行在這個隔離級別時, `SELECT`查詢(沒有`FOR UPDATE/SHARE`子句)只能看到查詢開始之前已提交的數據而無法看到未提交的數據或者在查詢執行期間其它事務已提交的數據 。實際上,`SELECT` 查詢看到一個在查詢開始運行的瞬間該數據庫的一個快照。 不過,`SELECT`看得見其自身所在事務中前面更新執行結果。即使它們尚未提交。請注意, 在同一個事務里兩個相鄰的`SELECT`命令可能看到不同的快照,因為其它事務會在第一個`SELECT`執行期間提交。 `UPDATE`, `DELETE`, `SELECT FOR UPDATE`和`SELECT FOR SHARE`命令在搜索目標行時的行為和`SELECT`一樣: 它們只能找到在命令開始的時候已經提交的行。不過, 這樣的目標行在被找到的時候可能已經被其它并發事務更新、刪除、鎖住。在這種情況下, 即將進行的更新將等待第一個事務提交或者回滾(如果它還在處理)。如果第一個事務回滾, 那么它的作用將被忽略,而第二個事務將繼續更新最初發現的行。如果第一個事務提交, 那么如果第一個事務刪除了該行,則第二個事務將忽略該行, 否則它將試圖在該行的已更新的版本上施加它的操作。系統將重新計算命令搜索條件(`WHERE`子句), 看看該行已更新的版本是否仍然符合搜索條件。如果符合,則第二個事務從該行的已更新版本開始繼續其操作。 如果是`SELECT FOR UPDATE`和`SELECT FOR SHARE`則意味著把已更新的行版本鎖住并返回給客戶端。 因為上面的規則,正在更新的命令可能會看到不一致的快照: 它們可以看到影響它們更新的并發命令的效果,但是卻看不到那些命令對數據庫里其它行的作用。 這樣的行為令讀已提交模式不適合用于哪種涉及復雜搜索條件的命令。不過,它對于簡單的情況而言是正確的。 比如,假設我們用類似下面這樣的命令更新銀行余額: ``` BEGIN; UPDATE accounts SET balance = balance + 100.00 WHERE acctnum = 12345; UPDATE accounts SET balance = balance - 100.00 WHERE acctnum = 7534; COMMIT; ``` 如果兩個并發事務試圖同時修改帳號12345的余額,那我們很明顯希望第二個事務是從已更新過的行版本上進行更新。 因為每個命令只是影響一個已經決定了的行,因此讓它看到更新后的版本不會導致任何不一致的問題。 更復雜的用法可以在讀已提交模式下產生不需要的結果。比如,考慮`DELETE`命令數據操作 通過另外一個命令的限制標準中被添加或者刪除等,假設`website`是`website.hits` 等同于`9`和 `10`的兩行表格。 ``` BEGIN; UPDATE website SET hits = hits + 1; -- run from another session: DELETE FROM website WHERE hits = 10; COMMIT; ``` `DELETE`不會產生影響,即使在`UPDATE`之前和之后有`website.hits = 10`。 這發生是因為先前更新的行值`9`被忽略,并且當`UPDATE`完成而且`DELETE`獲得鎖時, 新的行值不再是`10`而是`11`,它不再符合標準。 因為在讀已提交模式里,每個新的命令都是從一個新的快照開始的, 而這個快照包含所有到該時刻為止已提交的事務, 因此同一事務中后面的命令將看到任何已提交的其它事務的效果。 這里關心的問題是在_單個_命令里是否看到數據庫里絕對一致的視圖。 讀已提交模式提供的部分事務隔離對于許多應用而言是足夠的,并且這個模式速度快,使用簡單。 不過,對于做復雜查詢和更新的應用, 可能需要保證數據庫有比讀已提交模式更加嚴格的一致性視圖。 ## 13.2.2\. 可重復讀隔離級別 _可重復讀_隔離級別僅僅看到事務開始之前提交的數據,它不能看到在并發事務執行期間未提交的數據和已提交的改變。 (然而,查詢看到在自身事務中執行的先前更新的效果,即使它們沒有被提交)。比為這一隔離級別的SQL標準需求來說,這是一個更強烈的保證。 避免所有在[Table 13-1](#calibre_link-1174)描述的現象。正如上述所提及的,這是通過標準允許的, 這僅僅描述必須提供的每個隔離級別的_最低限度_保護。 這個級別和讀已提交級別是不一樣的。重復讀事務中的查詢看到的是_事務_開始時的快照, 而不是該事務內部當前查詢開始時的快照,這樣, 同_一個_事務內部后面的`SELECT`命令總是看到同樣的數據等,它們沒有看到通過 自身事務開始之后提及的其他事務做出的改變。 使用這個級別的應用必須準備好重試事務,因為串行化失敗。 `UPDATE`, `DELETE`, `SELECT FOR UPDATE`和`SELECT FOR SHARE`在搜索目標行時的行為和`SELECT`一樣: 它們將只尋找在事務開始的時候已經提交的目標行。但是, 這樣的目標行在被發現的時候可能已經被另外一個并發的事務更新、刪除、鎖住。在這種情況下, 可串行化的事務將等待第一個正在更新的事務提交或者回滾(如果它仍然在處理中)。如果第一個事務回滾, 那么它的影響將被忽略,而這個可串行化的就可以繼續更新它最初發現的行。 但是如果第一個事務被提交了(并且實際上更新或者刪除了該行,而不只是鎖住它)那么可串行化事務將回滾, 并返回下面信息: ``` ERROR: could not serialize access due to concurrent update ``` 因為一個可串行化的事務在開始之后不能更改或者鎖住被其它事務更改過的行。 當應用收到這樣的錯誤信息時,它應該退出當前的事務然后從新開始進行整個事務。第二次運行時, 該事務看到的快照將包含前一次已提交的修改,所以不會有邏輯沖突。 請注意只有更新事務才需要重試,只讀事務從來沒有串行化沖突。 可重復讀事務級別提供了嚴格的保證:每個事務都看到一個完全一致的數據庫視圖。然而, 這種觀點也不一定總是與(一次一個)同一級別的并發事務連續執行一致。 例如,即使在這個級別上的一個只讀事務可以看到控制記錄更新顯示一批已經完成,但 _不能_看到一批邏輯部分的詳細記錄, 因為它讀取較早版本的控制記錄。如果不仔細使用顯式鎖來阻止沖突事務,通過運行在這個隔離級別上的事務嘗試執行業務規則是不能正常工作的。 > **Note:** PostgreSQL9.1版本之前,為序列化事務隔離級別的請求提供完全相同的描述。為保留傳統的串行化行為,現在要求可重復讀。 ## 13.2.3\. 可串行化隔離級別 _可串行化_級別提供最嚴格的事務隔離。這個級別為所有已提交事務模擬串行的事務執行, 就好像事務將被一個接著一個那樣串行(而不是并行)的執行。不過,正如可重復讀隔離級別一樣, 使用這個級別的應用必須準備在串行化失敗的時候重新啟動事務。 事實上,該隔離級別和可重復讀希望的完全一樣, 它只是監視這些條件,以所有事務的可能的序列不一致的(一次一個)的方式執行并行的可序列化事務執行的行為。 這種監測不引入任何阻止可重復讀出現的行為,但有一些開銷的監測,檢測條件這可能會導致_序列化異常_ 將觸發_序列化失敗_。 舉例來說,假設一個表`mytab`,最初包含: ``` class | value -------+------- 1 | 10 1 | 20 2 | 100 2 | 200 ``` 假設可串行化事務 A 計算: ``` SELECT SUM(value) FROM mytab WHERE class = 1; ``` 然后把結果(30)作為`value`字段值插入到表中,并令新行的`class` `= 2` 。同時,另一個并發的可串行化的事務B進行下面計算 ``` SELECT SUM(value) FROM mytab WHERE class = 2; ``` 然后把結果(300)作為`class` `= 1`字段值插入到表中。 然后兩個事務都提交。如果事務都在可重復讀隔離級別上運行,兩者都不允許提交;但是因為 沒有執行一致性結果的序列順序,使用可串行化事務將允許一個事務被提交,并且回滾到該消息的其他塊中。 ``` ERROR: could not serialize access due to read/write dependencies among transactions ``` 這是因為如果 A 在 B 之前執行,B 應該計算出總和 330 ,而不是300, 如果B在A之前執行,那么 A 計算出的總和也會不同。 當依賴于可串行化事務阻止異常時,來自永久用戶表讀取的任何數據不被認為是有效的,直到事務讀取的成功提交為止。 這對于只讀事務是真的,除了在_可延期的_只讀事務中的數據讀是有效的。 因為這樣一個事務等待直到它可以在開始讀取任何數據之前獲得一個快照保證這些問題是自由的。 在所有其他情況下,應用不依賴于結果讀,期間事務之后被停止;相反,他們應該重啟事務直到成功為止。 為了保證PostgreSQL真正可串行化使用_謂詞鎖定_。 這意味著當寫對于并發事務的先前讀結果有重大影響時,它使鎖決定首先運行。 在PostgreSQL這些鎖不造成任何阻塞,因此可以_不_導致僵局。 它們被用來識別和標記并發序列化事務中的依賴關系,其中一定的組合可導致序列化異常。 相反,讀已提交或者可重復讀取的事務要確保數據的一致性可能需要獲取整個表鎖, 它可以阻止其他嘗試使用該表的用戶,也可以使用`SELECT FOR UPDATE`或者`SELECT FOR SHARE`,這不僅可以阻止其他事務而且可能導致磁盤訪問。 PostgreSQL中的謂詞鎖,像其他大多數數據庫系統一樣, 基于通過事務實際訪問的數據,這些顯示在[`pg_locks`](#calibre_link-723) 系統視圖中,并帶有`SIReadLock`的`模式`。 查詢執行期間特定的鎖的獲得將取決于使用的查詢計劃。以及事務進程防止用于跟蹤鎖定的內存耗盡期間的多個細粒度鎖(例如,元組鎖)可以組合成較少的粗粒度的鎖(例如,頁鎖)。 `只讀`事務可以在完成之前釋放SIRead鎖,如果它檢測到沒有沖突仍然發生,這可能會導致一系列的異常。事實上, `只讀`事務會經常建立啟動事實,并且避免采取任何謂詞鎖。如果你明確要求`SERIALIZABLE READ ONLY DEFERRABLE`事務,這將阻塞直到它可以建立這一事實。 (這是_唯一_情況,可序列化事務塊可以但可重復讀事務不行。)另一方面,SIRead鎖經常需要保持過去的事務提交,直到重疊讀寫事務完成。 可序列化事務一致性的使用可以簡化開發。如果他們每次運行一個,保證任何一組并發序列化事務會具有相同的效果。 這意味著如果你能證明單一事務,作為書面的,當自己運行時將做正確事情, 你可以有信心它會在任何組合可序列化事務中做正確的事,即使沒有任何有關那些其他事務的消息。 使用這種技術的環境中有一個處理序列化失敗的方法是很重要的(它總是返回'40001'的SQLSTATE值), 因為它很難準確預測,事務可能有助于讀/寫依賴并且需要回滾防止序列化異常。讀/寫依賴的監控是有成本的,正如序列化失敗而終止之后進行事務重新啟動, 但權衡成本和使用顯式鎖以及`SELECT FOR UPDATE`或者`SELECT FOR SHARE`涉及到的阻斷, 可序列化事務在這種環境下是性能最好的選擇。 為了最佳性能,當為并發控制依賴于可串行化事務時,應該考慮這些問題: * 可能時作為`只讀`聲明事務。 * 如果需要,可以使用連接池,控制活動連接數。這是一個重要性能的考慮,但是在使用可串行化 事務的繁忙系統中尤其重要。 * 比起需要完整性目的來說不要將更多的東西放到單一事務中。 * 不要讓連接在"閑置的事務"中停留超過需要的時間。 * 消除顯示鎖,`SELECT FOR UPDATE`和 `SELECT FOR SHARE`不再需要,因為通過可串行化事務自動提供保護。 * 當系統強制連接多個頁級別謂詞鎖到單一關系級別謂詞鎖,因為謂詞鎖表是短期存儲, 可能產生可串行化失敗率的增加。你可以通過增加[max_pred_locks_per_transaction](#calibre_link-1176) 來避免。 * 順序掃描總是需要一個關系級別謂詞鎖。這可能會導致序列化失敗率增加。 這可能有助于鼓勵減少[random_page_cost](#calibre_link-1177) 和/或增加[cpu_tuple_cost](#calibre_link-1178)的索引掃描使用。 一定要權衡任何事務回滾的減少,并且重新啟動查詢執行時間內的任何整體變化。 | **Warning** | |:--- | | 序列化事務隔離級別尚未被添加到熱備復制目標中 (正如在[Section 25.5](#calibre_link-1047)中描述的)。 嚴格的隔離級別目前熱備方式上支持可重復讀。 當在主庫上執行所有永久數據庫寫入可序列化事務中將確保所有的措施將最終達成一致, 運行在備庫上的可重復讀事務會看到一個過渡狀態,與主庫上的任何串行執行的可序列化事務不一致。 |
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看