Azure Blob 存储中的更改源支持

更改源的用途是提供存储帐户中 Blob 和 Blob 元数据发生的所有更改的事务日志。 更改源提供这些更改的有序、有保证、持久、不可变、只读的日志。 客户端应用程序可以在流式处理或批处理模式下随时读取这些日志。 使用更改源可以生成高效且可缩放的解决方案,因此能够以较低的成本处理 Blob 存储帐户中发生的更改事件。

更改源的工作原理

更改源记录作为 Blob 存储在存储帐户中的特殊容器内,按标准的 Blob 定价计费。 你可以根据要求控制这些文件的保留期(请参阅当前版本的条件)。 更改事件根据 Apache Avro 格式(一种简洁且快速的二进制格式,通过内联架构提供丰富的数据结构)规范以记录的形式追加到更改源。 这种格式广泛用于 Hadoop 生态系统、流分析和 Azure 数据工厂。

可通过异步、增量或整体方式处理这些日志。 任意数目的客户端应用程序都可以按照自身的步调单独并行读取更改源。 分析应用程序(例如 Apache DrillApache Spark)可以直接将日志用作 Avro 文件,使你能够以较低的成本和较高的带宽处理这些日志,而无需编写自定义应用程序。

下图显示了如何将记录添加到更改源:

图中显示更改源如何提供 Blob 更改的有序日志

更改源支持非常适合基于已更改的对象处理数据的方案。 例如,应用程序可以执行以下操作:

  • 更新辅助索引,与缓存、搜索引擎或任何其他内容管理方案同步。
  • 基于对象发生的更改提取业务分析见解和指标,不管是采用流式处理方式还是批处理模式。
  • 针对企业数据管理的安全性、合规性或智能,存储、审核和分析对象在任意时间段内发生的更改。
  • 生成解决方案来备份、镜像或复制帐户中的对象状态,以满足灾难管理或合规性要求。
  • 生成连接的应用程序管道,以便根据创建的或更改的对象来响应更改事件或计划执行。

更改源是对象复制块 blob 的时间点还原的先决条件功能。

注意

更改源提供一个持久且有序的日志模型来记录发生在 Blob 中的更改。 在发生更改后的几分钟内,这些更改就会写入并出现在更改源日志中。 如果应用程序必须以比这快得多的速度对事件做出反应,请考虑改用 Blob 存储事件Blob 存储事件提供实时的一次性事件,使 Azure Functions 或应用程序能够快速对 Blob 中发生的更改做出反应。

启用和禁用更改源

必须在存储帐户中启用更改源,才能开始捕获和记录更改。 禁用更改源可停止捕获更改。 可以在门户或 PowerShell 中使用 Azure 资源管理器模板来启用和禁用更改。

启用更改源时,请注意以下几个事项。

  • 每个存储帐户中只有一个 Blob 服务的更改源。 更改源记录存储在 $blobchangefeed 容器中。

  • 仅在 Blob 服务级别捕获“创建”、“更新”和“删除”更改。

  • 更改源会捕获帐户中发生的所有可用事件的所有更改。 客户端应用程序可根据需要筛选出事件类型。 (请参阅当前版本的条件)。

  • 只有标准常规用途 v2、高级块 Blob 和 Blob 存储帐户才能启用更改源。 当前不支持启用了分层命名空间的帐户。 不支持常规用途 v1 存储帐户,但可以在不停机的情况下将其升级到常规用途 v2。有关详细信息,请参阅升级到 GPv2 存储帐户

使用 Azure 门户在存储帐户中启用更改源:

  1. Azure 门户中,选择存储帐户。

  2. 导航到“数据管理”下的“数据保护”选项 。

  3. 在“跟踪”下,选择“启用 blob 更改源” 。

  4. 选择“保存”按钮以确认数据保护设置。

    显示如何在 Azure 门户中启用更改源的屏幕截图

使用更改源

更改源生成多个元数据文件和日志文件。 这些文件位于存储帐户的 $blobchangefeed 容器中。 可以通过 Azure 门户或 Azure 存储资源管理器查看 $blobchangefeed 容器。

客户端应用程序可以通过更改源处理器 SDK 随附的 Blob 更改源处理器库来使用更改源。

参阅处理 Azure Blob 存储中的更改源日志

更改源细分

更改源是按小时段组织的更改日志,但系统每隔几分钟就会在其中追加和更新内容。 仅当在该小时内发生了 Blob 更改事件时,才会创建这些段。 因此,客户端应用程序可以使用在特定时间范围内发生的更改,不必搜索整个日志。 有关详细信息,请参阅规范

更改源的可用小时段在清单文件中描述,该文件指定了该段的更改源文件的路径。 $blobchangefeed/idx/segments/ 虚拟目录的列表按时间顺序显示这些段。 段的路径描述该段所代表的小时时间范围的开始时间。 可以使用该列表筛选出你感兴趣的日志段。

Name                                                                    Blob Type    Blob Tier      Length  Content Type    
----------------------------------------------------------------------  -----------  -----------  --------  ----------------
$blobchangefeed/idx/segments/1601/01/01/0000/meta.json                  BlockBlob                      584  application/json
$blobchangefeed/idx/segments/2019/02/22/1810/meta.json                  BlockBlob                      584  application/json
$blobchangefeed/idx/segments/2019/02/22/1910/meta.json                  BlockBlob                      584  application/json
$blobchangefeed/idx/segments/2019/02/23/0110/meta.json                  BlockBlob                      584  application/json

注意

启用更改源时,会自动创建 $blobchangefeed/idx/segments/1601/01/01/0000/meta.json。 可以放心地忽略此文件。 它是一个始终为空的初始化文件。

段清单文件 (meta.json) 显示该段的更改源文件在 chunkFilePaths 属性中的路径。 下面是段清单文件的示例。

{
    "version": 0,
    "begin": "2019-02-22T18:10:00.000Z",
    "intervalSecs": 3600,
    "status": "Finalized",
    "config": {
        "version": 0,
        "configVersionEtag": "0x8d698f0fba563db",
        "numShards": 2,
        "recordsFormat": "avro",
        "formatSchemaVersion": 1,
        "shardDistFnVersion": 1
    },
    "chunkFilePaths": [
        "$blobchangefeed/log/00/2019/02/22/1810/",
        "$blobchangefeed/log/01/2019/02/22/1810/"
    ],
    "storageDiagnostics": {
        "version": 0,
        "lastModifiedTime": "2019-02-22T18:11:01.187Z",
        "data": {
            "aid": "55e507bf-8006-0000-00d9-ca346706b70c"
        }
    }
}

注意

只有在帐户中启用更改源功能之后,才会显示 $blobchangefeed 容器。 启用更改源后,必须先等待几分钟,然后才能列出容器中的 Blob。

更改事件记录

更改源文件包含一系列更改事件记录。 每条更改事件记录对应于单个 Blob 发生的一项更改。 使用 Apache Avro 格式规范序列化记录并将其写入文件。 可以使用 Avro 文件格式规范读取记录。 有多个库可用于处理该格式的文件。

更改源文件以追加 Blob 的形式存储在 $blobchangefeed/log/ 虚拟目录中。 每个路径下的第一个更改源文件的文件名包含 00000(例如 00000.avro)。 添加到该路径的每个后续日志文件的名称编号将递增 1(例如:00001.avro)。

事件记录架构

有关每个属性的说明,请参阅 Blob 存储的 Azure 事件网格事件架构。 BlobPropertiesUpdated 和 BlobSnapshotCreated 事件当前是更改源独有的事件,尚不支持用于 Blob 存储事件。

注意

创建某个段后,该段的更改源文件不会立即显示。 延迟时长处于正常的更改源发布延迟间隔范围内,而该间隔为更改后的几分钟内。

架构版本 1

可以使用架构版本 1 在更改源记录中捕获以下事件类型:

  • BlobCreated
  • BlobDeleted
  • BlobPropertiesUpdated
  • BlobSnapshotCreated

以下示例显示了使用事件架构版本 1 的 JSON 格式的更改事件记录:

{
    "schemaVersion": 1,
    "topic": "/subscriptions/<subscription>/resourceGroups/<resource-group>/providers/Microsoft.Storage/storageAccounts/<storage-account>",
    "subject": "/blobServices/default/containers/<container>/blobs/<blob>",
    "eventType": "BlobCreated",
    "eventTime": "2022-02-17T12:59:41.4003102Z",
    "id": "322343e3-8020-0000-00fe-233467066726",
    "data": {
        "api": "PutBlob",
        "clientRequestId": "f0270546-168e-4398-8fa8-107a1ac214d2",
        "requestId": "322343e3-8020-0000-00fe-233467000000",
        "etag": "0x8D9F2155CBF7928",
        "contentType": "application/octet-stream",
        "contentLength": 128,
        "blobType": "BlockBlob",
        "url": "https://www.myurl.com",
        "sequencer": "00000000000000010000000000000002000000000000001d",
        "storageDiagnostics": {
            "bid": "9d725a00-8006-0000-00fe-233467000000",
            "seq": "(2,18446744073709551615,29,29)",
            "sid": "4cc94e71-f6be-75bf-e7b2-f9ac41458e5a"
        }
    }
}

架构版本 3

可以使用架构版本 3 在更改源记录中捕获以下事件类型:

  • BlobCreated
  • BlobDeleted
  • BlobPropertiesUpdated
  • BlobSnapshotCreated

以下示例显示了使用事件架构版本 3 的 JSON 格式的更改事件记录:

{
    "schemaVersion": 3,
    "topic": "/subscriptions/<subscription>/resourceGroups/<resource-group>/providers/Microsoft.Storage/storageAccounts/<storage-account>",
    "subject": "/blobServices/default/containers/<container>/blobs/<blob>",
    "eventType": "BlobCreated",
    "eventTime": "2022-02-17T13:05:19.6798242Z",
    "id": "eefe8fc8-8020-0000-00fe-23346706daaa",
    "data": {
        "api": "PutBlob",
        "clientRequestId": "00c0b6b7-bb67-4748-a3dc-86464863d267",
        "requestId": "eefe8fc8-8020-0000-00fe-233467000000",
        "etag": "0x8D9F216266170DC",
        "contentType": "application/octet-stream",
        "contentLength": 128,
        "blobType": "BlockBlob",
        "url": "https://www.myurl.com",
        "sequencer": "00000000000000010000000000000002000000000000001d",
        "previousInfo": {
            "SoftDeleteSnapshot": "2022-02-17T13:08:42.4825913Z",
            "WasBlobSoftDeleted": "true",
            "BlobVersion": "2024-02-17T16:11:52.0781797Z",
            "LastVersion" : "2022-02-17T16:11:52.0781797Z",
            "PreviousTier": "Hot"
        },
        "snapshot": "2022-02-17T16:09:16.7261278Z",
        "blobPropertiesUpdated" : {
            "ContentLanguage" : {
                "current" : "pl-Pl",
                "previous" : "nl-NL"
            },
            "CacheControl" : {
                "current" : "max-age=100",
                "previous" : "max-age=99"
            },
            "ContentEncoding" : {
                "current" : "gzip, identity",
                "previous" : "gzip"
            },
            "ContentMD5" : {
                "current" : "Q2h1Y2sgSW51ZwDIAXR5IQ==",
                "previous" : "Q2h1Y2sgSW="
            },
            "ContentDisposition" : {
                "current" : "attachment",
                "previous" : ""
            },
            "ContentType" : {
                "current" : "application/json",
                "previous" : "application/octet-stream"
            }
        },
        "storageDiagnostics": {
            "bid": "9d726370-8006-0000-00ff-233467000000",
            "seq": "(2,18446744073709551615,29,29)",
            "sid": "4cc94e71-f6be-75bf-e7b2-f9ac41458e5a"
        }
    }
}

架构版本 4

可以使用架构版本 4 在更改源记录中捕获以下事件类型:

  • BlobCreated
  • BlobDeleted
  • BlobPropertiesUpdated
  • BlobSnapshotCreated
  • BlobTierChanged
  • BlobAsyncOperationInitiated
  • RestorePointMarkerCreated

以下示例显示了使用事件架构版本 4 的 JSON 格式的更改事件记录:

{
    "schemaVersion": 4,
    "topic": "/subscriptions/<subscription>/resourceGroups/<resource-group>/providers/Microsoft.Storage/storageAccounts/<storage-account>",
    "subject": "/blobServices/default/containers/<container>/blobs/<blob>",
    "eventType": "BlobCreated",
    "eventTime": "2022-02-17T13:08:42.4835902Z",
    "id": "ca76bce1-8020-0000-00ff-23346706e769",
    "data": {
        "api": "PutBlob",
        "clientRequestId": "58fbfee9-6cf5-4096-9666-c42980beee65",
        "requestId": "ca76bce1-8020-0000-00ff-233467000000",
        "etag": "0x8D9F2169F42D701",
        "contentType": "application/octet-stream",
        "contentLength": 128,
        "blobType": "BlockBlob",
        "blobVersion": "2022-02-17T16:11:52.5901564Z",
        "containerVersion": "0000000000000001",
        "blobTier": "Archive",
        "url": "https://www.myurl.com",
        "sequencer": "00000000000000010000000000000002000000000000001d",
        "previousInfo": {
            "SoftDeleteSnapshot": "2022-02-17T13:08:42.4825913Z",
            "WasBlobSoftDeleted": "true",
            "BlobVersion": "2024-02-17T16:11:52.0781797Z",
            "LastVersion" : "2022-02-17T16:11:52.0781797Z",
            "PreviousTier": "Hot"
        },
        "snapshot": "2022-02-17T16:09:16.7261278Z",
        "blobPropertiesUpdated" : {
            "ContentLanguage" : {
                "current" : "pl-Pl",
                "previous" : "nl-NL"
            },
            "CacheControl" : {
                "current" : "max-age=100",
                "previous" : "max-age=99"
            },
            "ContentEncoding" : {
                "current" : "gzip, identity",
                "previous" : "gzip"
            },
            "ContentMD5" : {
                "current" : "Q2h1Y2sgSW51ZwDIAXR5IQ==",
                "previous" : "Q2h1Y2sgSW="
            },
            "ContentDisposition" : {
                "current" : "attachment",
                "previous" : ""
            },
            "ContentType" : {
                "current" : "application/json",
                "previous" : "application/octet-stream"
            }
        },
        "asyncOperationInfo": {
            "DestinationTier": "Hot",
            "WasAsyncOperation": "true",
            "CopyId": "copyId"
        },
        "storageDiagnostics": {
            "bid": "9d72687f-8006-0000-00ff-233467000000",
            "seq": "(2,18446744073709551615,29,29)",
            "sid": "4cc94e71-f6be-75bf-e7b2-f9ac41458e5a"
        }
    }
}

架构版本 5

可以使用架构版本 5 在更改源记录中捕获以下事件类型:

  • BlobCreated
  • BlobDeleted
  • BlobPropertiesUpdated
  • BlobSnapshotCreated
  • BlobTierChanged
  • BlobAsyncOperationInitiated

以下示例显示了使用事件架构版本 5 的 JSON 格式的更改事件记录:

{
    "schemaVersion": 5,
    "topic": "/subscriptions/<subscription>/resourceGroups/<resource-group>/providers/Microsoft.Storage/storageAccounts/<storage-account>",
    "subject": "/blobServices/default/containers/<container>/blobs/<blob>",
    "eventType": "BlobCreated",
    "eventTime": "2022-02-17T13:12:11.5746587Z",
    "id": "62616073-8020-0000-00ff-233467060cc0",
    "data": {
        "api": "PutBlob",
        "clientRequestId": "b3f9b39a-ae5a-45ac-afad-95ac9e9f2791",
        "requestId": "62616073-8020-0000-00ff-233467000000",
        "etag": "0x8D9F2171BE32588",
        "contentType": "application/octet-stream",
        "contentLength": 128,
        "blobType": "BlockBlob",
        "blobVersion": "2022-02-17T16:11:52.5901564Z",
        "containerVersion": "0000000000000001",
        "blobTier": "Archive",
        "url": "https://www.myurl.com",
        "sequencer": "00000000000000010000000000000002000000000000001d",
        "previousInfo": {
            "SoftDeleteSnapshot": "2022-02-17T13:12:11.5726507Z",
            "WasBlobSoftDeleted": "true",
            "BlobVersion": "2024-02-17T16:11:52.0781797Z",
            "LastVersion" : "2022-02-17T16:11:52.0781797Z",
            "PreviousTier": "Hot"
        },
        "snapshot" : "2022-02-17T16:09:16.7261278Z",
        "blobPropertiesUpdated" : {
            "ContentLanguage" : {
                "current" : "pl-Pl",
                "previous" : "nl-NL"
            },
            "CacheControl" : {
                "current" : "max-age=100",
                "previous" : "max-age=99"
            },
            "ContentEncoding" : {
                "current" : "gzip, identity",
                "previous" : "gzip"
            },
            "ContentMD5" : {
                "current" : "Q2h1Y2sgSW51ZwDIAXR5IQ==",
                "previous" : "Q2h1Y2sgSW="
            },
            "ContentDisposition" : {
                "current" : "attachment",
                "previous" : ""
            },
            "ContentType" : {
                "current" : "application/json",
                "previous" : "application/octet-stream"
            }
        },
        "asyncOperationInfo": {
            "DestinationTier": "Hot",
            "WasAsyncOperation": "true",
            "CopyId": "copyId"
        },
        "blobTagsUpdated": {
            "previous": {
                "Tag1": "Value1_3",
                "Tag2": "Value2_3"
            },
            "current": {
                "Tag1": "Value1_4",
                "Tag2": "Value2_4"
            }
        },
        "restorePointMarker": {
            "rpi": "cbd73e3d-f650-4700-b90c-2f067bce639c",
            "rpp": "cbd73e3d-f650-4700-b90c-2f067bce639c",
            "rpl": "test-restore-label",
            "rpt": "2022-02-17T13:56:09.3559772Z"
        },
        "storageDiagnostics": {
            "bid": "9d726db1-8006-0000-00ff-233467000000",
            "seq": "(2,18446744073709551615,29,29)",
            "sid": "4cc94e71-f6be-75bf-e7b2-f9ac41458e5a"
        }
    }
}

规范

  • 更改事件记录只会追加到更改源。 一旦追加这些记录,它们就不可变,且记录位置是稳定的。 客户端应用程序可以在更改源的读取位置维护其自身的检查点。

  • 在更改后的几分钟内,就会追加更改事件记录。 客户端应用程序可以选择在追加记录后使用这些记录以进行流式访问,或者在任何其他时间批量使用这些记录。

  • 更改事件记录按每个 Blob 的修改顺序排序。 Azure Blob 存储中不会定义跨 Blob 的更改顺序。 前一段中的所有更改位于后续段中任何更改的前面。

  • 使用 Apache Avro 1.8.2 格式规范将更改事件记录序列化为日志文件。

  • eventType 值为 Control 的更改事件记录是内部系统记录,不反映对帐户中的对象所做的更改。 可以放心地忽略这些记录。

  • storageDiagnostics 属性包中的值仅供内部使用,按设计不供应用程序使用。 应用程序不应该对该数据存在协定性依赖关系。 可以放心地忽略这些属性。

  • 段所代表的时间是近似时间,其时长为 15 分钟。 因此,为了确保能够使用指定时间内的所有记录,请使用连续的前一小时和后一小时段。

  • 由于对日志流进行了内部分区以管理发布吞吐量,因此每个段可以包含不同数量的 chunkFilePaths。 保证每个 chunkFilePath 中的日志文件包含互斥的 Blob,并且可以并行使用和处理这些文件,而不会在迭代期间违反每个 Blob 的修改顺序。

  • 段的最初状态为“Publishing”。 一旦完成将记录追加到段的过程,段的状态将变成“Finalized”。 应用程序不应使用在 $blobchangefeed/meta/Segments.json 文件中 LastConsumable 属性日期之后创建的任何段中的日志文件。 下面是 $blobchangefeed/meta/Segments.json 文件中 LastConsumable 属性的示例:

{
    "version": 0,
    "lastConsumable": "2019-02-23T01:10:00.000Z",
    "storageDiagnostics": {
        "version": 0,
        "lastModifiedTime": "2019-02-23T02:24:00.556Z",
        "data": {
            "aid": "55e551e3-8006-0000-00da-ca346706bfe4",
            "lfz": "2019-02-22T19:10:00.000Z"
        }
    }
}

条件和已知问题

本部分介绍当前的更改源版本中的已知问题和条件。

  • 日志文件的 url 属性目前始终是空的。
  • segments.json 文件的 LastConsumable 属性不会列出更改源最终处理的第一个段。 此问题只会在对第一个段进行最终处理之后才出现。 第一个小时之后的所有后续段会准确捕获到 LastConsumable 属性中。
  • 目前,在调用 ListContainers API 时看不到 $blobchangefeed 容器,且在 Azure 门户或存储资源管理器中也看不到该容器。 可以通过直接在 $blobchangefeed 容器上调用 ListBlobs API 来查看内容。
  • 以前启动了帐户故障转移的存储帐户可能会出现不显示日志文件的问题。 将来的任何帐户故障转移也可能会影响日志文件。

功能支持

启用 Data Lake Storage Gen2、网络文件系统 (NFS) 3.0 协议或 SSH 文件传输协议 (SFTP) 可能会影响对此功能的支持。

如果已启用这些功能中的某一项,请参阅 Azure 存储帐户中的 Blob 存储功能支持,以评估对此功能的支持。

常见问题

更改源与存储分析日志记录有何区别?

分析日志包含所有读取、写入、列出和删除操作的记录,以及对所有操作的成功和失败请求的记录。 分析日志的记录原则是“尽量记录”,不保证记录顺序。

更改源解决方案提供对帐户成功执行的变动或更改操作(例如创建、修改和删除 Blob)的事务日志。 更改源保证按照每个 Blob 的成功更改顺序记录和显示所有事件,因此你无需从大量的读取操作或失败请求中筛选出干扰性的信息。 从根本上讲,更改源是针对需要特定保证的应用程序开发进行设计和优化的。

应该使用更改源还是存储事件?

可以利用这两项功能,因为更改源和 Blob 存储事件以相同的交付可靠性保证提供相同的信息,其主要差别在于事件记录的延迟、排序和存储。 在更改后的几分钟内,更改源就会将记录发布到日志,同时还保证每个 Blob 的更改操作顺序。 存储事件将实时推送,可能不会排序。 更改源事件根据你自己定义的保留期,以只读的稳定日志形式持久存储在存储帐户中,而存储事件由事件处理程序即时使用,除非你显式存储这些事件。 借助更改源,任意数量的应用程序都可以通过 Blob API 或 SDK 在方便的时间使用日志。

后续步骤