由于Delta Lake为经典数据湖添加了事务层,因此我们可以执行经典的DML操作,例如更新、删除和合并。当在Delta表上执行删除操作时,操作是在数据文件级别执行的,根据需要删除和添加数据文件。已删除的数据文件不再是Delta表的当前版本的一部分,但不应立即物理删除,因为您可能希望通过时间旅行(时间旅行在第6章中讨论)恢复到表的旧版本。当运行更新操作时也是如此。根据需要,数据文件将从Delta表中添加和删除。
Delta Lake中最强大的DML操作是合并操作,它允许您在Delta表上执行"upsert"操作,即在Delta表上执行UPDATE、DELETE和INSERT操作的混合操作。您将源表和目标表连接在一起,编写匹配条件,然后指定匹配或不匹配的记录应该发生什么。
从Delta表中删除数据
我们将从一个干净的taxidb.YellowTaxis表开始。这个表是由第4.1章的"章节初始化"脚本创建的。它包含999,995万行:
sql
%sql
SELECT
COUNT(id)
FROM
taxidb.YellowTaxis
输出:
sql
+----------+
| count(1) |
+----------+
| 9999995 |
+----------+
表的创建和DESCRIBE HISTORY
taxidb.YellowTaxis Delta表是在"章节初始化"脚本中创建的,并复制到我们的/chapter04文件夹中。让我们查看该表的DESCRIBE HISTORY:
sql
%sql
DESCRIBE HISTORY taxidb.YellowTaxis
输出(仅显示相关部分):
lua
+-----------+--------------------------------+---------------------+
| operation | operationParameters | operationMetrics |
+-----------+--------------------------------+---------------------+
| WRITE | [('mode', 'Overwrite'), (...)] | [('numFiles', '2'), |
| | | ('numOutputRows', |
| | | '9999995'), ...] |
+-----------+--------------------------------+---------------------+
我们可以看到有一个事务,其中包含一个WRITE操作,写入了两个数据文件,总共包含9,999,995行。让我们查找有关这两个文件的一些详细信息。 在第2章中,您学习了如何使用事务日志来查看添加和删除文件的操作。让我们来看一下_delta_log目录:
bash
%sh
ls /dbfs/mnt/datalake/book/chapter04/YellowTaxisDelta/_delta_log/*.json
正如预期的那样,我们只看到一个事务日志条目:
bash
/dbfs/mnt/datalake/book/chapter04/YellowTaxisDelta/_delta_log/....0000.json
这个日志条目应该有两个文件添加操作,因为DESCRIBE HISTORY中的numfiles条目为两。再次使用我们的grep命令来查找这些部分:
bash
%sh
grep \"add\" /dbfs/.../chapter04/YellowTaxisDelta/_delta_log/..0000.json |
sed -n 1p > /tmp/commit.json
python -m json.tool < /tmp/commit.json
前一个命令的一个变种是,由于现在有两个条目,我们需要使用sed命令来提取正确的添加条目。
生成的输出(仅显示相关部分):
swift
{
"add": {
"path": "part-00000-...-c000.snappy.parquet",
...
"stats": "{\"numRecords\":5530100,...}}",
"tags": {
...
}
}
在这里,我们看到了我们表格的第一个数据文件的名称,以及该文件中的记录数量。我们可以使用相同的命令sed -n 2p来获取第二个添加操作,以获取第二个数据文件:
bash
{
"add": {
"path": "part-00001-...-c000.snappy.parquet",
"...: ""
stats": {\"numRecords\":4469895,...}}",
"tags": {
...
}
}
}
现在我们知道我们的表格有以下数据文件:

这些文件与我们的目录列表相对应,因此事务日志和目录列表报告是一致的:
bash
%sh
ls -al /dbfs/mnt/datalake/book/chapter04/YellowTaxisDelta
drwxrwxrwx 2 _delta_log
-rwxrwxrwx 1 part-00000-d39cbaa1-ea7a-4913-a416-e229aa1d5616-c000.snappy.parquet
-rwxrwxrwx 1 part-00001-947cccf8-41ae-4212-a474-fedaa0f6623d-c000.snappy.parquet
执行DELETE操作
假设我们想要删除单个记录,这里是RideId = 100000的记录。首先,我们应该通过SQL SELECT确保该记录确实仍然存在于表中:
sql
%sql
-- First, show that you have data for RideId = 10000
SELECT
RideId,
VendorId,
CabNumber,
TotalAmount
FROM
taxidb.YellowTaxis
WHERE
RideId = 100000
输出:
diff
+--------+----------+-----------+-------------+
| RideId | VendorId | CabNumber | TotalAmount |
+--------+----------+-----------+-------------+
| 100000 | 2 | T478827C | 7.56 |
+--------+----------+-----------+-------------+
要删除这一行,我们可以使用简单的SQL DELETE。我们可以使用DELETE命令根据谓词或筛选条件有选择性地删除行:
sql
%sql
DELETE FROM
taxidb.YellowTaxis
WHERE RideId = 100000
输出:
diff
+------------------+
|num_affected_rows |
+------------------+
| 1 |
+------------------+
我们可以确认我们实际上删除了一行。当我们使用DESCRIBE HISTORY命令查看表上的不同操作时,对于版本1,我们得到以下输出(为了可读性,行的输出进行了旋转):
sql
version: 1
timestamp: 2022-12-14T17:50:23.000+0000
operation: DELETE
operationParameters: [('predicate',
'["(spark_catalog.taxidb.YellowTaxis.RideId = 100000)"]')]
operationMetrics: [('numRemovedFiles', '1'),
('numCopiedRows', '5530099'),
('numAddedChangeFiles', '0'),
('executionTimeMs', '32534'),
('numDeletedRows', '1'),
('scanTimeMs', '1524'),
('numAddedFiles', '1'),
('rewriteTimeMs', '31009')]
我们可以看到操作是DELETE,我们用于删除的谓词是WHERE RideId = 100000。Delta Lake删除了一个文件(numRemovedFiles = 1)并添加了一个新文件(numAddedFiles = 1)。如果我们使用我们信任的grep命令查找详细信息,情况如下:


Delta Lake在DELETE操作中执行以下操作:
- Delta Lake首先扫描数据,以识别包含与谓词条件匹配的行的文件。在这种情况下,文件是e229aa1d5616数据文件;它包含RideId = 100000的记录。
- 在第二次扫描中,Delta Lake将匹配的数据文件读入内存。此时,Delta Lake在将新的干净数据文件写入存储之前删除了相关的行。这个新的数据文件是0d3e0e77c4c1数据文件。由于Delta Lake删除了一条记录,这个新的数据文件包含5,530,099条记录(5,530,100 - 1)。
- 随着Delta Lake完成DELETE操作,数据文件e229aa1d5616现在从Delta事务日志中删除,因为它不再是Delta表的一部分。这个过程称为"墓碑标记"。然而,重要的是要注意,这个旧数据文件并没有被删除,因为您可能仍然需要它来回溯到表的早期版本。您可以使用VACUUM命令来删除早于某个时间段的文件。时间旅行和VACUUM命令在第6章中有详细介绍。
- 数据文件fedaa0f6623d仍然是Delta表的一部分,因为对它没有进行任何更改。
我们可以在目录列表中看到已添加到目录中的一个数据文件(0d3e0e77c4c1):
diff
%sh
ls -al /dbfs/mnt/datalake/book/chapter04/YellowTaxisDelta/
drwxrwxrwx _delta_log
-rwxrwxrwx part-00000-96c2f047-99cc-4a43-b2ea-0d3e0e77c4c1-c000.snappy.parquet
-rwxrwxrwx part-00000-d39cbaa1-ea7a-4913-a416-e229aa1d5616-c000.snappy.parquet
-rwxrwxrwx part-00001-947cccf8-41ae-4212-a474-fedaa0f6623d-c000.snappy.parquet
数据文件e229aa1d5616并没有被物理删除。 从中最重要的信息是,删除事务发生在数据文件级别。Delta Lake将根据需要在事务日志中创建新的分区,并插入新的添加文件和删除文件操作。性能调整的第6章将涵盖VACUUM命令和其他清理不再需要的墓碑标记数据文件的方法。
DELETE性能调优提示
提升Delta Lake上DELETE操作性能的主要方法是添加更多谓词以缩小搜索范围。例如,如果您有分区化的数据,并且知道要删除的记录属于哪个分区,您可以将它们的分区子句添加到DELETE谓词中。
Delta Lake还提供了许多其他优化条件,如数据跳过和Z-order优化。Z-order重新组织每个数据文件的布局,使类似的列值被战略性地放置在一起,以实现最大效率。有关更多详细信息,请参考第5章。
在表中更新数据
现在您已经看到了DELETE操作对YellowTaxis表的影响,让我们快速看一下UPDATE操作。您可以使用UPDATE操作有选择地更新匹配筛选条件(也称为谓词)的任何行。
用例描述
让我们假设RideId = 9999994的记录的DropLocationId存在错误。首先,让我们使用以下SELECT确保此记录在我们的表中:
sql
SELECT
INPUT_FILE_NAME(),
RideId,
VendorId,
DropLocationId
FROM
taxidb.YellowTaxis
WHERE
RideId = 9999994
Spark SQL的INPUT_FILE_NAME()函数是一个方便的函数,它可以提供包含记录的文件名:
diff
+---------------------------+---------+----------+----------------+
| input_file_name() | RideId | VendorId | DropLocationId |
+---------------------------+---------+----------+----------------+
| .../part-00001-...parquet | 9999994 | 1 | 243 |
+---------------------------+---------+----------+----------------+
INPUT_FILE_NAME函数显示我们的记录位于fedaa0f6623d数据文件中,这是有道理的,因为它是最后的记录,所以从逻辑上说它位于最后创建的数据文件中。我们可以看到现有的DropLocationId目前是243。假设我们要将此字段更新为值250。接下来,我们将查看实际的DELETE操作。
在表中更新数据
现在我们可以将UPDATE SQL语句编写如下:
ini
%sql
UPDATE
taxidb.YellowTaxis
SET
DropLocationId = 250
WHERE
RideId = 9999994
我们可以看到我们更新了一行数据:
diff
+-------------------+
| num_affected_rows |
+-------------------+
| 1 |
+-------------------+
首先,让我们验证我们成功地更新了表格:
sql
%sql
SELECT
RideId,
DropLocationId
FROM
taxidb.YellowTaxis
WHERE
RideId = 9999994
+---------+----------------+
| RideId | DropLocationId |
+---------+----------------+
| 9999994 | 250 |
+---------+----------------+
输出显示记录已成功更新。当我们在表格上使用DESCRIBE HISTORY命令时,我们可以看到表格版本3上的UPDATE操作(为了清晰起见,输出进行了旋转):
sql
version: 3
timestamp: 2022-12-23 17:20:45+00:00
operation: UPDATE
operationParameters: [('predicate', '(RideId = 9999994)')]
operationMetrics: [('numRemovedFiles', '1'),
('numCopiedRows', '4469894'),
('numAddedChangeFiles', '0'),
('executionTimeMs', '25426'),
('scanTimeMs', '129'),
('numAddedFiles', '1'),
('numUpdatedRows', '1'),
('rewriteTimeMs', '25293')]
有一个文件被移除('numRemovedFiles','1'),并且有一个文件被添加('numAddedFiles','1')。我们还可以看到我们的UPDATE谓词[('predicate','(RideId = 9999994)')]。如果我们使用grep命令查找细节,情况如下:

Delta Lake对表执行更新操作分为两个步骤:
- 它查找并选择包含与谓词匹配的数据的数据文件,因此需要更新这些数据。Delta Lake尽可能使用数据跳过以加速此过程。在这种情况下,数据文件为fedaa0f6623d。我们还可以使用INPUT_FILE_NAME() SQL函数来验证这一点。
- 接下来,Delta Lake将每个匹配的文件读入内存,更新相关行,并将结果写入新的数据文件。在这种情况下,新的数据文件是50807db851f6。它现在包含fedaa0f6623d分区的所有记录,但已应用了更新,本例中是RideId = 9999994的更新。这个数据文件是50807db851f6。这个数据文件继续保持4,469,895条记录。一旦Delta Lake成功执行了UPDATE操作,它会为新的数据文件添加一个添加文件操作。
由于不再需要,数据文件fedaa0f6623d会在事务日志中通过删除提交操作从Delta表中移除。然而,与DELETE操作一样,文件并没有被物理删除,以防我们需要查看表的旧版本。数据文件0d3e0e77c4c1不受我们的更新影响,因此它仍然是Delta表的一部分,并继续保持5,530,099条记录。
性能调优技巧更新
与DELETE操作类似,提升Delta Lake上UPDATE命令性能的主要方法是添加更多的谓词以缩小搜索范围。搜索条件越具体,Delta Lake需要扫描和/或修改的文件就越少。 正如在前一节中提到的,Delta Lake的其他特性,如Z-ordering,可以用来进一步加速UPDATE操作。有关Delta Lake优化的详细信息,请参阅第5章。
使用MERGE操作进行数据Upsert
Delta Lake的MERGE命令允许您执行数据Upsert操作。Upsert是UPDATE和INSERT命令的结合。为了理解upserts,让我们假设我们有一个现有表(目标表)和一个包含新记录和对现有记录的更新的源表。以下是upsert的实际工作方式:
- 当源表中的记录与目标表中的现有记录匹配时,Delta Lake会更新该记录。
- 当没有这样的匹配时,Delta Lake会插入新记录。
用例描述
让我们将MERGE操作应用到我们的YellowTaxis表上。让我们对YellowTaxis表进行计数:
sql
%sql
SELECT
COUNT(*)
FROM
taxidb.YellowTaxis
我们可以看到我们有9,999,994条记录。
sql
+----------+
| count(1) |
+----------+
| 9999994 |
+----------+
我们希望重新插入在本章的DELETE部分中删除的RideId为100000的记录。因此,在我们的源数据中,我们需要一条RideId设置为100000的记录。 对于这个示例,让我们假设我们还希望更新RideId为999991的记录,因为VendorId被错误地插入,需要将它们的VendorId更新为1(VendorId = 1)这五条记录。
最后,我们希望将记录数调整为恰好10,000,000条记录,因此我们需要再增加5条记录,RideId范围从999996到10000000。
合并数据集
在我们为本书提供的伴随源数据文件中,有一个名为YellowTaxisMergeData.csv的文件,其中包含了这些记录。由于我们需要提供模式(schema),我们首先从现有表中读取模式:
ini
df = spark.read.format("delta").table("taxidb.YellowTaxis")
yellowTaxiSchema = df.schema
print(yellowTaxiSchema)
一旦我们加载了模式,就可以加载我们的合并数据CSV文件:
bash
yellowTaxisMergeDataFrame = spark \
.read \
.option("header", "true") \
.schema(yellowTaxiSchema) \
.csv("/mnt/datalake/book/chapter04/YellowTaxisMergeData.csv")
.sort(col("RideId"))
yellowTaxisMergeDataFrame.show()
这里显示了部分输出:
yaml
+----------+----------+------------------------------+
| RideId | VendorId | PickupTime |
+----------+----------+------------------------------+
| 100000 | 2 | 2022-03-01T00:00:00.000+0000 |
| 9999991 | 1 | 2022-04-04T20:54:04.000+0000 |
| 9999992 | 1 | 2022-04-04T20:54:04.000+0000 |
| 9999993 | 1 | 2022-04-04T20:54:04.000+0000 |
| 9999994 | 1 | 2022-04-04T20:54:04.000+0000 |
| 9999995 | 1 | 2022-04-04T20:54:04.000+0000 |
| 9999996 | 3 | 2022-03-01T00:00:00.000+0000 |
| 9999997 | 3 | 2022-03-01T00:00:00.000+0000 |
| 9999998 | 3 | 2022-03-01T00:00:00.000+0000 |
| 9999999 | 3 | 2022-03-01T00:00:00.000+0000 |
| 10000000 | 3 | 2022-03-01T00:00:00.000+0000 |
+----------+----------+------------------------------+
我们可以看到我们的RideId为100000的记录,五条新的记录(从9999991到9999995)的VendorId已经更新为1,以及从9999996开始的五条新记录。 我们希望在SQL中编写我们的MERGE语句,因此我们需要在SQL中使用我们的DataFrame。DataFrame类有一个非常方便的方法叫做createOrReplaceTempView,它正是这个目的:
bash
# Create a Temporary View on top of our DataFrame, making it
# accessible to the SQL MERGE statement below
yellowTaxisMergeDataFrame.createOrReplaceTempView("YellowTaxiMergeData")
现在我们可以在SQL中使用视图名称:
sql
%sql
SELECT
*
FROM
YellowTaxiMergeData
这显示的结果与使用DataFrame的display()方法完全相同。
MERGE语句
现在你可以按照以下方式编写你的MERGE语句:
sql
%sql
MERGE INTO taxidb.YellowTaxis AS target
USING YellowTaxiMergeData AS source
ON target.RideId = source.RideId
-- You need to update the VendorId if the records
-- matched
WHEN MATCHED
THEN
-- If you want to update all columns,
-- you can say "SET *"
UPDATE SET target.VendorId = source.VendorId
WHEN NOT MATCHED
THEN
-- If all columns match, you can also do a "INSERT *"
INSERT(RideId, VendorId, PickupTime, DropTime, PickupLocationId,
DropLocationId, CabNumber, DriverLicenseNumber, PassengerCount,
TripDistance, RateCodeId, PaymentType, TotalAmount, FareAmount,
Extra, MtaTax, TipAmount, TollsAmount, ImprovementSurCharge)
VALUES(RideId, VendorId, PickupTime, DropTime, PickupLocationId,
DropLocationId, CabNumber, DriverLicenseNumber, PassengerCount,
TripDistance, RateCodeId, PaymentType, TotalAmount, FareAmount,
Extra, MtaTax, TipAmount, TollsAmount, ImprovementSurCharge)
让我们分析这个语句:
- 我们将执行MERGE INTO操作,目标是YellowTaxis Delta表。请注意,我们为表起了一个别名source。
- 使用USING子句,我们指定了源数据集,这里是视图YellowTaxiMergeData,并为其起了别名source。
- 定义源数据集与目标数据集之间的连接条件。在我们的情况下,我们只想在VendorId上进行连接。如果你有分区数据,并且想要针对一个分区进行操作,你可能需要在这里使用AND语句添加条件。
- 指定当RideId在源和目标之间匹配时的操作。在这个用例中,我们希望使用源的VendorId(设置为1)来更新源数据。在这里,我们只更新了一个列,但如果需要,可以提供一个由逗号分隔的列列表。如果要更新所有列,我们可以简单地使用UPDATE SET *。
- 定义当记录存在于源但不存在于目标时的操作。在WHEN NOT MATCHED中,我们没有添加任何其他条件,但如果用例需要,可以添加任何其他子句。大多数情况下,您将提供一个INSERT语句作为操作。由于我们的源和目标列名相同,我们也可以使用简单的INSERT *。
当执行这个MERGE语句时,我们会得到以下输出:
diff
+-------------------+------------------+------------------+-------------------+
| num_affected_rows | num_updated_rows | num_deleted_rows | num_inserted_rows |
+-------------------+------------------+------------------+-------------------+
| 11 | 5 | 0 | 6 |
+-------------------+------------------+------------------+-------------------+
这个输出正是您所期望的:
- 我们更新了五行(VendorId从9999991到9999995)
- 我们插入了六行:
- 一行的RideId为100000
- 最后五行(从9999996到10000000)
我们可以看到前五行的更新:
sql
%sql
-- Make sure that the VendorId has been updated
-- for the records with RideId between
-- 9999991 and 9999995
SELECT
RideId,
VendorId
FROM
taxidb.YellowTaxis
WHERE RideId BETWEEN 9999991 and 9999995
+---------+----------+
| RideId | VendorId |
+---------+----------+
| 9999991 | 1 |
| 9999992 | 1 |
| 9999993 | 1 |
| 9999994 | 1 |
| 9999995 | 1 |
+---------+----------+
现在所有行的VendorId都已经变成了源VendorId 1。
我们可以看到插入的RideId为100000的记录:
sql
%sql
--Make sure that you have a record with VendorId = 100000
SELECT
*
FROM
taxidb.YellowTaxis
WHERE
RideId = 100000
输出(显示部分输出):
diff
+--------+----------+---------------------------+---------------------------+
| RideId | VendorId | PickupTime | DropTime |
+--------+----------+---------------------------+---------------------------+
| 100000 | 2 | 2022-03-01 00:00:00+00:00 | 2022-03-01 00:12:01+00:00 |
+--------+----------+---------------------------+---------------------------+
最后,我们可以看到具有 RideId > 9999995 的新行:
sql
%sql
SELECT
*
FROM
taxidb.YellowTaxis
WHERE
RideId > 9999995
+----------+----------+---------------------------+
| RideId | VendorId | PickupTime |
+----------+----------+---------------------------+
| 9999996 | 3 | 2022-03-01 00:00:00+00:00 |
| 9999997 | 3 | 2022-03-01 00:00:00+00:00 |
| 9999998 | 3 | 2022-03-01 00:00:00+00:00 |
| 9999999 | 3 | 2022-03-01 00:00:00+00:00 |
| 10000000 | 3 | 2022-03-01 00:00:00+00:00 |
+----------+----------+---------------------------+
总共有一千万条记录:
sql
%sql
SELECT
COUNT(RideId)
FROM
taxidb.YellowTaxis
+----------+
| count(1) |
+----------+
| 10000000 |
+----------+
使用MERGE修改不匹配的行:
Delta Lake的MERGE操作中的一个重要补充是最近发布的"WHEN NOT MATCHED BY SOURCE"子句。该子句可用于更新或删除目标表中没有与源表对应的记录。这可以用于删除目标表中不再存在于源表中的记录,或用于标记表示数据已删除或不活动的记录,同时仍然保留这些记录在目标表中(即软删除)。
要删除源表中存在但目标表中不存在的记录(即硬删除),请使用"WHEN NOT MATCHED BY SOURCE"子句,如以下示例代码中所示:
sql
%sql
MERGE INTO taxidb.YellowTaxis AS target
USING YellowTaxiMergeData AS source
ON target.RideId = source.RideId
WHEN MATCHED
UPDATE SET *
WHEN NOT MATCHED
INSERT *
-- DELETE records in the target that are not matched by the source
WHEN NOT MATCHED BY SOURCE
DELETE
如果您希望标记目标表中不再存在于源表中但满足特定条件的记录(即软删除),您可以指定一个MERGE条件和一个UPDATE操作:
sql
%sql
MERGE INTO taxidb.YellowTaxis AS target
USING YellowTaxiMergeData AS source
ON target.RideId = source.RideId
WHEN MATCHED
UPDATE SET *
WHEN NOT MATCHED
INSERT *
-- Set target.status = 'inactive' when records in the target table
-- don't exist in the source table and condition is met
WHEN NOT MATCHED BY SOURCE target.PickupTime >=
(current_date() - INTERVAL '5' DAY)
THEN
UPDATE SET target.status = 'inactive'
最佳实践是在添加"WHEN NOT MATCHED BY SOURCE"子句以更新或删除目标行时,同时添加可选的MERGE条件。这是因为如果没有指定MERGE条件,可能会导致大量目标行被修改。因此,为了获得最佳性能,应用一个MERGE条件到"WHEN NOT MATCHED BY SOURCE"子句中(例如,在前面的示例代码中使用 target.PickupTime >= (current_date() - INTERVAL '5' DAY) 来限制要更新或删除的目标行数,因为只有当该条件对某行为真时,才会修改目标行。
您还可以将多个"WHEN NOT MATCHED BY SOURCE"子句添加到MERGE操作中。当存在多个子句时,它们按照指定的顺序进行评估,所有"WHEN NOT MATCHED BY SOURCE"子句,除了最后一个子句,都必须包含条件。
使用DESCRIBE HISTORY分析MERGE操作
当我们在YellowTaxis表上运行DESCRIBE HISTORY时,在输出的operationsParameters部分,我们可以看到我们的MERGE谓词:
rust
operation: MERGE
[('predicate',
'(target.RideId = source.RideId)'),
('matchedPredicates', '[{"actionType":"update"}]'),
('notMatchedPredicates', '[{"actionType":"insert"}]')]
我们可以看到连接条件(target.RideId = source.RideId),指定更新的matchedPredicate以及指定插入的notMatchedPredicate。operationMetrics输出部分显示了不同操作的详细信息:
css
[('numTargetRowsCopied', '4469890'), ('numTargetRowsDeleted', '0'), ('numTargetFilesAdded', '4'), ('executionTimeMs', '91902'), ('numTargetRowsInserted', '6'), ('scanTimeMs', '8452'), ('numTargetRowsUpdated', '5'), ('numOutputRows', '4469901'), ('numTargetChangeFilesAdded', '0'), ('numSourceRows', '11'), ('numTargetFilesRemoved', '1'), ('rewriteTimeMs', '16782')]
在这里,我们再次可以看到有六行被插入(numTargetRowsInserted),并且有五行被更新(numTargetRowsUpdated)。我们的Delta表中新增了四个新的数据文件,而一个数据文件被移除。
MERGE 操作的内部工作原理
在内部,Delta Lake 完成 MERGE 操作的步骤如下: 首先,它执行目标表和源表之间的内部连接,以选择包含匹配的所有数据文件。这可以防止不必要地传输数据,可以安全地忽略。 接下来,它在目标表和源表中选择的文件之间执行外部连接,并根据用户指定的 INSERT、DELETE 或 UPDATE 子句应用相应的操作。 MERGE 与 UPDATE 或 DELETE 在底层的主要不同之处在于 Delta Lake 使用连接来完成 MERGE。这允许您在尝试提高性能时使用一些独特的策略。
总结
像DELETE、UPDATE和MERGE这样的DML操作对于任何表格格式和ETL操作都是必不可少的操作,所有这些操作都是通过事务日志来实现的。通过利用这些操作,您可以开始高效处理数据变更并在数据平台中维护数据完整性。
与传统的关系数据库管理系统中的表格类似,您在本章中了解到,使用Delta表格,您可以执行DELETE、UPDATE和MERGE操作,但您还可以使用SQL或DataFrame API来应用这些操作。更重要的是,您了解了Delta Lake内部是如何处理Delta表格目录中的底层文件,以及事务日志如何记录和跟踪这些不同类型的条目。使用DESCRIBE HISTORY命令,我们可以查看有关表格事务输出的详细信息。在执行操作期间,每个操作也可以利用谓词来减少扫描的文件数量并提高性能。在操作期间使用谓词之外,还有其他性能调整技术可以应用于Delta表格,您将在接下来的章节中学习到。