數據庫上的隔離級別和寫入衝突

表的隔離級別定義了必須將事務與並發操作所做的修改隔離的程度。數據庫上的寫衝突取決於隔離級別。

Delta Lake在讀寫之間提供ACID事務保證。這意味著:

  • 跨多個集群的多個寫入器可以同時修改一個表分區。寫入者會看到表的一致快照視圖,寫入以串行順序進行。

    • 讀者可以繼續看到Databricks作業開始時使用的表的一致快照視圖,即使在作業期間對表進行了修改。

看到ACID對Databricks有什麼保證?

請注意

默認情況下,數據庫對所有表都使用Delta Lake。本文描述了Delta Lake在Databricks上的行為。

Delta Lake什麼時候不讀表就提交了?

三角洲湖插入或者,如果滿足以下條件,追加操作在提交前不讀取表狀態:

  1. 邏輯是用插入SQL邏輯或追加模式。

  2. 邏輯不包含引用寫操作所針對的表的子查詢或條件。

與其他提交一樣,Delta Lake在提交時使用事務日誌中的元數據驗證和解析表版本,但實際上不讀取表的版本。

請注意

許多常用模式使用合並根據表條件插入數據的操作。盡管可以使用。重寫此邏輯插入語句,對目標表中數據條件的任何引用都會觸發相同的並發限製合並

將衝突寫入數據庫

下表描述了每個操作中可能發生衝突的寫操作對隔離級別

請注意

帶有標識列的表不支持並發事務。看到在Delta Lake中使用身份欄

插入(1)

更新、刪除、合並成

優化

插入

不衝突

更新、刪除、合並成

可以在Serializable衝突,不能在WriteSerializable衝突

可以在Serializable和WriteSerializable衝突

優化

不衝突

可以在Serializable和WriteSerializable衝突

可以在Serializable和WriteSerializable衝突

重要的

(1)所有插入上表中的操作描述了在提交前不從同一個表中讀取任何數據的追加操作。插入包含讀同一表的子查詢的操作支持相同的並發性合並

編寫可序列化與可序列化的隔離級別

表的隔離級別定義了必須將事務與並發事務所做的修改隔離的程度。Databricks上的Delta Lake支持兩種隔離級別:Serializable和WriteSerializable。

  • 可序列化的:最強隔離級別。它確保提交的寫操作和所有的讀操作都是正確的可序列化的.隻要存在一個每次執行一個操作的序列,生成與表中所示相同的結果,操作就被允許。對於寫操作,串行序列與表曆史中看到的完全相同。

  • WriteSerializable(默認):比Serializable隔離級別弱的隔離級別。它隻確保寫操作(也就是說,不是讀操作)是可序列化的。不過,這還是強於快照隔離。WriteSerializable是默認的隔離級別,因為它為大多數常見操作在數據一致性和可用性方麵提供了很好的平衡。

    在這種模式下,Delta表的內容可能不同於從表曆史中看到的操作序列所期望的內容。這是因為這種模式允許某些並發寫對(比如,操作X和Y)繼續進行,這樣的結果就好像Y在X之前執行(也就是說,它們之間是可序列化的),即使曆史記錄顯示Y在X之後提交。設置表隔離級別Serializable,從而導致這些事務失敗。

讀操作始終使用快照隔離。寫隔離級別決定了讀寫器是否可以看到根據曆史記錄“從未存在過”的表的快照。

對於Serializable級別,讀取器總是隻看到符合曆史記錄的表。對於WriteSerializable級別,讀者可以看到Delta日誌中不存在的表。

例如,考慮txn1,一個長時間運行的刪除和txn2,它插入由txn1刪除的數據。Txn2和txn1是完整的,它們在曆史中按此順序被記錄。根據曆史記錄,txn2中插入的數據應該不存在於表中。對於Serializable級別,閱讀器永遠不會看到txn2插入的數據。但是,對於WriteSerializable級別,讀者在某些時候可以看到txn2插入的數據。

有關每個隔離級別中哪些類型的操作可能相互衝突以及可能的錯誤的詳細信息,請參見使用分區和分離命令條件避免衝突

設置隔離級別

屬性設置隔離級別改變表格命令。

改變表格<表格-的名字>TBLPROPERTIES“delta.isolationLevel”<水平-的名字>

在哪裏<級別名稱>可序列化的WriteSerializable

例如,將隔離級別更改為默認級別WriteSerializable可序列化的運行:

改變表格<表格-的名字>TBLPROPERTIES“delta.isolationLevel”“序列化”

使用分區和分離命令條件避免衝突

在所有標記為“can conflict”的情況下,兩個操作是否會衝突取決於它們是否對同一組文件進行操作。通過按照操作條件中使用的列對表進行分區,可以使這兩組文件分離。例如,這兩個命令更新表格在哪裏日期>“2010-01-01”...而且刪除表格在哪裏日期<“2010-01-01”如果表沒有按日期分區,則會發生衝突,因為兩者都可以嚐試修改同一組文件。對表進行分區日期會避免衝突。因此,根據命令上常用的條件對表進行分區可以顯著減少衝突。但是,根據具有高基數的列對表進行分區可能會導致其他性能問題,因為有大量的子目錄。

衝突異常

當事務衝突發生時,您將觀察到以下異常之一:

ConcurrentAppendException

當並發操作在您的操作讀取的同一個分區(或未分區表中的任何位置)中添加文件時,會發生此異常。文件添加可能由插入刪除更新,或合並操作。

使用默認隔離級別WriteSerializable,由盲目的插入操作(即盲目地追加數據而不讀取任何數據的操作)不會與任何操作發生衝突,即使它們涉及相同的分區(或未分區表中的任何位置)。如果隔離級別設置為可序列化的,那麼盲目的追加可能會衝突。

此異常通常在並發期間拋出刪除更新,或合並操作。雖然並發操作可能在物理上更新不同的分區目錄,但其中一個操作可能讀取另一個並行更新的相同分區,從而導致衝突。可以通過在操作條件中顯式地分離來避免這種情況。考慮下麵的例子。

//目標'deltaTable'按日期和國家劃分deltaTable作為“t”).合並作為“s”),“s.user_id = t.user_id AND s.date = t.date AND s.country = t.country”whenMatched().updateAll()whenNotMatched().insertAll()執行()

假設您針對不同的日期或國家同時運行上述代碼。由於每個作業都在目標Delta表上的獨立分區上工作,因此不會有任何衝突。但是,這個條件不夠顯式,可以掃描整個表,並且可能與更新任何其他分區的並發操作衝突。相反,您可以重寫語句,向合並條件添加特定的日期和國家,如下例所示。

//目標'deltaTable'按日期和國家劃分deltaTable作為“t”).合並作為“s”),"s.user_id = t.user_id AND s.date = t.date AND s.country = t.country AND t.date = '"+<日期>+" AND t.country = '"+<國家>+“”whenMatched().updateAll()whenNotMatched().insertAll()執行()

該操作現在可以安全地在不同的日期和國家同時運行。

ConcurrentDeleteReadException

當並發操作刪除您的操作讀取的文件時,會發生此異常。常見的原因有刪除更新,或合並文件重寫操作。

ConcurrentDeleteDeleteException

當並發操作刪除了您的操作也刪除的文件時,會發生此異常。這可能是由重寫相同文件的兩個並發壓縮操作引起的。

MetadataChangedException

當並發事務更新Delta表的元數據時發生此異常。常見的原因有改變表格操作或寫入您的Delta表以更新表的模式。

ConcurrentTransactionException

如果使用相同檢查點位置的流查詢被並發啟動多次,並試圖同時寫入Delta表。永遠不要讓兩個流查詢使用同一個檢查點位置並同時運行。

ProtocolChangedException

此異常可在以下情況下發生:

  • 當Delta表升級到新的協議版本時。為使以後的操作成功,您可能需要升級Databricks運行時。

  • 當多個寫入器同時創建或替換一個表時。

  • 當多個寫入器同時寫入一個空路徑時。

看到表協議版本控製欲知詳情。