这可能是我第十次实施这样的事情,而且我从未对我提出的解决方案百分百满意.
使用mysql表而不是"正确"的消息传递系统的原因很有吸引力,主要是因为大多数应用程序已经将一些关系数据库用于其他东西(对于我一直在做的大多数事情,它往往是mysql),而很少有应用程序使用消息传递系统.此外 - 关系数据库具有非常强大的ACID属性,而消息传递系统通常没有.
第一个想法是使用:
create table jobs( id auto_increment not null primary key, message text not null, process_id varbinary(255) null default null, key jobs_key(process_id) );
然后入队看起来像这样:
insert into jobs(message) values('blah blah');
出列似乎是这样的:
begin; select * from jobs where process_id is null order by id asc limit 1; update jobs set process_id = ? where id = ?; -- whatever i just got commit; -- return (id, message) to application, cleanup after done
表和入队看起来不错,但出队有点困扰我.回滚的可能性有多大?还是被封锁了?我应该用什么键来制作O(1)-ish?
或者,我正在做什么更好的解决方案?
paw*_*ong 26
你的出队可能更简洁.您可以在没有显式事务的情况下在一个原子语句中执行此操作,而不是依赖于事务回滚:
UPDATE jobs SET process_id = ? WHERE process_id IS NULL ORDER BY ID ASC LIMIT 1;
Run Code Online (Sandbox Code Playgroud)
然后你可以用(括号[]表示可选,取决于你的具体情况)拉取作业:
SELECT * FROM jobs WHERE process_id = ? [ORDER BY ID LIMIT 1];
Run Code Online (Sandbox Code Playgroud)
我已经构建了一些消息排队系统,我不确定你指的是什么类型的消息,但是在出列的情况下(是一个单词吗?)我做了同样的事情你已经完成了.您的方法看起来简单,干净,坚固.并不是说我的工作是最好的,但事实证明它对许多网站的大型监控非常有效.(错误记录,大量电子邮件营销活动,社交网络通知)
我的投票:不用担心!
Brian Aker刚才谈到了一个队列引擎.一直在谈论SELECT table FROM DELETE语法.
如果您不担心吞吐量,可以始终使用SELECT GET_LOCK()作为互斥锁.例如:
SELECT GET_LOCK('READQUEUE');
SELECT * FROM jobs;
DELETE FROM JOBS WHERE ID = ?;
SELECT RELEASE_LOCK('READQUEUE');
Run Code Online (Sandbox Code Playgroud)
如果你想变得非常花哨,请将其包装在存储过程中.
在 MySQL 8 中,您可以使用 newNOWAIT和SKIP LOCKED关键字来避免特殊锁定机制的复杂性:
START TRANSACTION;
SELECT id, message FROM jobs
WHERE process_id IS NULL
ORDER BY id ASC LIMIT 1
FOR UPDATE SKIP LOCKED;
UPDATE jobs
SET process_id = ?
WHERE id = ?;
COMMIT;
Run Code Online (Sandbox Code Playgroud)
传统上,如果没有黑客和不寻常的特殊表或列、不可靠的解决方案或失去并发性,这是很难实现的。
SKIP LOCKED可能会导致大量消费者出现性能问题。
然而,这仍然不能处理在事务回滚时自动标记作业完成的情况。为此,您可能需要保存点。然而,这可能并不能解决所有情况。您确实希望设置一个在事务失败时执行的操作,但作为事务的一部分!
将来可能会有更多功能来帮助优化某些情况,例如也可以返回匹配行的更新。在变更日志中随时了解新特性和功能非常重要。
| 归档时间: |
|
| 查看次数: |
21471 次 |
| 最近记录: |