【问题标题】:Track changes to SQL query in real time实时跟踪 SQL 查询的更改
【发布时间】:2010-10-31 21:06:25
【问题描述】:

这是我的情况:

  1. 用户 A 必须监控 sql 查询 Q 的结果。
  2. 用户B可以修改可能改变Q结果的数据
  3. 在整个数据库上运行 Q 很慢

我想要的是:每当用户 B 修改数据时: 使用创建、更新或删除,然后生成 Q 结果的增量。 即返回(修改列表、创建列表、删除列表)三元组。

因此,对 Q 的更新会快得多。

实现细节:SQL Server 2005,带有 NHibernate 层。目前,用户 A 以轮询模式运行 Q,每 10 秒一次。

我的一个想法是创建数据库模式的内存副本,当用户 B 进行更改,然后也在内存副本中进行此更改,然后运行 ​​Q 在这个内存中的副本上。

有更好的方法吗?

后记:感谢所有详细的 cmets。我决定按如下方式实现此功能:

  1. 使用 NHibernate PostUpdate 事件在数据库发生更改时获取通知
  2. 对内存中的更新集合运行 LINQ to Object 查询
  3. 对于系统中注册的每个 SQL 查询,将结果列表存储在 Redis 服务器上的内存中(不持久化到磁盘)
  4. 如果 LINQ 查询表明更新与给定查询相关,即更改结果集,则更新 Redis 上的列表
  5. 使用某种类型的通知(例如 Redis Pub/Sub)来通知用户给定查询的更改
  6. 在 Redis 上更新列表时,传入 sql server 更新时间戳。 Redis 端需要一些代码来解决基于时间戳的冲突。如果以较低的时间戳传入更改,则忽略。

【问题讨论】:

  • 你希望用这个做什么?
  • @OMG Ponies:我需要在我的数据库前面有一个快速缓存,以加快查询速度。另外,想让它增量,这样我就不必对整个数据库重复查询。

标签: sql performance sql-server-2005 nhibernate


【解决方案1】:

好的,没有办法做到这一点,而不需要通过大量的过度/轮询查询来访问服务器。很简单,因为 SQL 不是像我这样的人所知道的“股票行情”。它不在那里分发实时更新。 SQL 几乎是“问你就被告知”。所以“让我实时更新”意味着问“我们到了吗?我们到了吗?我们到了吗?”一直以来,就像怪物史莱克 2 中的驴一样。不过,SQL Server 是一个更好的 rShrek,并且很乐意一直回答“还没有”,但它会导致负载 - 或导致延迟。

大多数人在做你所做的事情是在数据库之外进行的。基本上通过处理更新的应用程序服务器运行,并在将更新写入数据库时​​分发更新信息。有些人以相当高的性能做到这一点 - 我有一个从数据库获得的提要,该数据库在安静时间内处理每秒约 250.000 次更新 - 峰值约为每秒 600.000 次更新。所有这些都是对已知行数(约 150 万行)的更新。 udpate 分布在全球范围内,不会导致数据库负载 - 因为分发是在应用程序层的数据库之外完成的。实时。哦,这里的“最小延迟”是“尽可能快”。

使用 SQL Server 无法像您希望的那样快速高效地分发结果。您总是以一种或另一种方式进入您的方式的查询语义结束。

提供的答案仍然正确 - 但仅在小容量场景或显着延迟(0.5 秒以上)可行的场景中可行(抱歉,在我的世界中,对于 SLOW 系统的实时处理时间少于 5 毫秒)。

您要寻找的是许多人都有的标准问题……与财务数据有关。实时交易系统需要快速和实时的信息,而拉取数据库是不可行的。如果您想要一个可扩展的解决方案(或需要一个),请在该领域寻找方法。

【讨论】:

  • 谢谢,汤姆汤姆。我 am 试图避免为此任务访问数据库。有很多方法可以解决这个问题;一个不错的方法是使用 LINQ 在内存中查询更新。然后,如果更新相关,我可以从服务器向用户 B 发送消息。
【解决方案2】:

在 SQL Server 2005 中,他们引入了Service Broker。您可以将其用作队列。您从触发器发送消息(通过将记录添加到表中),另一个进程调用存储过程,该过程设置为等待队列中的消息(表中的记录)。在添加此消息之前,存储过程将在那里等待(我认为有一个超时设置 - 但我认为还有一个无限选项)。

In this blog post,海报展示了一个使用触发器中的 Service Broker 来提高性能的示例。

【讨论】:

  • 谢谢,加布里埃尔。触发器和服务代理肯定会解决这个问题。然而,我意识到我的记忆已经发生了变化; NHibernate 可以在更新数据库后发送更新后事件。然后,我可以在内存中对更新运行 LINQ 查询,并将增量发送给查询结果因更新而发生更改的用户。
【解决方案3】:

一种方法是优化轮询查询。确保表中有一个列可以跟踪上次更新的时间。然后创建一个以UpdateDt 开头的索引。那么,轮询查询就可以做到:

select  *
from    TheBigTable
where   UpdateDt > 'LastTimeIChecked'

这将非常快,因为 UpdateDt 上有一个索引。它仍然是一个相当简单的代码/设置。

附:如果重要的是哪个用户进行了更改,您还可以将UpdatedBy 添加到表和索引中。

【讨论】:

  • 谢谢,安多玛。这肯定会加快速度。由于我有点速度恶魔,我想完全避免使用数据库,并在内存中工作。而且,由于我有一个 NHibernate 层,我已经知道更新是什么,而无需访问数据库。
【解决方案4】:

用户 B 多久更新一次数据?最好不要让 A 轮询更改,而是让 B 向 A 发出一些事件信号,表明数据已更改。 À 可以简单地等待这个事件。 该事件可以保存您想要的任何数据。也许正是 B 对数据所做的更改。 这样可以节省轮询导致的负载,并且通过这种方式 A 经历的延迟最小。

【讨论】:

  • 谢谢,H den Breejan。是的,我绝对想摆脱民意调查。
【解决方案5】:

您可以在 sql 数据库旁边使用诸如最近更改列表之类的内容。更改列表类似于 RSS 提要。当 rss 阅读器下载博客的 feedurl 时,Feed 第一次加载所有文章,之后只加载新文章。所描述的应用程序的 rss-feed 将与原始 rss-feed 略有不同,因为它不仅包含新文章,还包含对文章的更改和已删除的文章。为了优化性能,可以按一定间隔(秒或毫秒)读取 rss-feed,并按一定间隔(在更改批量大小或时间间隔后更新文件)写入 rss-feed。当在 UI 中一次显示大量数据时,可能需要使用 sql 查询进行第一次显示,然后轮询 rss-feed 以获得可接受的性能。在使用 sql-query 和 rss-feed 读取和更新 UI 时,可以将 feed 的更改次数限制为最大更改次数。 rss-feed 可以使用消息队列或 esb 来实现,但您也可以使用不同的方式。提要中的数据可以是 xml、json、二进制序列化对象或 UI 中最容易使用的任何内容。提要可以存储在某种缓存中,直接存储在 Ram 内存中或静态文件中。您可能必须在数据库中添加一个版本号列,并在每次更改时增加该数字,以便可以决定在第一次加载时从数据库中读取什么以及需要从 rss-feed 处理哪些更改。

阅读:

  1. 使用 Sql-query 加载初始数据
  2. 等待时间间隔过去
  3. 阅读带有更改的 rss-feed
  4. 使用新更改更新 UI
  5. 转到 2

写作

  1. 开始交易
  2. 将更改写入 sql 数据库
  3. 将更改写入 rss-feed
  4. 提交事务

将第 3 步写入消息队列并异步处理写入可能是可以接受的。

“实时”显示更改的一个主要问题是实时并不意味着零秒,而是“用户可以解释更改的时间”,对于多人第一人称游戏来说,这在 0.05 秒之间射击游戏和 30 分钟或更长时间的经理要求的报告,由秘书打印出来,并在几个电话后由经理阅读。

【讨论】:

  • 谢谢,帕科。对我来说,实时可能是每 10 秒更新一次。
  • RSS 类比很有趣,但我认为这种情况有点不同,因为重要的是我要获得最新的更改:如果用户 A 在时间 t1 更新了提要,并且用户B 在时间 t2 更新,其中 t1
  • 我在桌子上使用了一个int版本
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-01-18
  • 1970-01-01
  • 1970-01-01
  • 2021-09-06
  • 2017-09-05
  • 1970-01-01
相关资源
最近更新 更多