【问题标题】:Is it better to update or increment a value when persisting to a database?持久化到数据库时更新或增加值会更好吗?
【发布时间】:2009-08-13 12:07:22
【问题描述】:

我有一个应用程序,用户在其中执行操作并获得积分。在应用程序中执行算术并使用结果值更新点数据库字段,或者让数据库进行数学运算会更好吗?

假设一个积分为 0 的用户额外获得 2 分:

//app does the math (0+2) and issues this statement
update users set points = 2 where id = 1

//app only knows to update by 2, db does the math
update users set points = points+2 where id = 1

在 SQL 性能方面有什么不同吗?就应用程序设计、此逻辑应驻留在何处等而言,一种方法是否比另一种方法更好?

编辑:此编辑可能为时已晚,无法起到很好的作用,但我只是想回应一些反馈并做出澄清。虽然我对任何 db 性能差异感到好奇,但这不是一个关注。我关心的是这种逻辑在哪里最适合,为什么一个应该比另一个更受青睐,以及在哪些情况下。

一方面,我几乎所有的逻辑都驻留在应用程序中,因此在那里进行数学运算是一致的,like Hank's answer。但另一方面,BlixtAndrew 提出的一些潜在的延迟/线程问题可能表明逻辑应该由数据库执行。

【问题讨论】:

  • 我认为这可能是主观和争论的,虽然我有兴趣看到人们提出的案例

标签: .net sql sql-server


【解决方案1】:

这两种操作是有区别的。第一个说:

用户应该有两点;不多不少。

第二个说:

除了他/她已经拥有的积分之外,用户还应该再获得两分。

我会避免将这种数据逻辑放在业务逻辑层中。业务逻辑应该是“用户得到两分”,它应该告诉数据库这一点。它不应该自己动手,说“数据库告诉我用户有两点,所以现在他们有四点!”如果业务层和数据库之间存在延迟,或者在多个线程中同时进行大量更新,这将是危险的。

我意识到我实际上并没有以明文形式列出我喜欢的选择:
在业务逻辑中确定用户应该获得多少分。然后发出一条语句,告诉数据库将用户的分数增加这些分数。通过这样做,您使数据库负责保持数据的一致性,这是一项只应属于数据库的任务。我的意思是他们就是这么做的,对吧?

UPDATE users SET points = points + ? WHERE user_id = ?;

您的业务逻辑层将简单地填补空白。

如果您正在做一个大型项目,您甚至可能需要考虑将其放入存储过程中,因为您将来可能会更改数据结构(例如将点拆分到另一个表等):

userChangePoints ?, ?

【讨论】:

  • 谢谢!这真的有助于我做出更清晰的决定。通过将这个任务强加给数据库,它将更好地确保数据的一致性,这最终应该是我最关心的问题。
【解决方案2】:

虽然在某种意义上它可以被视为过早的优化,但对于多线程/用户环境而言,两者之间的差异将非常关键,因为直接读取然后设置值允许两个事务干扰并导致其中之一他们可能会丢失。

使用 points=points+2 发送积分更新至少可以隔离这两个语句,但如果不同用户同时提交两次,结果将为 4。

这同样可能不适合情况/应用,但我认为您的选择应该考虑到您需要什么样的隔离/保护。

这是特定于应用程序的,用户可以登录两次等,但两者之间的主要区别仍然存在。

【讨论】:

  • "但是如果不同的用户同时提交两次,结果会是 4。" -- 一个很好的观点,但我认为如果有某种更新逻辑在任何用户触发的代码中运行并且不是由该用户的操作决定的,它应该放在它自己的更新线程中(也许由cron) 运行以避免这种情况。明文:如果用户点击“投票”,在用户交互中执行的代码中是可以的,但如果你有类似“每 10 分钟,给所有用户 1 分”之类的内容,put这在它自己的线程中。我从非常糟糕的经历中知道...
  • 这是很重要的一点。只有用户可以更改他或她的积分,但在未来,用户完全有可能同时在 2 个浏览器上执行一个操作(并且可能会访问不同的服务器),因此可能会产生一些干扰。
  • 同意 Blixt,因为解决方案可能从客户端应用程序中抽象出来,但最初的问题包括“我有一个用户执行操作并接收积分的应用程序”,所以我认为它是导致更新的 UI 驱动事件。
  • 我明白了。如果用户同时在多个客户端中执行操作,则不会出现问题(除非它应该只允许发生一次,但这就是数据库事务的来源。)如果客户端尝试,唯一的问题将出现做服务器逻辑(虽然这个也可以用事务来解决,但会很笨拙,效率也较低。)
【解决方案3】:

我认为这是“过早优化”或过度优化的情况。

答案是:没关系。尽一切可能使您的应用程序更简单、清晰和可维护。

添加:至于性能,我认为您不会注意到任何差异,除非您对表中的所有数据运行此更新。但是如果你必须知道,我相信第二种选择会稍微多一点,因为你首先需要读取值,然后在它的基础上计算,然后再写回来。因此,您可以使用第一个选项节省一次读取操作。

【讨论】:

  • 我认为在这种情况下应该完全忽略优化部分。虽然简单性、可读性和可维护性很重要,但更重要的是要认识到这两个语句在多线程环境中可能意味着不同的东西。
  • @New in town:点重新:优化,虽然这不是我最关心的问题。主要是我在寻求帮助,以确定哪个选项使我的应用程序更简单、清晰和可维护,但我自己无法确定。
【解决方案4】:

将添加项放在执行其余业务逻辑的任何位置。如果您的应用程序让数据库处理所有业务逻辑,请在 SQL 中进行数学运算。如果你的业务逻辑在应用层,把数学放在那里并发出一个普通的UPDATE

【讨论】:

    【解决方案5】:

    我会说和镇上的新人一样,不要太担心它。

    我会先写出有意义且更易于阅读的内容(所以是第一个),对两种方式进行分析,然后在需要时进行更改。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-03-26
      • 2011-04-22
      • 2017-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多