在你的应用程序中执行sql计算的优缺点是什么?
shopkeeper
表有以下领域:
id (bigint),amount (numeric(19,2)),createddate (timestamp)
比方说,我有上表。 我想获取昨天的logging,并通过将金额印在美分生成报告。
一种做法是在我的Java应用程序中执行计算并执行一个简单的查询
Date previousDate ;// $1 calculate in application Date todayDate;// $2 calculate in application select amount where createddate between $1 and $2
然后循环遍历logging,并在我的Java应用程序中将金额转换为分,并生成报告
另一种方式就像在SQL查询本身执行计算:
select cast(amount * 100 as int) as "Cents" from shopkeeper where createddate between date_trunc('day', now()) - interval '1 day' and date_trunc('day', now())
然后遍历logging并生成报告
从某种意义上说,我的所有处理都是在java应用程序中完成的,并且简单的查询被触发。 在其他情况下,所有的转换和计算都是在Sql查询中完成的。
上面的用例只是一个例子,在一个真实场景中,一个表可能有许多列需要类似的处理。
你能告诉我哪种方法在性能和其他方面更好,为什么?
这取决于很多因素 – 但最重要的是:
- 计算的复杂性(喜欢在应用程序服务器上执行复杂的操作,因为这会扩展;而不是一个可扩展的数据库服务器)
- 数据量(如果您需要访问/汇总大量数据,在数据库服务器上执行此操作将节省带宽,而如果可以在索引内完成聚合,则使用磁盘io)
- 方便(sql不是复杂工作的最佳语言 – 尤其对于程序性工作来说不是很好,但是对于基于集合的工作来说非常好;糟糕的error handling,尽pipe如此)
与往常一样,如果您确实将数据带回应用程序服务器,则最小化列和行将对您有所帮助。 确保查询被调整并适当地索引将有助于两种情况。
重新logging:
然后循环logging
循环logging在sql中几乎总是错误的 – 写一个基于集合的操作是首选。
作为一般规则 ,我宁愿将数据库的工作保持在最低限度“存储此数据,获取此数据” – 但是,总是有一些场景的示例,其中服务器上的优雅查询可以节省大量带宽。
还要考虑:如果这在计算上是昂贵的,它可以caching在什么地方?
如果你想要一个准确的 “哪个更好”; 对两种方式进行编码并进行比较(注意两者的初稿可能不会100%调整)。 但是,在典型的用法中要考虑到这一点:如果实际上一次被调用5次(单独),那么就模拟:不要只比较一个“1个1”。
让我用一个比喻:如果你想在巴黎买一条金项链 ,金匠可以坐在开普敦或巴黎,这是一个技巧和品味的问题。 但是,你永远不会把南非的黄金矿石运往法国。 矿石在采矿场(或至less在一般地区)加工,只有黄金被运送。 应用程序和数据库也应该如此。
就PostgreSQL而言,你可以在服务器上几乎做任何事情,相当有效率。 RDBMS擅长于复杂的查询。 对于程序需求,您可以select各种服务器端脚本语言 :tcl,python,perl等等。 不过,大多数情况下我使用PL / pgSQL 。
最糟糕的情况是重复地去一个更大的集合中的每一行的服务器。 (这会像运送一吨矿石一次。)
第二 ,如果你发送一个级联的查询,每个查询都依赖于之前的查询,而所有查询都可以在服务器上的一个查询或过程中完成。 (就像运送黄金,然后每艘珠宝依次分开运送)。
在应用程序和服务器之间来回是昂贵的。 对于服务器和客户端。 试着减less这种情况,你就会赢得胜利:在必要时使用服务器端程序和/或复杂的SQL。
我们刚刚完成了一个项目,将几乎所有复杂的查询都打包到存储过程中。 该应用程序交付参数,并获得所需的数据集。 快速,干净,简单(对于应用程序开发人员),I / O减less到最低限度…一个低碳足迹的shiny项链。
在这种情况下,在SQL中进行计算可能会稍微好一些,因为数据库引擎可能比Java有更高效的十进制算术例程。
一般来说,虽然行级计算没有太大的区别。
它有什么区别是:
- 像SUM(),AVG(),MIN(),MAX()这样的数据库引擎的聚合计算将比Java实现快一个数量级。
- 任何地方使用计算来过滤行。 在数据库中进行过滤要比读取一行然后丢弃更为有效。
数据访问逻辑的哪些部分应该在SQL中执行,哪些部分应该在您的应用程序中执行,没有黑/白。 我喜欢马克·格雷韦尔的措辞,区分
- 复杂的计算
- 数据密集型计算
SQL的强大function和performance力被严重低估了。 自引入窗函数以来,很多非严格的面向集合的计算可以在数据库中非常容易和优雅地执行。
不pipe总体应用程序架构如何,应始终遵循三条经验法则:
- 保持数据库和应用程序之间的数据传输量小(有利于计算DB中的数据)
- 保持由数据库slim加载的数据量(有利于让数据库优化语句以避免不必要的数据访问)
- 不要通过复杂的并发计算将数据库推到其CPU限制(有利于将数据拉入应用程序内存并在那里执行计算)
根据我的经验,用一个体面的DBA和一些体面的数据库的知识,你不会很快遇到你的数据库CPU限制。
进一步阅读这些东西的解释:
- Java开发人员在编写SQL时犯的10个常见错误
- Java开发人员在编写SQL时遇到的更多常见错误
一般情况下,如果有相同或其他项目中的其他模块或组件需要获取这些结果的机会,请在SQL中进行。 一个primefaces操作完成服务器端也更好,因为你只需要从任何数据库pipe理工具调用存储过程得到最终值,无需进一步处理。
在某些情况下,这不适用,但是当它确实有意义时。 一般来说,分贝箱也有最好的硬件和性能。
如果您正在编写ORM或编写临时的低性能应用程序,请使用任何模式简化应用程序。 如果您正在编写高性能应用程序并仔细考虑规模,您将通过将处理转移到数据来获胜。 我强烈主张将处理转移到数据上。
让我们分两步来考虑:(1)OLTP(less量logging)事务。 (2)OLAP(多条logging的长时间扫描)。
在OLTP的情况下,如果你想快速(每秒10k-100k事务),你必须从数据库中删除锁存,locking和死锁争用。 这意味着您需要消除交易中的长时间摊位:从客户到数据库的往返处理将处理移动到客户端就是这样一个长时间的摊位。 你不能有长期的交易(使读/更新primefaces),并具有非常高的吞吐量。
Re:水平缩放。 现代数据库横向扩展。 那些系统已经实现了HA和容错。 利用它并尝试简化您的应用程序空间。
我们来看一下OLAP–在这种情况下,显然拖拽可能的太字节数据到应用程序是一个可怕的想法。 这些系统专门针对压缩,预先组织的柱状数据进行高效运作。 现代的OLAP系统也可以水平扩展,并具有复杂的查询计划程序,可将工作水平分散(将处理内部移至数据)。
如果我们能够确定我们在业务实施中的目标,是否在前端或后端执行计算是非常重要的。 在那时候,java代码可能比写好的sql代码更好,反之亦然。 但是,如果混淆,你可以尝试确定第一 –
- 如果你可以通过数据库sql直接获得一些东西,那么最好使用db,因为db会更好,然后在那里进行计算,然后进行结果获取。 但是,如果实际的计算需要从这里和那里的东西太多的计算,那么你可以去应用程序代码。 为什么? 因为在大多数情况下,类似于循环的情况并不是最好的,所以前端语言更适合这些东西。
- 如果需要从许多地方进行类似的计算,那么显然将计算代码放在db端将会更好地保持在同一个地方。
- 如果有很多计算要通过许多不同的查询来达到最终结果,那么也要去db端,因为你可以在存储过程中放置相同的代码,比从后端获取结果更好,然后在前端计算它们结束。
在决定代码的放置位置之前,您可以考虑很多其他方面。 一种看法是完全错误的 – 一切都可以在Java(应用程序代码)中最好地完成,并且/或者一切都最好由db(sql代码)来完成。
形成一个性能的观点:这是一个非常简单的算术运算,几乎肯定可以比从数据库中的实际磁盘上获取数据快得多。 此外,在任何运行时,计算where子句中的值可能会非常快。 总之,瓶颈应该是磁盘IO,而不是数值的计算。
根据可读性,我认为如果使用ORM,应该在应用程序服务器环境中执行,因为ORM将使您可以使用基于集合的操作轻松处理底层数据。 如果你要编写原始的SQL,那么在那里做计算没有什么问题,如果格式正确的话,你的SQL也会看起来更好一点,也更容易阅读。
至关重要的是,“表演”没有被界定。
对我来说最重要的是开发者时间。
编写SQL查询。 如果太慢或数据库成为瓶颈,则重新考虑。 到那个时候,你将能够对两种方法进行基准testing,并根据与你的设置相关的实际数据(硬件和你所在的任何堆栈)做出决定。
如果没有具体的例子和基准,我不相信性能差异是可以推论的,但是我还有另外一个观点:
你可以保持更好的? 例如,您可能希望将前端从Java切换到Flash,或HTML5或C ++或其他。 大量的程序已经经历了这样的变化,或者甚至以多种语言开始,因为它们需要在多个设备上工作。
即使你有一个适当的中间层(从给出的例子来看,情况似乎并非如此),该层可能会改变,JBoss可能会变成Ruby / Rails。
另一方面,你不太可能将SQL后端replace为不是SQL关系数据库的东西,即使你这样做了,你也必须从头开始重写前端,所以这一点是没有意义的。
我的想法是,如果您在数据库中进行计算,稍后编写第二个前端或中间层会容易得多,因为您不必重新实现所有内容。 然而,在实践中,我认为“在哪里可以做到这一点,人们会理解的代码”是最重要的因素。
为了简化如何回答这个问题,可以看看负载均衡。 你想把负荷放在最有能力的地方(如果有意义的话)。 在大多数系统中,SQL服务器很快就成为瓶颈,所以可能的答案是你不希望SQL做更多的工作。
在大多数体系结构中,构成系统核心和外部系统的SQL服务器也是如此。
但是,上面的math是如此微不足道,除非你把你的系统推到极限,否则最好的地方就是你想要的地方。 如果math不是微不足道的,比如计算sin / cos / tan来计算距离,那么这个努力可能变得不平凡,需要仔细的计划和testing。
这个问题的其他答案是有趣的。 令人惊讶的是,没有人回答你的问题。 你想知道:
- 在查询中投入分数是否更好? 我不认为投给美分会在你的查询中添加任何东西。
- 在查询中使用now()会更好吗? 我宁愿将date传递到查询中,而不是在查询中计算它们。
更多信息:对于问题一你想确保聚合的分数工作,没有舍入误差。 我认为数字19,2对于金钱是合理的,在第二种情况下,整数是可以的。 因为这个原因,使用浮动金钱是错误的。
对于第二个问题,作为一个程序员,我喜欢完全控制什么date被认为是“现在”。 当使用像now()这样的函数时,编写自动unit testing可能很困难。 另外,当你有一个更长的事务脚本时,可以设置一个等于now()的variables并使用该variables,这样所有的逻辑使用完全相同的值。