我有一个下表(伪DDL):
CREATE TABLE MESSAGE (
MESSAGE_GUID GUID PRIMARY KEY,
INSERT_TIME DATETIME
)
CREATE INDEX MESSAGE_IE1 ON MESSAGE (INSERT_TIME);
多个客户端同时在该表中插入行,可能每秒多次。我需要设计一个“Monitor”应用程序,它将:
可能有多个监视器同时运行。所有监视器都需要查看所有行(即插入行时,所有当前正在运行的监视器都必须“检测到”。
这个应用程序最初是为Oracle开发的,但是我们需要让它可以移植到每个主要的RDBMS,并且希望避免尽可能多的数据库特定的东西。
天真的解决方案是简单地在步骤1中选择的行中找到最大的INSERT_TIME然后......
SELECT * FROM MESSAGE WHERE INSERT_TIME >= :max_insert_time_from_previous_select
...在第2步中。
然而,我担心这可能导致竞争条件。请考虑以下情形:
因此,事务A插入的行永远不会被提取。
任何想法如何设计客户端SQL甚至更改数据库模式(只要它可以轻松移植),这样可以避免这些类型的并发问题,同时仍能保持良好的性能?
感谢。
答案 0 :(得分:2)
不使用任何特定于平台的变更数据捕获(CDC)技术,有几种方法。
选项1
每个 Monitor 都会注册MESSAGE
表的一种订阅。写入消息的代码然后每 Monitor 写一次MESSAGE
,即
CREATE TABLE message_subscription (
message_subscription_id NUMBER PRIMARY KEY,
message_id RAW(32) NOT NULLL,
monitor_id NUMBER NOT NULL,
CONSTRAINT uk_message_sub UNIQUE (message_id, monitor_id)
);
INSERT INTO message_subscription
SELECT message_subscription_seq.nextval,
sys_guid,
monitor_id
FROM monitor_subscribers;
每个 Monitor 然后在处理后从其订阅中删除该消息。
选项2
每个 Monitor 维护一个已处理的最近消息的缓存,该缓存至少与运行时间最长的事务一样长。例如,如果 Monitor 维护了它在过去5分钟内处理过的消息的缓存,它将在MESSAGE
表中查询所有消息晚于LAST_MONITOR_TIME
的消息。然后, Monitor 将负责注意它已选择的某些行已被处理。 Monitor 只会处理不在其缓存中的MESSAGE_ID
值。
选项3
就像选项1一样,您为每个 Monitor 设置订阅,但您使用一些排队技术将消息传递到 Monitor 。这比其他两个选项的可移植性差,但大多数数据库可以通过某种类型的队列向应用程序传递消息(例如,如果 Monitor 是Java应用程序,则为JMS队列)。通过构建自己的队列表,您可以避免重新发明轮子,并在应用程序层中为您提供标准接口以进行编码。
答案 1 :(得分:0)
您需要能够识别自上次检查后添加的所有行(即监视器检查)。您有一个“插入时间”列。但是,当您拼写出来时,插入列的时间不能与“大于[最后检查]”逻辑一起使用,以可靠地识别随后插入的新项目。提交不会以(初始)插入的顺序出现。我不知道任何适用于所有主要RDBMS的东西,它们会在提交的实际时间清楚安全地应用这样的“as of”标记。 [ 而不是“大于最后检查”算法。
这会导致过滤。插入后,项目(行)被标记为“尚未检查”;当montior登录时,它会读取所有尚未检查的项目,返回该组,并将标志翻转为“已检查”(如果有多个监视器,则必须在其自己的事务中完成)。监视器的查询必须读取所有数据并挑选尚未检查的数据。然而,这意味着这将是一组相当小的数据,至少相对于整个数据集。从这里,我看到两个可能的选择:
答案 2 :(得分:0)
您应该将Oracle AQ与多用户队列一起使用。
这是特定于Oracle的,但您可以创建存储过程的抽象层(或者如果您愿意,可以使用Java抽象),这样您就可以使用通用API来排队新消息,并让每个订阅者(监视器)将任何待处理消息排队。在该API的背后,对于Oracle,您使用AQ。
我不确定是否有其他数据库的排队解决方案。
我认为您无法提出满足您要求的完全数据库不可知方法。您可以扩展上面包含“已检查”列的示例,以使第二个表名为monitor_checked,每个监视器每个消息包含一行。这基本上就是AQ在幕后所做的事情,所以它重新发挥了作用。
答案 3 :(得分:0)
使用PostgreSQL,使用PgQ。它为您准备了所有这些小细节。
我怀疑你会找到一个强大且可管理的数据库无关解决方案。