行级并发通过检测行级别的更改并自动解决并发写入更新或删除同一数据文件中的不同行时发生的冲突,从而减少并发写入操作之间的冲突。
行级并发的要求
满足以下所有要求时,将自动启用行级并发:
- 使用 Databricks Runtime 14.3 LTS 及更高版本。
- 源表不使用分区。
- 源表已启用删除向量。
分区表不支持行级并发操作。 但是,在启用删除向量时,分区表仍然可以避免 OPTIMIZE 与写入操作之间的冲突。 请参阅行级别并发的限制。
对于 14.3 LTS 之前的 Databricks Runtime 版本,请参阅 行级并发旧版行为。
与行级并发的冲突矩阵
对于具有行级并发的源表,下表展示了每对并发写入操作在各隔离层中的表现。
并发元数据变更是表中所有结果的例外。 元数据更改(如 ALTER TABLE 命令或更新表架构的写入操作)可能导致所有并发写入操作失败,包括 INSERT。 参见 元数据变更冲突。
| 运算对 | WriteSerializable(默认) | 可序列化 |
|---|---|---|
| INSERT (1) + INSERT | 无法冲突 | 无法冲突 |
| INSERT + UPDATE、DELETE、MERGE INTO | 无法冲突 | 修改同一行时可能发生冲突。 失败的是 UPDATE, DELETE, 或 MERGE 操作,不是 INSERT。 |
| 插入 + 优化 | 无法冲突 | 无法冲突 |
| UPDATE、DELETE、MERGE INTO + UPDATE、DELETE、MERGE INTO | 修改同一行时可能会冲突 | 修改同一行时可能会冲突 |
| UPDATE、DELETE、MERGE INTO + OPTIMIZE | 使用 ZORDER BY 时,可能会发生冲突。 否则不会冲突。 |
使用 ZORDER BY 时,可能会发生冲突。 否则不会冲突。 |
| OPTIMIZE + OPTIMIZE | 使用 ZORDER BY 时,可能会发生冲突。 否则不会冲突。 |
使用 ZORDER BY 时,可能会发生冲突。 否则不会冲突。 |
(1) 此表中的所有 INSERT 操作都描述不包含从同一表读取数据的子查询的追加操作。
INSERT 包含从同一表读取数据的子查询的操作支持与 MERGE 相同的并发性。
注释
- 当一对数据发生冲突时,只有读取受影响数据的操作失败。 在不读取表的情况下追加数据的
INSERT并不是会失败的操作,因此重试逻辑应应用于并发的UPDATE、DELETE或MERGE。 - 具有标识列的表不支持并发事务处理。 请参阅标识列。
-
REORG操作的隔离语义与OPTIMIZE重写数据文件时完全相同。 使用REORG应用升级时,表协议会更改,这与所有正在进行的操作冲突。
在没有行级别并发的情况下发生写入冲突
对于没有行级并发的源表,下表展示了每对并发写操作在各隔离层中的表现。
并发元数据变更是表中所有结果的例外。 元数据更改(例如 ALTER TABLE 命令或更新表架构的写入操作)可能会导致所有并发写入操作失败,包括 INSERT。 参见 元数据变更冲突。
| 运算对 | WriteSerializable(默认) | 可序列化 |
|---|---|---|
| INSERT (1) + INSERT | 无法冲突 | 无法冲突 |
| INSERT + UPDATE、DELETE、MERGE INTO | 无法冲突 | 可能会有冲突。 失败的是 UPDATE, DELETE, 或 MERGE 操作,不是 INSERT。 请参阅 通过分区避免冲突。 |
| 插入 + 优化 | 无法冲突 | 无法冲突 |
| UPDATE、DELETE、MERGE INTO + UPDATE、DELETE、MERGE INTO | 可能会有冲突。 请参阅 通过分区避免冲突。 | 可能会冲突。 请参阅 通过分区避免冲突。 |
| UPDATE、DELETE、MERGE INTO + OPTIMIZE | 无法在启用删除向量的表中发生冲突,除非使用了ZORDER BY。 其他情况下,可能会出现冲突。 |
无法在启用删除向量的表中发生冲突,除非使用了ZORDER BY。 其他情况下,可能会出现冲突。 |
| OPTIMIZE + OPTIMIZE | 无法在启用删除向量的表中发生冲突,除非使用了ZORDER BY。 其他情况下,可能会出现冲突。 |
无法在启用删除向量的表中发生冲突,除非使用了ZORDER BY。 其他情况下,可能会出现冲突。 |
(1) 此表中的所有 INSERT 操作都描述不包含从同一表读取数据的子查询的追加操作。
INSERT 包含从同一表读取数据的子查询的操作支持与 MERGE 相同的并发性。
注释
- 当一对数据发生冲突时,只有读取受影响数据的操作失败。 在不读取表的情况下追加数据的
INSERT并不是会失败的那个操作,因此重试逻辑应放在并发的UPDATE、DELETE或MERGE上。 - 具有标识列的表不支持并发事务处理。 请参阅标识列。
-
REORG操作的隔离语义与OPTIMIZE重写数据文件时完全相同。 当你使用REORG执行升级时,表协议会发生变化,并与所有正在进行的操作产生冲突。
行级别并发的限制
行级并发存在限制。 对于以下操作,冲突解决遵循处理写入冲突的常规并发标准。 请参阅无行级并发时的写入冲突。
| 限度 | 说明 |
|---|---|
| 复杂条件子句 | 复杂数据类型(结构、数组、映射)、非确定性表达式、子查询和相关子查询的条件 |
MERGE 谓词要求 |
在 Databricks Runtime 14.2 中, MERGE 命令必须使用目标表上的显式谓词来筛选与源表匹配的行 |
| 性能权衡 | 行级冲突检测可能会增加执行总时间。 对于许多并发事务,编写器将延迟优先于冲突解决 |
通过使用分区来避免冲突
对于在冲突矩阵中标记为“可以冲突”的所有情况,仅当这两个操作影响同一组文件时,才会发生冲突。 为了使两组文件互不相交,请按操作条件中使用的列对表进行分区。
Example:
如果表未按日期进行分区,则命令 UPDATE table WHERE date > '2010-01-01' ... 和 DELETE table WHERE date < '2010-01-01' 冲突,因为两者都可能尝试修改相同的文件。 对表进行分区,通过 date 避开冲突。
注释
将表按具有高基数的列分区可能会导致性能问题,因为子目录的数量庞大。
避免与显式分区筛选器冲突
此异常通常会在并发执行 DELETE、UPDATE 或 MERGE 操作期间引发,即使这些操作更新的是不同的分区,也可能会读取同一分区。 在操作条件中明确分离:
// Problem: Condition can scan the entire table
deltaTable.as("t").merge(
source.as("s"),
"s.user_id = t.user_id AND s.date = t.date AND s.country = t.country")
.whenMatched().updateAll()
.whenNotMatched().insertAll()
.execute()
// Solution: Add explicit partition filters
deltaTable.as("t").merge(
source.as("s"),
"s.user_id = t.user_id AND s.date = t.date AND s.country = t.country AND t.date = '" + date + "' AND t.country = '" + country + "'")
.whenMatched().updateAll()
.whenNotMatched().insertAll()
.execute()
冲突异常
发生事务冲突时,您可能会遇到以下异常之一:
ConcurrentAppendException
当并发操作在操作读取的同一分区(或未分区表中的任何位置)中添加文件时,会发生此异常。 文件添加操作可能是由 INSERT、DELETE、UPDATE 或 MERGE 操作引起的。
使用默认的 WriteSerializable 隔离级别时,由 INSERT 操作添加且仅追加数据而不读取任何数据的文件,不会与任何操作发生冲突。 如果隔离级别是可序列化的,则任何追加可能会冲突。
重要
如果多个并发的 DELETE、UPDATE 或 MERGE 操作可能引用由 INSERT 操作附加的值,则在 WriteSerializable 模式下仍可能发生冲突。 失败的是 DELETE、UPDATE 或 MERGE 操作,因为该操作会读取附加的数据。 要避免此情况:
- 确保并发
DELETE、UPDATE或MERGE操作不会读取追加的数据 - 至多有一个
DELETE、UPDATE或MERGE操作可以读取追加的数据
ConcurrentDeleteReadException
当并发操作删除操作读取的文件时,会发生此异常。 常见原因是DELETE、UPDATE或MERGE等操作会重写文件。
ConcurrentDeleteDeleteException
当并发操作删除操作也删除的文件时,会发生此异常。 这可能是由于两个并发压缩操作重写相同的文件引起的。
MetadataChangedException(元数据更改异常)
当并发事务更新了Delta Lake表的元数据时,就会出现该例外。 常见原因是执行更新表架构的 ALTER TABLE 操作或写入。
ConcurrentTransactionException
如果使用同一检查点位置的流式查询同时多次启动,并尝试同时写入Delta Lake表,则会出现该异常。 永远不要同时运行具有相同检查点位置的两个流式处理查询。
协议更改异常
在以下情况下,可能会出现此异常:
- 您的 Delta Lake 表已升级到新的协议版本(您可能需要升级 Databricks Runtime)
- 多个编写器同时创建或替换表
- 多个编写器同时写入空路径
请参阅 Delta Lake 功能兼容性和协议。
行级并发旧版行为
在 Databricks Runtime 13.3 LTS 中,行级并发使用旧行为:
需要删除向量。
采用液态聚类的表会自动启用行级并发。