PAYATHON 2026

能否用日志或 KQL 追踪 Azure Synapse SQL 中超过100 MB的下载?

数据小陈

仅凭 SynapseBuiltinSqlPoolRequestsEnded,无法准确列出“哪些用户通过 SQL Explorer 下载了超过100 MB的文件”,也无法确认导出的 CSV、JSON 格式。KQL 可以分析已有日志,却无法补出未记录的下载事件。

要可靠追踪下载,需要在实际导出和下载路径上记录事件,并关联用户身份、导出任务、文件大小和传输记录。

为什么处理量不能代表下载量?

SynapseBuiltinSqlPoolRequestsEnded 记录内置 serverless SQL pool 已结束的请求,包含 Identity、Properties 等字段。它不是客户端下载日志,也不能直接用于所有 dedicated SQL pool 场景。参见 请求日志字段说明。

dataProcessedBytes 也不能简单理解为“扫描文件的字节数”。serverless SQL pool 的数据处理量可能包含存储读取、中间结果传输、向端点传输的未压缩数据,以及 CETAS 写入存储的数据,不能直接换算成最终下载文件的大小。参见 数据处理量的定义。

例如,扫描大量数据后执行 COUNT(*),可能只返回一个数值;同一结果集保存为 CSV 和 JSON,文件大小也可能不同。

要追踪下载,需要分别记录:

  • 查询执行:谁发起了 SQL 请求。
  • 文件生成:是否生成了导出文件,格式和大小是什么。
  • 文件传输:哪个身份请求了文件,服务端发送了多少字节。

即使服务端记录了传输成功,也不能据此保证文件已保存到用户本地磁盘。

现有日志可以怎样用 KQL 排查?

可以先筛选处理量较大的查询,作为调查线索。下面的查询查找过去 24 小时内单次处理量超过 100 MB 的请求:

let ThresholdBytes = 100000000;
SynapseBuiltinSqlPoolRequestsEnded
| where TimeGenerated >= ago(24h)
| extend ProcessedBytes = tolong(Properties.dataProcessedBytes)
| where ProcessedBytes > ThresholdBytes
| project
    TimeGenerated,
    Identity,
    QueryText = tostring(Properties.queryText),
    ProcessedBytes,
    ProcessedMB = ProcessedBytes / 1000000.0,
    ResultType,
    CorrelationId,
    _ResourceId
| order by ProcessedBytes desc

Properties.dataProcessedBytes 和 Properties.queryText 的用法可参考微软发布的 serverless SQL 请求日志查询示例。运行前,应查看工作区中的实际记录,确认字段是否存在及其类型。

查询结果只能说明请求的处理量较大,不能标记为“已下载超过100 MB”。示例按 1 MB = 1,000,000 bytes 计算;若业务要求使用 MiB,应将阈值改为 100 * 1024 * 1024,同时调整显示单位。

如何设计能够审计下载的路径?

可以考虑统一导出入口:

用户通过 Microsoft Entra ID 登录
    → 导出服务校验权限并创建 exportId
    → 执行 Synapse SQL 查询
    → 生成 CSV / JSON 文件并存入受控存储
    → 用户通过受控入口下载
    → 导出事件与传输日志进入 Log Analytics

这个应用架构需要自行实现,仅启用 Synapse 诊断设置无法完成。

导出服务至少应记录以下信息:

信息用途
用户对象 ID、租户 ID标识发起导出的身份
exportId、关联请求 ID连接查询、文件生成和下载记录
文件格式、文件字节数确定生成的文件类型及大小
文件标识、生成时间、任务状态区分成功、失败和取消的导出
下载请求 ID、发送字节数、传输状态识别完整、部分或重复传输

文件大小应在序列化完成后测量。如果还会压缩,应分别保存原始文件大小与压缩后大小,并明确告警使用哪个数值。

需要区分“生成了超过100 MB的文件”和“实际传输了超过100 MB的数据”。前者用导出任务日志判断,后者需要下载服务或存储访问日志。点击下载按钮或签发下载链接,都不能单独证明文件已被传输。

如果用户仍可绕过导出服务,直接连接 SQL 并读取相同数据,这条路径就无法覆盖全部数据提取行为。要提高覆盖率,需要结合 SQL 权限与访问路径控制;如果仍允许直接查询,应保留查询审计,并明确下载审计的覆盖范围。

文件存入 Azure Blob Storage 后能监控什么?

如果下载确实经过 Azure Blob Storage,可启用 Blob 服务的读取资源日志并发送到 Log Analytics。StorageBlobLogs 提供 OperationName、ResponseBodySize、AuthenticationType 和 RequesterObjectId 等字段,可用于分析存储服务返回的数据量。参见 StorageBlobLogs 字段说明。

下面的示例统计过去 24 小时内,用户通过 OAuth 对指定容器执行 GetBlob 时每小时收到的响应字节总量。将 your-export-container 替换为实际导出容器名称:

let ThresholdBytes = 100000000;
StorageBlobLogs
| where TimeGenerated >= ago(24h)
| where OperationName == "GetBlob"
| where AuthenticationType == "OAuth"
| where StatusCode in ("200", "206")
| extend ContainerName = tostring(split(parse_url(Uri).Path, "/")[1])
| where ContainerName == "your-export-container"
| where isnotempty(RequesterObjectId)
| summarize
    ResponseBytes = sum(ResponseBodySize),
    RequestCount = count()
    by RequesterObjectId, bin(TimeGenerated, 1h)
| where ResponseBytes > ThresholdBytes
| order by ResponseBytes desc

这条查询统计的是“每用户、每小时的响应字节总量”,并非单次完整文件下载。使用时还需要注意:

  • 分段读取、断点续传和重试可能产生多条记录,重复读取的字节会被累计。
  • 同一导出可能跨越两个小时,无法用固定小时窗口准确还原单次下载。
  • RequesterObjectId 可能属于应用或托管身份;如果下载经过后端代理,需要通过应用日志关联最终用户。
  • 使用 Shared Key 或 SAS 时,不能假设日志能够直接识别实际持有链接的个人,应另外维护授权与导出任务的关联。
  • 该示例只覆盖 GetBlob 路径;其他读取 API 应根据实际操作日志调整。

微软的 Blob Storage 监控指南 给出了基于 ResponseBodySize 统计读取量及使用身份字段审计的方式。

Synapse 读取源数据和用户下载最终导出文件是不同事件,不要把源数据的存储读取日志当作用户下载日志。

SQL 审计和数据外泄防护能否补齐缺口?

SQL 审计可以补充数据库活动线索。审计格式中包含 response_rows 等字段,但返回行数不能直接换算为 CSV、JSON 文件大小,也不能证明客户端已完成文件保存。启用前还应确认目标 SQL pool 和部署方式支持的审计配置。参见 Synapse SQL 审计日志格式。

Synapse 的 Data Exfiltration Protection 用于限制工作区向外部目标发送数据,例如约束 CETAS 的目标存储访问。它不提供 SQL Explorer 下载文件的字节审计,也不应被当作阻止已获授权用户保存查询结果的机制。其网络控制范围见 工作区数据外泄防护说明。

落地时,可以先保留现有请求日志作为异常查询线索,再让需要精确审计的大文件导出经过统一入口。分别针对“单次导出文件超过100 MB”和“用户在指定时间内累计传输超过100 MB”设置规则,调查结果会更容易解释。

备注:内容仅供参考。