我是否真的需要关系数据库的触发器,例如PostgreSQL?


10

我知道触发器可以用来验证存储的数据以保持数据库的一致性。但是,为什么不先在应用程序端对数据进行验证,然后再将其存储到数据库中呢?

例如,我们存储客户,并且我们想要执行一些在DDL级别上不容易完成的验证。 https://severalnines.com/blog/postgresql-triggers-and-stored-function-basics

另一个例子是审计。

更新资料

触发器和数据库事务如何一起工作。例如,如果我想对插入的数据进行验证。它是在事务内部完成的。之前发生了什么:事务已提交或触发器已执行?


However, why not perform validation of data on the application side before storing them into the database?好吧,这两个不是互斥的。您可能会在双方上验证不同的内容。尽管应用程序侧的验证以业务为中心,但数据库上的验证更以数据为中心。考虑由多个不同应用程序提供的数据库。
Laiv

您是否需要从外部数据中插入只是在不经意间将数据转储到系统中的数据?如果是这样,您可能会遇到需要以无效格式插入数据以便以后进行更正的情况。在这种情况下,触发器将导致无尽的问题。请记住这一点。有时您希望执行触发器。
Greg Burghardt

您的更新应该是一个单独的问题,也许是关于SO的问题,但是您的问题已经在Database Administrators.SE上有了答案。简而言之,即使在提交事务之前也执行了“更新后”触发器,并且如果在触发器内部引发异常,也会导致回滚。
布朗

@GregBurghardt大多数数据库都有可用于禁用此类活动的触发器的语句。
Blrfl

1
@Blrfl:是的,但是当您只想有条件地禁用当前会话的那些检查时,您需要意识到可以为所有连接的用户暂时禁用这些触发器。
Greg Burghardt

Answers:


12

这取决于您要构建哪种应用程序系统:

  • 如果要创建一个以应用程序为中心的系统,其中仅包含一个主应用程序,并且具有专用于该应用程序的专用数据库,并且理想情况下,一个团队负责并行开发应用程序和数据库,则可以保留所有验证逻辑并进行审计应用程序内部的逻辑。

    这样做的主要好处是,您不必在应用程序和数据库之间分配业务逻辑,因此维护和发展系统通常变得更加容易。另外,您不必将应用程序与特定类型的DBMS或DBMS供应商联系在一起。如果您的应用程序希望能够使用不提供触发器的轻量级数据库系统,则显然需要这种方法。

  • 但是,如果您创建了一个系统,其中许多不同的应用程序共享一个公共数据库,并且无法预先设想将来哪些应用程序将向其写入数据,或者哪些团队将开发将来将数据填充到db中的应用程序,则它将最好您的数据库将负责保证尽可能多的数据一致性。这就是触发器真正有用的地方。在较大的系统中,引用约束通常不足以满足此要求,但是调用存储过程的触发器几乎可以实现您需要的任何类型的验证。

使用触发器的另一个原因可能是性能:在复杂的数据模型中,遇到复杂的一致性规则并不罕见,该规则要求使用大量其他数据,这些数据不是客户端应用程序当前工作集的一部分。首先通过网络传输所有这些数据以使在客户端进行验证可能会对性能产生显着影响。

另请参阅此较早的SE帖子:用于数据库清理的应用程序逻辑与数据库触发器

因此,请自己决定要构建的系统类型,然后根据触发因素是否适合您的情况做出既定的决定。


3

我认为问题在于数据质量责任。

答案取决于您如何看待系统。

如果您将数据库视为独立于应用程序的独立,独立且自治的服务,则该数据库负责确保其包含的数据的一致性和质量。本质上是因为该数据库可以由其他应用程序使用,所以它不能依赖具有相同一致性和质量行为的第二个应用程序。在这些情况下,需要将数据库设计为公开API和自主行为。在此视图中,至少有两个应用程序,其中一个是数据库,另一个是使用它的应用程序。

相反,可以将数据库视为文件的复杂形式,该文件受应用程序直接和完全控制。从这个意义上讲,数据库演变为纯粹的序列化和文档导航工具。它可能会提供一些高级行为来支持查询和文档维护(例如JSON或XML工具),但又不必(就像大多数文件流一样)。在这种情况下,纯粹由程序负责在文件中维护正确的格式和内容。在此视图中,有一个应用程序。

在两种视图中,下一个问题是如何支持将数据库用作幻想文件或单独的服务。您可以通过以下方法实现此目的:

  • 使用数据库平台以表/视图/存储过程/触发器/等形式提供的工具...
  • 将数据库本身包装在所有客户端必须使用的服务中才能访问数据库
  • 将数据库包装在一个库中,所有客户端都必须使用该库才能访问数据。

每个都有各自的优点/缺点,并取决于系统在其中运行的环境的体系结构约束。

无论您采用哪种视图,始终都需要验证边界数据。

  • 验证用户输入的UI上的字段
  • 在离开客户端之前验证网络/ API请求
  • 在执行任何操作之前,请验证服务器中的网络/ API请求
  • 验证传递到业务规则中的数据
  • 在保留之前验证数据
  • 从持久性检索后验证数据
  • 依此类推

在每个边界上需要进行多少验证取决于不验证它的风险。

  • 将两个数字相乘?
    • 您输入的电话号码有问题吗?
  • 在给定的内存位置上调用过程?
    • 该内存位置中有什么?
    • 如果对象不存在或处于不良状态会怎样?
  • 在包含汉字的字符串上使用正则表达式?
    • regex模块可以处理unicode吗?
    • 正则表达式可以处理unicode吗?

集中验证逻辑是好的,但是imho触发器不是实现该逻辑的好方法。我曾经在一个系统上工作,其中多个应用程序都共享一个数据库,并且所有验证逻辑和副作用都是通过触发器和存储过程在数据库中实现的。我的印象是,最好在数据库前使用微服务并在其中实现所有逻辑。SQL数据库内部的非平凡逻辑是一种反模式。
Joeri Sebrechts

1
@JoeriSebrechts好吧,我要咬一口: 为什么数据库中的非平凡逻辑是反模式,与将它放在单独维护的程序中相比,为什么它更像是反模式?
Blrfl

@Blrfl有两个原因,用于访问数据库中逻辑的API不如Web服务API(更难版本化,更难使用,不容易缓存等),而数据库则使得清理结构和维护数据库变得更加困难。内部托管的代码库。以我的经验,将逻辑承载在数据库前面的Web服务中比在数据库内部更容易。
Joeri Sebrechts

@JoeriSebrechts我同意大多数数据库平台都提供了糟糕的工具来实现可信,有用和可开发的API。从很多方面来说,这无疑是引起很多痛苦的邀请。我的观点是关于透视的,认识到DB是一个精美的文件,或者它确实是一个单独的服务,这引出了下一个问题,即如何包装它以支持这种用法。我将详细说明我的答案。
Kain0_0

2

不,您永远不要使用触发器进行验证。

数据库仅负责其自身的完整性。任何面对验证的用户都应由您的应用程序执行。

数据库执行三个级别的完整性验证。第一个是字段级别验证。可能需要一个字段,如果没有值(空),则为错误。它也可以是检查约束;域具有枚举数的值。

其次,表之间存在关系。在一个表中,您存储一个或多个外键,将该表与其他表相关联,并要求这些值是“另一表”的有效键。考虑一个地址数据库,我们在其中支持不同国家的地址。地址中的国家/地区密钥必须指向已知国家/地区。数据(例如邮政编码)是否有效,与完整性检查无关。

第三,最复杂的是触发器。作为一般规则,这些规则应针对(有意无意地)涉及有条件的完整性规则。回到地址示例:如果一个国家没有邮政编码,则该列表中的一个国家如果有邮政编码将是一个问题。因此检查将是:如果该国家/地区没有邮政编码,则邮政编码字段应为null。

验证是应用程序的重点。德国邮政编码仅包含数字这一事实是应用程序应进行的检查,而不是数据库。这条线很细,因此在某些情况下,如果某些东西应该触发(保护数据库的完整性)或应用程序(面向用户的验证)中,您可能需要进行一些思考/讨论。


只是想补充一下,如果OP需要添加需要在数据库中的复杂验证规则,则他可以始终使用存储过程作为更安全的选择。
Borjab

@Borjab:也许是为了验证数据库的正确性。但是用户面临验证?号
MennoHölscher,

1
您的第一句话说“永远不要使用触发器来进行验证”,但是在下面写到“是的,您可以将触发器用于某些类型的验证,并且从本质上讲不清楚在哪里划界线”。这听起来很矛盾。我建议删除您的第一句话,这将大大改善您的答案。哦,您的最后一句话没有回答OP更新问题,因为“之前/之后”与事务无关。我建议也将其删除。(请参阅我在OP问题下方的评论)。
布朗

@DocBrown区别在于保护数据库免受损坏和面对用户的验证之间。因此,在“任何进一步的验证”中,我指的是面向用户的验证。我怎样才能使这种区分更清楚?首先,我删除了“进一步”。
MennoHölscher19年

2
在数据库中进行验证完全可以。就像在应用程序中做到这一点一样。两者都有其优势。在应用程序中进行验证意味着您每次运行SQL时都必须非常小心,而没有几乎每个复杂应用程序都需要的ORM。
Qwertie

1

审计是有效使用触发器的经典示例。我发现测试人员犯了一些错误(将客户端从一种服务级别转移到另一种服务级别),这要归功于触发器实现的Audit表。我强烈建议使用触发器进行审核。

验证可以在前端级别进行,但是我已经看到我处理过的数据库中的怪异错误(出生于3000岁的人等),并且由于其中一些是我自己做的,因此我强烈建议您增加一层以防万一。当然,可以通过检查约束来避免这些类型的错误,而且很多时候它们更有效(在MS SQL中,它们是首选方法;请始终检查文档)。


1

因为问题是关于我们是否真的需要关系数据库触发器,所以这里有一些其他使用触发器的用例:

  1. 用于其他答案中所述的审核。
  2. 从更广泛的意义上进行审计:如果数据库条目被更改,触发器可以记录该事件以进行异步后处理,例如每晚导出到另一个应用程序。
  3. 视图触发器:可以定义触发器instead of。这样一来,就可以从视图中插入,更新和删除条目。触发器可以将这些操作分散到多个表上。这是一种使受限视图可用而又不暴露基础表详细信息的方法。
  4. 要显式保存数据库周转时间:假设表A和B与中间表R之间存在N:M关系。您可以定义从R到A以及B的外键约束,并指定如果R中的条目被删除,则将其删除B中相应的条目将被删除。但是,业务逻辑有时要求A中的条目必须与B中的条目至少具有一个关系。在这种情况下,删除R的触发器可以帮助强制执行此逻辑:如果对于A中的条目,R中的最后一个条目被删除,则触发器可以删除A。在以应用程序为中心的视图中,至少需要两次转身。这是一个验证示例。还可以想到其他示例:除了用例(1),(2),
  5. 信任:有时数据库管理员不使用您的应用程序而在命令行上更改条目。管理员认真工作,知道自己的工作。但是,有时它们可​​能是错误的。如果一致性至关重要,则其安全带是触发因素。

作为缺点,业务逻辑分布在各层之间,这是维护的主要缺点。正如另一位作者所写,这是应用程序和数据库之间的界限,选择并不总是很清楚。我个人的看法是,触发器给开发人员带来了负担。它们可以节省开发时间。无疑,它们可以通过缓慢的网络连接提高性能,从而提高用户体验。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.