给定以下 InnoDB 表:
event_stream
+ id BIGINT PRIMARY AUTO-INCREMENT
+ event TEXT
考虑到有多个客户端同时向这个流中插入事件:我们可以使用什么技术来让多个监听器/消费者以增量方式处理这个事件流?
(编辑)我希望有多个客户端 连接到这个流,它们可以对新事件做出一次 react 并跟踪它们在流中的位置。
注意事项:
- 不使用 MySQL 来存储事件不是一种选择;
- 锁定整个表是 Not Acceptable ;
- 我想控制一个事件是否已经由消费者决定,这个表可能有多个消费者;
- 可以创建新列;
- 此表将增长到数亿个事件;
最佳答案
“不要排队,直接去做。”我发现数据库的排队机制很差。如果“工作”线程执行任务的时间不长,则让队列简单地执行任务;这消除了队列的开销,从而可能使系统更快并更好地扩展。
“数以亿计的事件”——而且几乎所有事件都已“处理”?这表明您有两个表——一个用于已处理的事件,一个用于等待处理的事件。后者很少有超过几十行??在这种情况下,处理效果会更好。
有两个额外的列:哪个 worker 拥有该进程,以及 worker 何时捕获它。时间到了,您可以处理这样的情况(是的,最终会发生)一个 worker 捕获了一个任务,然后死了——从而使任务成为孤立的。作为单独的工作可以“收割”这些孤儿。
单个 SQL UPDATE
可以获取表中的一行。单独在事务中执行此操作,不在流程中的任何事务中执行此操作。同样,在其自己的事务中“释放”任务。
抓取是这样的(autocommit=ON
):
UPDATE ToDo SET who = $me, when = NOW()
WHERE who IS NULL
LIMIT 1; -- grab one
SELECT ... FROM ToDo WHERE who = $me; -- get details on the task
“发布”可能涉及两个表,如下所示:
BEGIN;
$stuff = SELECT ... FROM ToDo WHERE who = $me;
DELETE FROM ToDo WHERE who = $me;
INSERT ... INTO History ... VALUES (most of stuff from $stuff);
COMMIT;
在抓取和释放之间,您有足够长的时间来执行“任务”。您不会被 InnoDB 超时等问题绊倒。
如果您想提供有关您的队列和任务的更多详细信息,我可能会进一步完善。
我所描述的内容应该可以处理任意数量的插入器、任意数量的工作人员和持续任意时间长度的任务。
AUTO_INCREMENT
对于遍历事件列表不可靠。 INSERT
是多个步骤:
- 开始交易
- 获取下一个 auto_incr id
- 插入
- COMMIT -- 只有现在其他人才能看到新的 id
COMMIT 相对于 auto_incr 可能“乱序”(尤其是在复制中)。
关于mysql - 如何在 MySQL 上创建事件流,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47465040/