微服务和存储过程


34

在微服务体系结构中,存储过程是否被视为不良做法?

这是我的想法:

  • 大多数有关微服务的书籍建议每个微服务使用一个数据库。存储过程通常在整体数据库上工作。

  • 再次,大多数微服务架构书籍都指出,它们应该是自治的且松散耦合的。使用专门在Oracle中编写的存储过程,可以将微服务与该技术紧密结合。

  • 大多数微服务架构书籍(我已经阅读过)都建议微服务应面向业务(使用域驱动设计(DDD)设计)。通过将业务逻辑转移到数据库中的存储过程中,情况已不再如此。

有什么想法吗?


10
@ RandomUs1r对不起,这对我来说没有意义。为什么数据库结构必须是非关系的?当然,它可能具有外部参考,但其内部结构很可能是100%相关的
IMil

12
您的观点的问题是您所有的前提都是错误的。该微服务应该是自主的,松耦合的方式,首先,他们应该是松耦合的声明彼此 ; 如何管理内部组件的耦合是另一回事-并且具有次要的重要性(但并非不重要)-尤其是如果您可以在更新中替换整个微服务时。因此,没有理由不能在这些范围内使用sproc。同样,DDD 也不禁止进行垃圾处理或范式混合;一些问题的某些方面并非最适合面向对象。
FilipMilovanović

3
数据库的整体性与数据设计和实现有关,与使用或不使用存储过程无关。
RBarryYoung

5
“存储过程通常在整体数据库上工作。” 您应该强烈考虑丢弃从与您共享“事实”的任何来源获得的任何信息或建议。
StingyJack

3
@ RandomUs1r嗯,不,您唯一真正失去的就是您不能在参考键上使用外键约束-这才是微服务的重点。曾经有人反复证明过NoSql数据库以某种方式神奇地更快的想法,但是即使它们更快(不是),您也可以免费获得所有现有的基础架构,知识和现有的代码-这是巨大的。欧洲核子研究组织(CERN)和其他许多组织都使用关系数据库来管理TB级的数据。NoSql数据库有其用途,但是它们与您是否使用微服务无关。
Voo

Answers:


45

没有什么明确禁止或反对将存储过程与微服务一起使用。

免责声明:我不喜欢开发人员POV中的存储过程,但这与微服务没有任何关系。

存储过程通常在整体数据库上工作。

我认为您屈从于逻辑上的谬误。

如今,存储过程正在减少。仍在使用的大多数存储过程都来自保留了较旧的代码库。那时,与微服务流行起来相比,单片数据库也更加流行。

存储的proc和整体数据库都出现在旧的代码库中,这就是为什么您经常看到它们的原因。但这不是因果关系。您不使用存储的proc,因为您有一个整体数据库。您没有整体数据库,因为您使用存储的proc。

大多数有关微服务的书籍建议每个微服务使用一个数据库。

这些较小的数据库不能具有存储过程没有任何技术原因。

如前所述,我不喜欢存储过程。我发现它们笨重并且无法将来维护。我确实认为,将存储过程分散到许多小型数据库中会进一步加剧我不喜欢的问题。但这并不意味着它无法完成。

再次,大多数微服务架构书籍都指出,它们应该是自治的且松散耦合的。使用专门在Oracle中写的存储过程,可以将微服务与该技术紧密结合。

另一方面,无论您的微服务使用什么ORM,都可以使用相同的参数。并非每个ORM都将支持每个数据库。耦合(特别是紧密度)是一个相对的概念。这是一个尽可能松散的问题。

不管微服务如何,存储过程通常都会发生紧密耦合。我一般建议不要使用sproc,但由于您正在使用微服务,因此不建议这样做。这和以前一样:我不认为sprocs(通常)是要走的路,但这可能只是我的偏见,并且与微服务无关。

大多数我读过的msa书籍都建议微服务应面向业务(使用ddd设计)。通过将业务逻辑转移到数据库中的存储过程中,情况已不再如此。

这一直是我对sproc的主要抱怨:数据库中的业务逻辑。即使没有意图,它也总是以某种方式结束。

但是同样,无论您是否使用微服务,这种困扰都是存在的。看起来更大的问题的唯一原因是微服务推动您现代化整个体系结构,而在现代体系结构中存储不再受青睐。


4
我不确定说微服务会推动您现代化整个体系结构是否正确。通常,它们最终只是一堆计划不周的代码的庞然大物之上的一薄层。如果做得很好,它们可能会很好,但是它们并不会以任何方式真正推动您获得比任何其他体系结构都更好的编码。不过,很好的答案。你从我这里得到+1。
T. Sar-恢复莫妮卡

11
@ T.Sar modern与更好不同。重构(对微服务或其他任何东西)意味着改变。改变迫使您使用当前的想法。我们希望它们是更好的主意。
candied_orange

2
@ T.Sar:黑客是永恒的,您通常可以滥用任何系统(无论现代与否)来执行其可以从技术上处理但从未打算这样做的事情。微服务敦促您以不同的方式进行操作(并因此重新评估一些旧方法),但是它们无法普遍实施。使用通用执法,您通常会在兼容性/有效附带案例部门中受苦。
更加平坦

4
@candied_orange“现代与更好不一样”-我想我完全同意这一点。很好的一点。
T. Sar-恢复莫妮卡

3
现代甚至不是“足够”的代名词。
莱夫

24

编写软件需要您紧密结合技术。

至少要针对其中正在开发的编程语言提供的运行时环境。

更一般地说,尽管您会发现微服务与多种技术紧密结合:

  • 网络服务框架提供高级HTTP / SSL / SOAP协议实现
  • 存储库/ ORM / DAO框架提供持久性。
  • 数据处理框架,提供用于处理数据的工具。
  • 进程/线程/ OS框架,用于访问OS资源,例如多任务,文件系统,内存,GPU计算,扩展卡等。

那就是做一个准系统的微服务。

储存程序

存储过程仅仅是您可以选择使用或不使用的另一种技术。它不会神奇地使您的代码成为整体或微型。

虽然是:

  • 另一项技术。应用程序中存在的每种技术都会降低任何给定的程序员可以阅读,理解并为该技术组合做出明智的设计选择的可能性。
  • 一种使用不同编程范例的语言。对于非专家来说,试图强制他们自己的命令性,功能性,OO等...的观点太容易了,这常常导致结果差强人意。
  • API。必须像代码库中的任何其他类一样对其进行维护。这也意味着数据库正在提供非通用接口。这使得既难以替换数据库引擎本身,又难以透明地应用诸如内存缓存之类的通用行为。
  • 人工制品。必须对其进行版本控制,测试和部署。可以做到,但是数据库是活物,需要使用不同的方法。通常,您不能仅删除原始文件并进行替换。为了将系统迁移到所需状态,经常需要精心安排变更的时间。

这些都是真实的成本。在某些情况下,成本是合理的,而在其他情况下则没有。

通过托管脚本引擎,您将支付几乎相同的费用。唯一的减少就是您可以选择与宿主语言相同的编程范例。

商业逻辑

将业务规则转移到数据库中是不好的做法。只是不是因为存储过程。

这是一个坏习惯,因为数据库和业务逻辑在不同的剪切级别上运行。

  • 一个成熟的应用程序中的数据库可以使用数十年。通常,这些系统将定期更新引擎,但是数据库本身已迁移。它从一开始就没有被杀死和重建。微服务没有理由不能长期存在。

  • 将数十年来与业务规则更改的速度进行对比。以我的经验,一条旧的业务规则可能已有数年历史,但是大多数规则却很快发生了变化,您永远无法判断下一个将会改变。监管者提出的新要求,旧产品退役,信头变更,向老板汇报的员工人数变更等,等等,等等。

如果业务逻辑分布在各个剪切层上,尤其是分布在较慢且寿命较长的层中,它将产生变化的阻力。这不一定是一件坏事。毕竟,其中唯一具有零业务逻辑的数据库是三元组存储。

指定表模式的唯一行为就是将业务逻辑转移到数据库中。

建筑

您正在争用适当的工具来解决适当的问题,而无需太多的工具,也不会使其太难解决,从而无法制定和维护解决方案。

这不容易。

但是,让我们想想这是不可想象的,您将如何维护分布在多种语言中的业务逻辑?

  • 目录...,以便可以跟踪和维护每个业务规则实施。
  • 测试...可以针对每个业务规则使用,而无论在何处以及如何实施。
  • 一种参考实现,以便在发现差异时存在真相(或至少是辩论)。

但这也要付出代价。

  • 允许业务规则具有许多实现更好吗?每个人都可以利用团队技能和框架条款,但是需要严格的质量控制来避免出现许多小问题吗?
  • 还是用单一语言编写单一事实来源更好?可以说实施起来更便宜,但也有单一的故障源,它本身是一个整体组件,可以抵抗面对不同平台,框架或尚未发明的工具的变化?

8

我将以说我实际上维护了几个使用存储过程的微服务作为开头。另外,我在职业生涯的各个阶段都编写了很多存储过程,并且我绝对同意,如果使用不正确,事情可能会变得非常非常错误。

因此,简短的答案是,不,在微服务架构中,存储过程并不是天生就不好。但是您确实需要了解:

  1. 您正在为替换存储引擎添加障碍。如果某些操作或性能特征或功能限制要求您切换存储引擎,则成本将更高,因为您将编写和测试许多新代码。除非您使用本身具有性能问题的两阶段提交(2PC),否则运行多个存储引擎(在迁移阶段或根据性能需求隔离活动)可能会导致一致性问题。
  2. 您需要维护另一个API,这意味着您的依赖关系可能会中断。在过程中添加,删除或更改参数的类型可能会破坏现有代码。表和查询也会发生相同的情况,但是您的工具在跟踪可能出现问题的位置时可能会有所帮助。通常在运行时发现存储过程的问题,这是在开发/部署过程的后期。
  3. 您的数据库权限变得更加复杂。过程是否以登录用户或其他角色运行?您需要考虑一下并进行管理(希望以自动化的方式进行)。
  4. 您需要能够安全地迁移到新版本。通常,必须删除过程并重新创建过程。同样,权限可能会给您带来一些问题。
  5. 回滚失败的迁移可能意味着额外的精力。当生产环境与开发人员分离时,事情将变得更具挑战性。

这些是我认为通常值得使用的存储过程的一些用途:

  1. 强制执行编辑历史记录(审核日志)。触发器通常用于此目的,并且触发器是存储过程。在某些数据库中,也有可能完全不允许应用程序角色进行插入和更新:客户端执行具有不同权限的程序,这些程序以不同的角色运行,并强制执行所有必要的行为。
  2. 扩展检查约束。这可能会使您陷入业务逻辑领域,但是在某些情况下,数据库的内置约束工具可能不足以满足您的需求。通常,最好的表达支票的方法是使用命令性代码,如果您依靠应用程序为您完成错误数据,则可能会冒入错误数据的风险。
  3. 当视图不合适或过于复杂时,对复杂查询进行封装。我见过一些情况,正确的视图需要一些极其复杂的SQL,这些SQL在存储过程中可以更容易理解。这可能很少见,但确实发生了。

通常,我建议您首先尝试视图,并仅在必要时才使用过程。精心设计的视图实际上可以充当API,抽象出如何查询基础表的详细信息。在某些情况下,使用存储过程增强API(视图)是有意义的。甚至有可能直接从SQL查询中发出JSON,从而避免将数据从查询结果映射到应用程序的数据模型的麻烦。这是一个好主意,是您根据自己的需要确定的东西。

由于您应该已经通过某种自动化工具来管理数据库资源(模式,权限等),因此不行,存储过程对于微服务并不是天生就有害的。


我认为,如果您使用Java框架编写业务逻辑,则所有第一个要点也适用。切换DB-Engine将改变性能特征,并且需要重新测试甚至可能重写语句。如果您在应用程序中以字符串形式编写SQL语句,则在更改变量时会遇到同样的问题。你需要决定,如果你的应用程序使用一个技术的用户或个人用户连接到数据库等等...
法尔科

@Falco我认为,如果您仅使用JPA,更改数据库应该不会太困难。性能肯定会发生很大变化,并且始终需要进行测试。我维护的几个服务不是“微型”服务,因为它们可以扫描或聚合数百万或数十亿个数据点并返回任意大(通常为分页)的数据集。我无法想象为它们使用JPA,但可以想象在维护相同API的同时更改基础数据库引擎(并重写SQL)。
ngreen

4

存储过程是实现细节。存储在文件系统某处的数据库函数,lambda或Shell脚本都是实现细节,与体系结构无关。

大多数有关微服务的书籍建议每个微服务使用一个数据库。

好的,因此我们可以对这些数据库中的存储过程进行编码。

再次,大多数微服务架构书籍指出,它们应该是自治的且松散耦合的

在业务功能,开发的生命周期,管理,部署,团队的位置等之间。与实现细节无关。微服务不能解决技术问题(正好相反)。他们来解决管理和上市时间方面的问题。这是一种策略,而不是战术。一种以尽可能少的成本进行快速故障修复的方法。如果某个业务功能被证明是毫无价值的,我们将其删除而不会弄乱其他功能,部署,项目的管理,发布...

请注意,“拆分”已经像去耦代理一样起作用。假设我们有两个服务,甲由Oracle支持,乙由MongoDB支持。如果我们没有打破去耦的黄金法则,那么应该可以放弃对甲乙具有微不足道副作用的甲甲骨文。

使用专门在Oracle中写的存储过程,可以将微服务与该技术紧密结合。

这可能会导致供应商锁定。很多时候,由于历史或合同原因1,卖方被企业强加给企业。重要的是要知道如何不将我们的代码锁定到供应商。例如,某些ORM和框架实现了一种新的查询语言,该语言隐藏了数据库内置的功能和特征。

虽然,如果我们的服务足够细微,供应商锁定就不再是问题,因为它会影响整体的一小部分。一小部分应该可以快速更换(或隔离)。

我读过的大多数MSA书籍都建议微服务应面向业务(使用DDD设计)。

它应该是业务驱动的,这里就是事情。并非所有业务都利用DDD。DDD和微服务在很多方面都有重叠,但是它们不是因果关系。我们最终可能会遇到一个由贫血服务组成的微服务生态系统。或由以下两者组成:实现复杂域的服务和直接从数据库提供POJO的哑贫服务。没有错。

关于书籍,它们仅关注策略的执行。这些策略- 如何利用分布式计算的优势 -如何使其成功运行,但是(通常)与该策略无关。公司之间的策略各不相同,并且很少依赖开发人员。因此,我们仍然必须根据我们的特定需求,要求和约束来推断和修改书籍中的内容。目标是使业务战略有利可图并且可持续。

始终牢记,任何体系结构都是达到目的的手段。业务规则。我们不会为时尚或对艺术的热爱而建立微服务生态系统。


1

它实际上与微服务没有任何关系。

如果您的服务具有“旧式”分层体系结构,其中数据库是服务的基础,并且数据访问和业务逻辑层位于顶层,则存储过程可能很有意义。在这种架构中,服务与数据库之间的接口非常特定于服务的最内部细节。通常,每种受支持的数据库都有特定于服务的适配器,并且适配器公开的API的特殊性使得可以在基础层中使用存储过程。

像这样的架构有很多问题。最重要的是,这使得大多数逻辑很难进行单元测试。这些体系结构不再受欢迎。

如果您使用的是较新样式的“干净架构”,“洋葱架构”或类似样式,则数据库将是在外层指定的注入依赖项。由于它是在外层定义的,因此为数据库提供的接口必须是通用的。它不能反映服务的最内层细节,因为这些细节必须从体系结构的最外层隐藏。定义可以与任何数据库或单元测试工具一起使用的通用存储过程接口是非常困难的,而且并不是真正必要的,因此在这些类型的体系结构中,存储过程通常并不实用。

与微服务的关系仅仅是微服务是新的并且是上升的-我们不再做独石了-这些新的体系结构样式也在上升-我们不再做平面层了。

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.