适用于 Functions 的 Azure SQL 触发器

注意

在消耗计划函数中,SQL 触发器不支持自动缩放。 如果自动缩放进程停止了函数,则会停止处理所有事件,并且需要手动重启。

要使 SQL 触发器能够获得缩放优势,请使用高级或专用计划。

Azure SQL 触发器使用 SQL 更改跟踪功能来监视 SQL 表的更改,并在创建、更新或删除行时触发函数。 有关用于 Azure SQL 触发器的更改跟踪的配置详细信息,请参阅设置更改跟踪。 有关 Azure Functions 的 Azure SQL 扩展的设置详细信息,请参阅 SQL 绑定概述

面向消耗计划和高级计划的 Azure SQL 触发器缩放决策是通过基于目标的缩放完成的。 有关详细信息,请参阅基于目标的缩放

功能概述

Azure SQL 触发器绑定使用轮询循环来检查是否有更改,并在检测到更改时触发用户函数。 在高级别,循环如下所示:

while (true) {
    1. Get list of changes on table - up to a maximum number controlled by the Sql_Trigger_MaxBatchSize setting
    2. Trigger function with list of changes
    3. Wait for delay controlled by Sql_Trigger_PollingIntervalMs setting
}

更改始终按照执行顺序进行处理,首先处理最早的更改。 有关更改处理的几点注意事项:

  1. 如果同时对多行进行更改,则将它们发送到函数的确切顺序取决于 CHANGETABLE 函数返回的顺序
  2. 一行的更改同时进行“批处理”。 如果在循环的每次迭代之间对一行进行了多次更改,那么该行将只存在一个更改条目,将会显示上次处理状态和当前状态之间的差异
  3. 如果对一组行进行了更改,然后对其中一半相同的行进行了另一组更改,则将首先处理未进行第二次更改的那一半行。 这样的处理逻辑是由于上述说明以及正在批处理的更改 - 触发器只会看到所做的“最后”更改,并根据它们来确定处理顺序

有关更改跟踪以及应用程序(如 Azure SQL 触发器)如何使用更改跟踪的详细信息,请参阅使用更改跟踪

用法示例

GitHub 存储库中提供了更多有关 Azure SQL 触发器的示例。

这些示例引用 ToDoItem 类和相应的数据库表:

namespace AzureSQL.ToDo
{
    public class ToDoItem
    {
        public Guid Id { get; set; }
        public int? order { get; set; }
        public string title { get; set; }
        public string url { get; set; }
        public bool? completed { get; set; }
    }
}
CREATE TABLE dbo.ToDo (
    [Id] UNIQUEIDENTIFIER PRIMARY KEY,
    [order] INT NULL,
    [title] NVARCHAR(200) NOT NULL,
    [url] NVARCHAR(200) NOT NULL,
    [completed] BIT NOT NULL
);

数据库和表中启用了更改跟踪

ALTER DATABASE [SampleDatabase]
SET CHANGE_TRACKING = ON
(CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON);

ALTER TABLE [dbo].[ToDo]
ENABLE CHANGE_TRACKING;

SQL 触发器绑定到 IReadOnlyList<SqlChange<T>>,即 SqlChange 对象列表,每个对象具有 2 个属性:

  • 项:已更改的项。 项的类型应遵循 ToDoItem 类中所示的表架构。
  • 操作:SqlChangeOperation 枚举中的值。 可能的值为 InsertUpdateDelete

以下示例显示了在 ToDo 表发生更改时调用的 C# 函数

using System;
using System.Collections.Generic;
using Microsoft.Azure.Functions.Worker;
using Microsoft.Azure.Functions.Worker.Extensions.Sql;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;


namespace AzureSQL.ToDo
{
    public static class ToDoTrigger
    {
        [Function("ToDoTrigger")]
        public static void Run(
            [SqlTrigger("[dbo].[ToDo]", "SqlConnectionString")]
            IReadOnlyList<SqlChange<ToDoItem>> changes,
            FunctionContext context)
        {
            var logger = context.GetLogger("ToDoTrigger");
            foreach (SqlChange<ToDoItem> change in changes)
            {
                ToDoItem toDoItem = change.Item;
                logger.LogInformation($"Change operation: {change.Operation}");
                logger.LogInformation($"Id: {toDoItem.Id}, Title: {toDoItem.title}, Url: {toDoItem.url}, Completed: {toDoItem.completed}");
            }
        }
    }
}

用法示例

GitHub 存储库中提供了更多有关 Azure SQL 触发器的示例。

该示例引用 ToDoItem 类、SqlChangeToDoItem 类、SqlChangeOperation 枚举和相应的数据库表:

在单独的 ToDoItem.java 文件中:

package com.function;
import java.util.UUID;

public class ToDoItem {
    public UUID Id;
    public int order;
    public String title;
    public String url;
    public boolean completed;

    public ToDoItem() {
    }

    public ToDoItem(UUID Id, int order, String title, String url, boolean completed) {
        this.Id = Id;
        this.order = order;
        this.title = title;
        this.url = url;
        this.completed = completed;
    }
}

在单独的 SqlChangeToDoItem.java 文件中:

package com.function;

public class SqlChangeToDoItem {
    public ToDoItem item;
    public SqlChangeOperation operation;

    public SqlChangeToDoItem() {
    }

    public SqlChangeToDoItem(ToDoItem Item, SqlChangeOperation Operation) {
        this.Item = Item;
        this.Operation = Operation;
    }
}

在单独的 SqlChangeOperation.java 文件中:

package com.function;

import com.google.gson.annotations.SerializedName;

public enum SqlChangeOperation {
    @SerializedName("0")
    Insert,
    @SerializedName("1")
    Update,
    @SerializedName("2")
    Delete;
}
CREATE TABLE dbo.ToDo (
    [Id] UNIQUEIDENTIFIER PRIMARY KEY,
    [order] INT NULL,
    [title] NVARCHAR(200) NOT NULL,
    [url] NVARCHAR(200) NOT NULL,
    [completed] BIT NOT NULL
);

数据库和表中启用了更改跟踪

ALTER DATABASE [SampleDatabase]
SET CHANGE_TRACKING = ON
(CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON);

ALTER TABLE [dbo].[ToDo]
ENABLE CHANGE_TRACKING;

SQL 触发器绑定到 SqlChangeToDoItem[],即 SqlChangeToDoItem 对象数组,每个对象具有 2 个属性:

  • 项:已更改的项。 项的类型应遵循 ToDoItem 类中所示的表架构。
  • 操作:SqlChangeOperation 枚举中的值。 可能的值为 InsertUpdateDelete

以下示例显示了在 ToDo 表发生更改时调用的 Java 函数:

package com.function;

import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.sql.annotation.SQLTrigger;
import com.function.Common.SqlChangeToDoItem;
import com.google.gson.Gson;

import java.util.logging.Level;

public class ProductsTrigger {
    @FunctionName("ToDoTrigger")
    public void run(
            @SQLTrigger(
                name = "todoItems",
                tableName = "[dbo].[ToDo]",
                connectionStringSetting = "SqlConnectionString")
                SqlChangeToDoItem[] todoItems,
            ExecutionContext context) {

        context.getLogger().log(Level.INFO, "SQL Changes: " + new Gson().toJson(changes));
    }
}

用法示例

GitHub 存储库中提供了更多有关 Azure SQL 触发器的示例。

该示例引用 ToDoItem 数据库表:

CREATE TABLE dbo.ToDo (
    [Id] UNIQUEIDENTIFIER PRIMARY KEY,
    [order] INT NULL,
    [title] NVARCHAR(200) NOT NULL,
    [url] NVARCHAR(200) NOT NULL,
    [completed] BIT NOT NULL
);

数据库和表中启用了更改跟踪

ALTER DATABASE [SampleDatabase]
SET CHANGE_TRACKING = ON
(CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON);

ALTER TABLE [dbo].[ToDo]
ENABLE CHANGE_TRACKING;

SQL 触发器绑定到 todoChanges,即对象列表,每个对象具有 2 个属性:

  • 项:已更改的项。 项的结构将遵循表架构。
  • 操作:可能的值为 InsertUpdateDelete

以下示例显示了在 ToDo 表发生更改时调用的 PowerShell 函数。

下面是 function.json 文件中的绑定数据:

{
    "name": "todoChanges",
    "type": "sqlTrigger",
    "direction": "in",
    "tableName": "dbo.ToDo",
    "connectionStringSetting": "SqlConnectionString"
}

配置部分解释了这些属性。

下面是 run.ps1 文件中的函数的示例 PowerShell 代码:

using namespace System.Net

param($todoChanges)
# The output is used to inspect the trigger binding parameter in test methods.
# Use -Compress to remove new lines and spaces for testing purposes.
$changesJson = $todoChanges | ConvertTo-Json -Compress
Write-Host "SQL Changes: $changesJson"

用法示例

GitHub 存储库中提供了更多有关 Azure SQL 触发器的示例。

该示例引用 ToDoItem 数据库表:

CREATE TABLE dbo.ToDo (
    [Id] UNIQUEIDENTIFIER PRIMARY KEY,
    [order] INT NULL,
    [title] NVARCHAR(200) NOT NULL,
    [url] NVARCHAR(200) NOT NULL,
    [completed] BIT NOT NULL
);

数据库和表中启用了更改跟踪

ALTER DATABASE [SampleDatabase]
SET CHANGE_TRACKING = ON
(CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON);

ALTER TABLE [dbo].[ToDo]
ENABLE CHANGE_TRACKING;

SQL 触发器绑定到 todoChanges,即对象数组,每个对象具有 2 个属性:

  • 项:已更改的项。 项的结构将遵循表架构。
  • 操作:可能的值为 InsertUpdateDelete

以下示例显示了在 ToDo 表发生更改时调用的 JavaScript 函数。

下面是 function.json 文件中的绑定数据:

{
    "name": "todoChanges",
    "type": "sqlTrigger",
    "direction": "in",
    "tableName": "dbo.ToDo",
    "connectionStringSetting": "SqlConnectionString"
}

配置部分解释了这些属性。

下面是 index.js 文件中的函数的示例 JavaScript 代码:

module.exports = async function (context, todoChanges) {
    context.log(`SQL Changes: ${JSON.stringify(todoChanges)}`)
}

用法示例

GitHub 存储库中提供了更多有关 Azure SQL 触发器的示例。

该示例引用 ToDoItem 数据库表:

CREATE TABLE dbo.ToDo (
    [Id] UNIQUEIDENTIFIER PRIMARY KEY,
    [order] INT NULL,
    [title] NVARCHAR(200) NOT NULL,
    [url] NVARCHAR(200) NOT NULL,
    [completed] BIT NOT NULL
);

数据库和表中启用了更改跟踪

ALTER DATABASE [SampleDatabase]
SET CHANGE_TRACKING = ON
(CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON);

ALTER TABLE [dbo].[ToDo]
ENABLE CHANGE_TRACKING;

SQL 触发器绑定到变量 todoChanges,即对象列表,每个对象具有 2 个属性:

  • 项:已更改的项。 项的结构将遵循表架构。
  • 操作:可能的值为 InsertUpdateDelete

以下示例显示了在 ToDo 表发生更改时调用的 Python 函数。

以下是 function_app.py 文件的示例 python 代码:

import json
import logging
import azure.functions as func
from azure.functions.decorators.core import DataType

app = func.FunctionApp()

@app.function_name(name="ToDoTrigger")
@app.sql_trigger(arg_name="todo",
                        table_name="ToDo",
                        connection_string_setting="SqlConnectionString")
def todo_trigger(todo: str) -> None:
    logging.info("SQL Changes: %s", json.loads(todo))

特性

C# 库使用 SqlTrigger 属性来声明函数中的 SQL 触发器,该函数具有以下属性:

Attribute 属性 说明
TableName 必需。 触发器监视的表的名称。
ConnectionStringSetting 必需。 应用设置的名称,其中包含数据库的连接字符串,该数据库包含被监视更改的表。 连接字符串设置名称对应于应用程序设置(本地开发为 local.settings.json),其中包含 Azure SQL 或 SQL Server 实例的连接字符串
LeasesTableName 可选。 用于存储租约的表的名称。 如果未指定,租约表名称将为 Leases_{FunctionId}_{TableId}。 可在此处找到有关如何生成此信息的详细信息。

批注

Java 函数运行时库中,对其值来自 Azure SQL 的参数使用 @SQLTrigger 注释 (com.microsoft.azure.functions.sql.annotation.SQLTrigger)。 此批注支持以下元素:

元素 说明
name 必需。 触发器绑定到的参数的名称。
tableName 必需。 触发器监视的表的名称。
connectionStringSetting 必需。 应用设置的名称,其中包含数据库的连接字符串,该数据库包含被监视更改的表。 连接字符串设置名称对应于应用程序设置(本地开发为 local.settings.json),其中包含 Azure SQL 或 SQL Server 实例的连接字符串
LeasesTableName 可选。 用于存储租约的表的名称。 如果未指定,租约表名称将为 Leases_{FunctionId}_{TableId}。 可在此处找到有关如何生成此信息的详细信息。

配置

下表解释了在 function.json 文件中设置的绑定配置属性。

“function.json”属性 说明
name 必需。 触发器绑定到的参数的名称。
type 必需。 必须设置为 sqlTrigger
direction 必需。 必须设置为 in
tableName 必需。 触发器监视的表的名称。
connectionStringSetting 必需。 应用设置的名称,其中包含数据库的连接字符串,该数据库包含被监视更改的表。 连接字符串设置名称对应于应用程序设置(本地开发为 local.settings.json),其中包含 Azure SQL 或 SQL Server 实例的连接字符串
LeasesTableName 可选。 用于存储租约的表的名称。 如果未指定,租约表名称将为 Leases_{FunctionId}_{TableId}。 可在此处找到有关如何生成此信息的详细信息。

可选配置

可以为用于本地开发的 SQL 触发器配置以下可选设置。

host.json

本部分介绍版本 2.x 及更高版本中可用于此绑定的配置设置。 host.json 文件中的设置将应用于函数应用实例中的所有函数。 下面的示例 host.json 文件仅包含此绑定的 2.x 版及更高版本设置。 若要详细了解版本 2.x 及更高版本中的函数应用程序配置设置,请参阅 Azure Functions 的 host.json 参考

设置 默认值 说明
MaxBatchSize 100 在发送到触发函数之前,使用触发器循环的每次迭代处理的最大更改数。
PollingIntervalMs 1000 处理每批更改之间的延迟(以毫秒为单位)。 (1000 毫秒为 1 秒)
MaxChangesPerWorker 1000 每个 application-worker 允许的用户表中挂起的更改的数量上限。 如果更改的计数超过此限制,则可能会导致横向扩展。此设置仅适用于启用了运行时驱动缩放的 Azure 函数应用。

示例 host.json 文件

下面是具有可选设置的 host.json 文件的示例:

{
  "version": "2.0",
  "extensions": {
      "Sql": {
        "MaxBatchSize": 300,
        "PollingIntervalMs": 1000,
        "MaxChangesPerWorker": 100
      }
  },
  "logging": {
    "applicationInsights": {
      "samplingSettings": {
        "isEnabled": true,
        "excludedTypes": "Request"
      }
    },
    "logLevel": {
      "default": "Trace"
    }
  }
}

local.setting.json

local.settings.json 文件存储应用设置和本地开发工具使用的设置。 只有在本地运行项目时,才会使用 local.settings.json 文件中的设置。 将项目发布到 Azure 时,请务必将任何必需的设置添加到函数应用的应用设置。

重要

因为 local.settings.json 可能包含机密(如连接字符串),因此你绝不能将其存储在远程存储库中。 支持 Functions 的工具提供了将 local.settings.json 文件中的设置与部署你的项目的函数应用中的应用设置同步的方法。

设置 默认值 说明
Sql_Trigger_BatchSize 100 在发送到触发函数之前,使用触发器循环的每次迭代处理的最大更改数。
Sql_Trigger_PollingIntervalMs 1000 处理每批更改之间的延迟(以毫秒为单位)。 (1000 毫秒为 1 秒)
Sql_Trigger_MaxChangesPerWorker 1000 每个 application-worker 允许的用户表中挂起的更改的数量上限。 如果更改的计数超过此限制,则可能会导致横向扩展。此设置仅适用于启用了运行时驱动缩放的 Azure 函数应用。

示例 local.settings.json 文件

下面是具有可选设置的 local.settings.json 文件的示例:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "FUNCTIONS_WORKER_RUNTIME": "dotnet",
    "SqlConnectionString": "",
    "Sql_Trigger_MaxBatchSize": 300,
    "Sql_Trigger_PollingIntervalMs": 1000,
    "Sql_Trigger_MaxChangesPerWorker": 100
  }
}

设置更改跟踪(必需)

设置用于 Azure SQL 触发器的更改跟踪需要执行两个步骤。 可以通过任何支持运行查询的 SQL 工具完成这两个操作,包括 Visual Studio CodeAzure Data StudioSQL Server Management Studio

  1. 在 SQL 数据库上启用更改跟踪,并将 your database name 替换为要监视的表所在的数据库的名称:

    ALTER DATABASE [your database name]
    SET CHANGE_TRACKING = ON
    (CHANGE_RETENTION = 2 DAYS, AUTO_CLEANUP = ON);
    

    CHANGE_RETENTION 选项指定了更改跟踪信息(更改历史记录)的保留时间。 SQL 数据库保留更改历史记录可能会影响触发器功能。 例如,如果 Azure Function 关闭数天然后恢复,则数据库会包含上述设置示例中过去两天进行的更改。

    可使用 AUTO_CLEANUP 选项来启用或禁用删除陈旧的更改跟踪信息的清除任务。 如果出现阻止触发器运行的临时问题,则可以关闭自动清理来暂停删除早于保留期的信息,直到问题得到解决。

    SQL 文档中提供了有关更改跟踪选项的详细信息。

  2. 在表中启用更改跟踪,并将 your table name 替换为要监视的表的名称(如果合适,更改架构):

    ALTER TABLE [dbo].[your table name]
    ENABLE CHANGE_TRACKING;
    

    触发器需要对被监视更改的表和更改跟踪系统表具有读取访问权限。 每个函数触发器都有一个关联更改跟踪,并租用架构 az_func 中的表。 如果这些表尚不存在,则由触发器创建。 Azure SQL 绑定库文档中提供了有关这些数据结构的详细信息。

启用运行时驱动的缩放

根据需要,函数可以根据用户表中待处理的更改数自动缩放。 使用 SQL 触发器时,若要允许函数在高级计划上正确缩放,需要启用运行时缩放监视。

在 Azure 门户的函数应用中,选择“配置”,然后在“函数运行时设置”选项卡上将“运行时缩放监视”切换到“打开”。

用于启用运行时缩放的 Azure 门户面板的屏幕截图。

重试支持

如需详细了解 GitHub 仓库中提供的 SQL 触发器重试支持租用表

启动重试

如果启动过程中发生异常,则主机运行时会自动尝试采用指数退避策略重启触发器侦听器。 这样的重启会一直进行,直到侦听器成功启动或者启动被取消。

连接断开重试

如果函数成功启动,但发生错误导致连接断开(例如,服务器掉线)则函数会继续尝试和重新打开连接,直到函数停止或连接成功。 如果成功重新建立连接,则会继续处理中断时正在处理的更改。

请注意,这些重试位于 SqlClient 具有的内置空闲连接重试逻辑之外,可以使用 ConnectRetryCountConnectRetryInterval 连接字符串选项进行配置。 会先尝试内置的空闲连接重试,如果未能重新建立连接,然后触发器绑定再尝试自己重新建立连接。

函数异常重试

如果处理更改时用户函数中发生异常,则会在 60 秒内在当前正在处理的行批次进行重试。 重试期间,将其他更改作为正常进行处理,但当超时期限过后,会忽略该批次中导致异常发生的行。

如果给定行的函数执行连续失败 5 次,则会完全忽略未来对该行的所有更改。 由于某一批次中的行具有不确定性,因此失败批次中的行最后可能会位于后续调用中的其他批次中。 这意味着,不是一定要忽略失败批次中的所有行。 如果该批次中的其他行导致发生异常,则“没有问题”的行最后会位于另一批次中,其在以后的调用中不会出现异常。

后续步骤