Docs 菜单
Docs 主页
/ /
Atlas App Services
/

数据库触发器

在此页面上

  • 创建数据库触发器
  • 配置
  • Trigger 详情
  • 触发器配置
  • eventType
  • 高级
  • 更改事件类型
  • 数据库触发器示例
  • 暂停触发器
  • 自动恢复暂停的触发器
  • 手动恢复暂停的触发器
  • 触发时间报告
  • 性能优化
  • 禁用突发操作的事件排序
  • 禁用集合级原像
  • 使用匹配表达式来限制触发器调用
  • 使用投影表达式减少输入数据大小

数据库触发器可以让您在关联的 MongoDB Atlas 集群上发生数据库更改时执行服务器端逻辑。您可以在单个集合、整个数据库和整个集群上配置触发器。

与在数据库服务器上运行的 SQL 数据触发器不同,触发器在独立于数据库服务器扩展的无服务器计算层上运行。触发器会自动调用 Atlas 函数,并可通过 AWS EventBridge 将事件转发给外部处理程序。

使用数据库触发器,实现事件驱动的数据交互。 例如,可以在相关文档发生更改时自动更新一个文档中的信息, 或者在插入新文档时向外部服务发送请求。

数据库触发器使用 MongoDB 变更流观察集合中的实时变化。一个变更流是一系列数据库事件,每个事件描述对该集合中一份文档的操作。您的应用为集合中创建的每个数据库触发器打开一个新的变更流。

重要

变更流限制

您可以在集群上打开的 change stream 总数有限制,具体取决于集群的大小。有关详细信息,请参阅 change stream 限制

不能在无服务器实例联合数据库实例上定义数据库触发器,因为它们不支持变更流功能。

可以控制哪些操作会导致触发器触发,以及触发器触发时会执行哪些操作。例如,可在文档的特定字段每次更新时运行某一函数。此函数可访问整个变更事件,因此您可始终了解更改的内容。还可将更改事件传递给 AWS EventBridge,以便在 Atlas 外部处理该事件。

触发器支持 $match 表达式来过滤更改事件,支持 $project 表达式来限制每个事件中包含的数据。

警告

在部署和数据库级别触发器中,允许以导致其他触发器触发的方式配置触发器,从而导致递归。示例包括:在同一数据库内写入到一个集合的数据库级触发器,或者在同一集群中将日志写入到另一个数据库的集群级记录器或日志转发器。

要在 App Services 用户界面中打开数据库触发器配置屏幕,请单击左侧导航菜单中的 Triggers,单击 Create a Trigger,然后选择 Trigger Type 旁边的 Database 标签页。

配置触发器,然后单击页面底部的 Save,将其添加到当前部署草稿中。

配置触发器的示例 UI

要使用 App Services CLI 创建数据库触发器,请执行以下操作:

  1. 将数据库触发器配置文件添加到本地应用程序目录的 triggers 子目录中。

  2. 部署 trigger:

    appservices push

注意

Atlas App Services 不会强制执行 Trigger 配置文件的特定文件名。但是,一旦导入,Atlas App Services 将重命名每个配置文件以匹配其定义的 Trigger 的名称,例如 mytrigger.json

Database Triggers 有以下配置选项:

Trigger Details 部分中,首先选择 Trigger Type 。将此值设置为数据库触发器的 Database

接下来,根据所需的粒度级别选择 Watch Against。具体选项包括:

  • Collection,当指定集合发生更改时

  • Database,当指定数据库中的任何集合发生更改时

  • Deployment,当指定集群上出现部署更改时。如果您选择“部署”源类型,则不会监视以下数据库的更改:

    • 管理员数据库 adminlocalconfig

    • 同步数据库 __realm_sync__realm_sync_<app_id>

    重要

    部署级别的源类型仅适用于专用层。

根据您使用的源类型,附加选项会有所不同。下表介绍了这些选项:

源类型
选项
Collection

  • Cluster Name与触发器关联的 MongoDB 集群的名称。

  • Database Name。包含监视集合的 MongoDB 数据库。

  • Collection Name。要监视的 MongoDB 集合。可选。如果将此选项留空,则 Source Type(源类型)将更改为 Database(数据库)。

  • Operation Type。导致触发器触发的操作类型。选择您希望触发器响应的操作类型。选项包括:

    • Insert

    • Update

    • 替换

    • 删除

    注意

    通过 MongoDB Compass 或 MongoDB Atlas 数据浏览器执行的更新操作会完全替换之前的文档。因此这些客户端的更新操作将生成“替换”更改事件,而不是“更新”事件。

  • Full Document。如果启用,更新变更事件将包括在 fullDocument 字段中应用该变更的已修改文档的最新多数提交版本

    注意

    无论此设置如何,插入替换事件始终包含 fullDocument 字段。删除事件从不包含 fullDocument 字段。

  • Document Preimage启用后,变更事件包括在 fullDocumentBeforeChange 字段中应用更改之前的最新已修改文档的副本。这有性能考量。除插入事件外的所有变更事件都包含文档原像。

Database

  • Cluster Name与触发器关联的 MongoDB 集群的名称。

  • Database Name。要监视的 MongoDB 数据库。可选。如果将此选项留空,Source Type(源类型)将更改为 Deployment(部署),除非您位于共享层,在这种情况下,App Services 将不允许您保存触发器。

  • Operation Type。导致触发器触发的操作类型。选择您希望触发器响应的操作类型。选项包括:

    • 创建集合

    • 修改集合

    • renameCollection

    • 删除集合

    • 分片集合

    • reshardCollection

    • refineCollectionShardKey

    注意

    通过 MongoDB Compass 或 MongoDB Atlas 数据浏览器执行的更新操作会完全替换之前的文档。因此这些客户端的更新操作将生成“替换”更改事件,而不是“更新”事件。

  • Full Document。如果启用,更新变更事件将包括在 fullDocument 字段中应用该变更的已修改文档的最新多数提交版本

    注意

    无论此设置如何,插入替换事件始终包含 fullDocument 字段。删除事件从不包含 fullDocument 字段。

  • Document Preimage启用后,变更事件包括在 fullDocumentBeforeChange 字段中应用更改之前的最新已修改文档的副本。这有性能考量。除插入事件外的所有变更事件都包含文档原像。对数据库和部署源禁用,以限制集群上对正在创建的新集合的不必要监视。

Deployment

  • Cluster Name与触发器关联的 MongoDB 集群的名称。

  • Operation Type。集群中发生的导致触发器触发的操作类型。选择您希望触发器响应的操作类型。选项包括:

    • 删除数据库

  • Full Document。如果启用,更新变更事件将包括在 fullDocument 字段中应用该变更的已修改文档的最新多数提交版本

    注意

    无论此设置如何,插入替换事件始终包含 fullDocument 字段。删除事件从不包含 fullDocument 字段。

  • Document Preimage启用后,变更事件包括在 fullDocumentBeforeChange 字段中应用更改之前的最新已修改文档的副本。这有性能考量。除插入事件外的所有变更事件都包含文档原像。对数据库和部署源禁用,以限制集群上对正在创建的新集合的不必要监视。

提示

原像和性能优化

原像需要额外的存储开销,可能会影响性能。如果没有在集合上使用原像,则应禁用原像。如需了解更多信息,请参阅禁用集合级原像

运行 MongoDB 4.4+ 的非分片 Atlas 集群以及运行 MongoDB 5.3 及更高版本的分片 Atlas 集群支持文档原像。可以将非分片集群(带有原像)升级到分片集群,只要该集群运行的是 5.3 或更高版本。

字段
说明
Auto-Resume Triggers

如果启用,则当在集群的 oplog 中找不到此触发器的恢复令牌时,触发器会在下一个相关的变更流事件中自动恢复处理事件。从触发器暂停到触发器恢复执行的所有变更流事件都不会触发触发器。

Event Ordering

如已启用,将按照触发事件发生的顺序进行处理。如已禁用,事件可以并行处理,当许多事件同时发生时,处理速度会更快。

如果启用了事件排序,此触发器的多次执行操作将根据更改事件的时间戳按顺序进行。如果禁用了事件排序,该触发器的多次执行操作将独立进行。

提示

性能优化

禁用事件排序,提高响应批量数据库操作的 Triggers 的性能。了解详情。

Skip Events On Re-Enable
默认禁用。如果启用,则不会处理在禁用此触发器时发生的任何变更事件。

Event Type(函数)部分,您可以选择触发器触发时要执行的操作。您可以选择运行函数或使用 AWS EventBridge

Advanced 部分中,提供了以下可选的配置选项:

字段
说明
Project Expression

一个 $project 表达式,它从变更流中的每个事件中选择一个字段子集。您可以使用它来优化触发器的执行

表达式是一个对象,可以将更改事件中的字段名称映射到 0(不包括该字段)或 1(包括该字段)。表达式的值可以是 01,但不能同时是 01。就是把投影分为两类,即包容性投影和排他性投影:

  • 包容性项目表达式指定要包含在每个更改事件文档中的字段。该表达式是一个对象,它将要包含的字段的名称映射到 1。如果不包含某个字段,则该字段不会包含在预计的更改事件中。

    例子

    以下投影仅包括 _idfullDocument 字段:

    {
    _id: 1,
    fullDocument: 1
    }
  • 独占项目表达式指定要从每个更改事件文档中排除的字段。该表达式是一个对象,它将要包含的字段的名称映射到 0。如果不排除某个字段,它将包含在预计的更改事件中。

    例子

    以下投影排除 _idfullDocument字段:

    {
    _id: 0,
    fullDocument: 0
    }

    注意

    您不能使用投影排除 operation_type 字段。这确保该触发器始终可以检查它是否应该针对给定事件的操作类型运行。

Match Expression

一个 $match 表达式文档,App Services 使用它来过滤哪些变更事件会导致触发器触发。触发器根据这个匹配表达式评估它收到的所有变更事件对象,仅当给定变更事件的表达式计算结果为 true 时才执行。

注意

对嵌入式字段使用点符号

MongoDB 对匹配表达式中的嵌入式文档执行完全等值匹配。如果要匹配嵌入式文档中的特定字段,请直接使用点符号引用该字段。请参阅 MongoDB Server 手册中的查询嵌入式文档,了解详情。

提示

性能优化

使用 $match 表达式限制触发器处理的字段数量。了解更多。

Maximum Throughput

如果链接的数据源是专用服务器(M10+ 层),则可以将最大吞吐量增加到超过默认的 10,000 个并发进程。

重要

要实现最大吞吐量,必须禁用事件排序。

在增加最大吞吐量之前,请考虑一个或多个触发器是否正在调用速率受限的外部 API。增加触发率可能会导致超限。

提高吞吐量还可能增加更大工作负载,影响集群的整体性能。

数据库变更事件代表关联 MongoDB Atlas 集群的特定集合中的各个变更。

每个数据库事件都具有与底层 变更流 发出的变更事件对象相同的操作类型和结构。变更事件具有以下操作类型:

操作类型
说明
Insert Document(插入文档)(所有触发器类型)
表示添加到集合的新文档。
Update Document(更新文档)(所有触发器类型)
表示对集合中现有文档的更改。
Delete Document(删除文档)(所有触发器类型)
表示从集合中删除的文档。
Replace Document(替换文档)(所有触发器类型)
表示用于替换集合中文档的新文档。
创建集合(仅限数据库和部署触发器类型)
表示创建新集合。
修改集合(仅限数据库和部署触发器类型)
表示修改集合。
重命名集合(仅限数据库和部署触发器类型)
表示正在重命名的集合。
Drop Collection(删除集合)(仅限数据库和部署触发器类型)
表示正在删除的集合。
分片集合(仅限数据库和部署触发器类型)
表示从未分片变为分片的集合。
重新分片集合(仅限数据库和部署触发器类型)
表示对集合分片的更改。
Refine Collection Shard Key(优化集合分片键)(仅限数据库和部署触发器类型)
表示集合分片键的变化。
Create Indexes(创建索引)(仅限数据库和部署触发器类型)
表示创建新索引。
Drop Indexes(删除索引)(仅限数据库和部署触发器类型)
表示正在删除的索引。
删除数据库(仅限部署触发器类型)
表示正在删除的数据库。

数据库变更事件对象采用以下常规形式:

{
_id : <ObjectId>,
"operationType": <string>,
"fullDocument": <document>,
"fullDocumentBeforeChange": <document>,
"ns": {
"db" : <string>,
"coll" : <string>
},
"documentKey": {
"_id": <ObjectId>
},
"updateDescription": <document>,
"clusterTime": <Timestamp>
}

一家在线商店希望在客户订单发生变化时通知其客户。他们将 store.orders 集合中的每个订单记录为类似于以下内容的文档:

{
_id: ObjectId("59cf1860a95168b8f685e378"),
customerId: ObjectId("59cf17e1a95168b8f685e377"),
orderDate: ISODate("2018-06-26T16:20:42.313Z"),
shipDate: ISODate("2018-06-27T08:20:23.311Z"),
orderContents: [
{ qty: 1, name: "Earl Grey Tea Bags - 100ct", price: NumberDecimal("10.99") }
],
shippingLocation: [
{ location: "Memphis", time: ISODate("2018-06-27T18:22:33.243Z") },
]
}

为了自动执行此过程,该商店创建了一个数据库触发器,用于监听 store.orders 集合中的更新事件。当触发器观察到更新事件时,它会将变更事件对象传递给其关联函数 textShippingUpdate。该函数会检查该变更事件以了解 shippingLocation 字段的任何更改,如果字段已更新,则向客户发送一条包含订单新位置的文本消息。

配置触发器的示例 UI
触发器配置
{
"type": "DATABASE",
"name": "shippingLocationUpdater",
"function_name": "textShippingUpdate",
"config": {
"service_name": "mongodb-atlas",
"database": "store",
"collection": "orders",
"operation_types": ["UPDATE"],
"unordered": false,
"full_document": true,
"match": {}
},
"disabled": false
}
textShippingUpdate
exports = async function (changeEvent) {
// Destructure out fields from the change stream event object
const { updateDescription, fullDocument } = changeEvent;
// Check if the shippingLocation field was updated
const updatedFields = Object.keys(updateDescription.updatedFields);
const isNewLocation = updatedFields.some(field =>
field.match(/shippingLocation/)
);
// If the location changed, text the customer the updated location.
if (isNewLocation) {
const { customerId, shippingLocation } = fullDocument;
const mongodb = context.services.get("mongodb-atlas");
const customers = mongodb.db("store").collection("customers");
const { location } = shippingLocation.pop();
const customer = await customers.findOne({ _id: customerId });
const twilio = require('twilio')(
// Your Account SID and Auth Token from the Twilio console:
context.values.get("TwilioAccountSID"),
context.values.get("TwilioAuthToken"),
);
await twilio.messages.create({
To: customer.phoneNumber,
From: context.values.get("ourPhoneNumber"),
Body: `Your order has moved! The new location is ${location}.`
})
}
};

数据库触发器可能会进入暂停状态,以响应阻止触发器的变更流继续的事件。可以暂停触发器的事件包括:

  • 无效事件,例如 dropDatabaserenameCollection 或网络中断导致的事件。

  • 恢复变更流所需的恢复令牌已不在集群 oplog 中。应用日志将此称为 ChangeStreamHistoryLost 错误。

如果触发暂停或失败,Atlas App Services 会向项目所有者发送一封电子邮件,提醒他们注意该问题。

您可以将触发器配置为在触发器因恢复令牌不再位于 oplog 中而被暂停时自动恢复。在恢复令牌丢失和恢复进程完成之间,触发器不会处理任何错过的变更流事件。

在 App Services 用户界面中创建或更新数据库触发器时,导航至要在暂停后自动恢复的触发器的配置页面。

Advanced (Optional) 部分,选择 Auto-Resume Triggers

保存并部署更改。

在使用 Realm CLI 创建或更新数据库触发器时,创建或导航至要在暂停后自动恢复的触发器的配置文件。

Trigger 的配置文件中,包括以下内容:

triggers/<trigger name>.json
{
"name": "<Trigger Name>",
"type": "DATABASE",
"config": {
"tolerate_resume_errors": true,
// ...rest of Database Trigger configuration
},
// ...rest of Trigger general configuration
}

使用以下命令部署更改:

appservices push --remote=<App ID>

当您手动恢复暂停的触发器时,您的应用程序会在该变更流停止后尝试在下一个变更流事件时恢复触发器。如果恢复令牌不再位于集群 oplog 中,则必须在没有恢复令牌的情况下启动触发器。这意味着触发器开始侦听新事件,但不会处理任何错过的过去事件。

您可以通过扩展 Atlas 集群来调整 oplog 大小,以便在暂停后将恢复令牌保留更长时间。保持 oplog 的大小比集群的峰值 oplog 吞吐量(GB/小时)大几倍,以降低暂停触发器的恢复令牌在触发器执行前从 oplog 掉落的风险。在 Atlas 集群指标中的 Oplog GB/Hour 图表中查看集群的 oplog 吞吐量。

您可以尝试从 App Services 用户界面或通过使用 App Services CLI 导入应用程序目录来重新启动挂起的触发器。

1

Triggers 页面的 Database Triggers 标签页上,在触发器列表中找到您要恢复的触发器。App Services 会将已暂停触发器的 Status 标记为 Suspended

在 UI 中标记为 “已暂停” 的数据库触发器
2

单击触发器的 Actions 列中的 Restart。您可以选择使用变更流恢复令牌重新启动触发器,或打开一个新的变更流。指示是否使用恢复令牌,然后单击 Resume Database Trigger

注意

恢复令牌

如果使用恢复令牌,App Services 会尝试在其处理的最后一个变更事件之后紧接的事件中恢复触发器的底层变更流。如果成功,触发器会处理暂停时发生的任何事件。如果未使用恢复令牌,触发器将开始侦听新事件,但不会为其暂停期间出现的任何事件进行触发。

UI 中的恢复数据库触发模式
1
appservices pull --remote=<App ID>
2

如果导出了应用程序的新副本,其中应当已经包含已暂停 trigger 的最新配置文件。您可以通过在 /triggers 目录中查找与 trigger 同名的 trigger 配置文件来确认配置文件是否存在。

3

验证触发配置文件存在后,将配置推送回应用。App Services 会自动尝试恢复部署中包含的任何暂停的触发器。

appservices push

Atlas App Services UI 中的触发器列表会显示三个时间戳:

最近修改

这是触发器的创建时间或最近更改时间。

最近心跳

Atlas App Services 会追踪上次运行触发器的时间。如果触发器未发送任何事件,服务器会发送一个心跳,以确保触发器的恢复令牌保持最新状态。最新发生的事件始终显示为 Latest Heartbeat(最新心跳)。

最后一次集群处理时间

Atlas App Services 还追踪 Last Cluster Time Processed(最近一次处理集群的时间),这是支持触发器的变更流最后一次发出事件的时间。如果自最新的心跳以来没有发生任何事件,那么它将比 Latest Heartbeat(最新心跳)要早。

如果您的触发器会在接收短暂事件(例如,插入数据是日常批处理作业的一部分)的集合上触发,则可以考虑禁用事件排序。

有序触发器等待执行特定事件的函数,直到先前事件的函数完成执行。因此,有序触发器实际上受到每个顺序触发器函数的运行时间的速率限制。这可能会导致出现在变更流上的数据库事件与触发器触发之间的明显延迟。在某些极端情况下,数据库事件可能会在长时间运行的有序触发器处理它们之前从 oplog 中脱离。

如果可能的话,无序触发器会并行执行函数,这可能会显著提高速度(取决于您的用例),但不保证多次执行触发器函数时遵循事件顺序。

文档原像要求集群记录有关集合上每个操作的额外数据。一旦为集合上的任何触发器启用原像,集群就会存储集合上每个操作的原像。

额外的存储空间和计算开销可能会降低触发器性能,具体取决于集群配置。

为了避免原像的存储和计算开销,必须禁用整个底层 MongoDB 集合的原像。这是与任何单个触发器的原像设置不同的设置。

如果禁用了集合级别原像,则该集合上的任何活动触发器都不能使用原像。但是,如果删除或禁用了某个集合上的所有原像触发器,那么也可以禁用集合级别原像。

要了解如何操作,请参阅禁用集合的原像

您可以通过在Match Expression 字段中指定 $match 表达式来限制触发器调用的次数。App Services 根据变更事件文档计算匹配表达式,仅当给定变更事件的表达式计算结果为 true 时才调用触发器。

匹配表达式是一个 JSON 文档,它使用 MongoDB 读取查询语法来指定查询条件。

我们建议仅当 Trigger 事件的数量明显成为性能问题时才使用匹配表达式。在此之前,接收所有事件并在 Trigger 函数代码中单独对其处理。

变更事件文档的具体结构取决于导致触发器触发的事件。有关详细信息,请参阅各事件类型的参考文档:

例子

仅当更改事件对象指定文档中的 status 字段发生更改时,以下匹配表达式才允许触发触发器。

updateDescription更新事件对象的一个字段。

{
"updateDescription.updatedFields.status": {
"$exists": true
}
}

以下匹配表达式允许触发器仅在文档的 needsTriggerResponse 字段为 true 时触发。插入更新替换事件的 fullDocument 字段表示指定操作执行后的文档。要接收 fullDocument 字段,您必须在触发器配置中启用 Full Document

{
"fullDocument.needsTriggerResponse": true
}

下面的步骤展示了一种测试匹配表达式是否按预期运行的方法:

  1. 下载 MongoDB Shell (mongosh) 并使用它连接您的集群。

  2. DB_NAME 替换为您的数据库名称,将 COLLECTION_NAME 替换为您的集合名称,将 YOUR_MATCH_EXPRESSION 替换为您要测试的匹配表达式,然后将以下内容粘贴到 mongosh 中以在现有集合上打开变更流:

    db.getSiblingDB(DB_NAME).COLLECTION_NAME.watch([{$match: YOUR_MATCH_EXPRESSION}])
    while (!watchCursor.isClosed()) {
    if (watchCursor.hasNext()) {
    print(tojson(watchCursor.next()));
    }
    }
  3. 在另一个终端窗口中,使用 mongosh 更改集合中的某些测试文档。

  4. 观察变更流筛选的内容。

Project Expression 字段中,使用 $project 表达式限制触发器处理的字段数量。

注意

项目仅包含在内

使用触发器时,投影表达式为包含性的。投影不支持将包含和排除相混合。投影表达式必须是包含性的,因为触发器要求包含 operationType

如果要排除单个字段,则投影表达式必须包括要排除的字段以外的所有字段。您只能明确排除 _id,因为默认情况下包含 _id

例子

触发器配置有以下 Project Expression

{
"_id": 0,
"operationType": 1,
"updateDescription.updatedFields.status": 1
}

App Services 传递给触发器函数的变更事件对象仅包含投影中指定的字段,如下例所示:

{
"operationType": "update",
"updateDescription": {
"updatedFields": {
"status": "InProgress"
}
}
}

有关集成到App Services App中的触发器的其他示例,请查看Github上的示例触发器。

后退

Triggers