Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/mysql/67.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在mysql中实现消息队列表的最佳方式是什么_Mysql_Job Queue - Fatal编程技术网

在mysql中实现消息队列表的最佳方式是什么

在mysql中实现消息队列表的最佳方式是什么,mysql,job-queue,Mysql,Job Queue,这可能是我第十次实现这样的东西了,我从来没有对我提出的解决方案100%满意过 使用mysql表而不是适当的消息传递系统之所以吸引人,主要是因为大多数应用程序已经使用一些关系数据库来处理其他事情,而我所做的大部分事情都是使用mysql,而很少有应用程序使用消息传递系统。此外,关系数据库具有非常强的ACID属性,而消息传递系统通常没有 第一个想法是使用: create table jobs( id auto_increment not null primary key, message te

这可能是我第十次实现这样的东西了,我从来没有对我提出的解决方案100%满意过

使用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 出列情况如下所示:

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 桌子和排队看起来不错,但排队让我有点烦。它回滚的可能性有多大?还是被封锁?我应该用什么键来让它变小

或者有没有比我现在做的更好的解决方案?

我建议使用

它有SQL Server、Oracle、MySql、SQLite和Firebird的提供商。

Brian Aker刚才谈到过。也有人讨论过从DELETE语法中选择表

如果您不担心吞吐量,那么可以始终将其用作互斥对象。例如:

SELECT GET_LOCK('READQUEUE');
SELECT * FROM jobs;
DELETE FROM JOBS WHERE ID = ?;
SELECT RELEASE_LOCK('READQUEUE');
如果你真的想让它变得更有趣,可以把它包装在一个存储过程中。

有应该可以映射的设计信息

引述:

以下是我过去成功使用的:

MsgQueue表模式

MsgId标识-不为空 MsgTypeCode varchar20-不为空 SourceCode varchar20-插入消息的进程-可为空 状态char1-'N'ew if queued'A'ctive if processing'C'completed,default'N'-非空 CreateTime datetime-默认GETDATE-不为空 Msg varchar255-可为空

您的消息类型正是您所期望的——例如,在某些情况下,使用XML或您选择的其他JSON表示形式构建的、符合进程插入和进程读取之间约定的消息会很方便

然后可以插入0-to-n进程,0-to-n进程可以读取和处理消息,每个读取进程通常处理一种消息类型。一个进程类型的多个实例可以运行以实现负载平衡

读卡器提取一条消息,并在处理该消息时将其状态更改为“活动”。完成后,它会将状态更改为“完成”。它可以删除或不删除邮件,具体取决于您是否希望保留审核跟踪。State='N'的消息按MsgType/Timestamp顺序提取,因此在MsgType+State+CreateTime上有一个索引

变化: 说明错误。 读卡器进程代码的列。 状态转换的时间戳


这提供了一个很好的、可伸缩的、可见的、简单的机制,可以完成您所描述的许多事情。如果您对数据库有基本的了解,那么它是非常简单和可扩展的。由于原子状态转换事务,锁回滚等问题从来没有出现过。

我已经构建了一些消息队列系统,我不确定您指的是哪种类型的消息,但在退出队列的情况下,这是一个词吗?我做了和你一样的事。你的方法看起来简单、干净、可靠。并不是说我的工作是最好的,但它被证明对许多站点的大型监控非常有效。错误日志、大量电子邮件营销活动、社交网络通知


我的投票:不用担心

您的出列可以更简洁。与依赖事务回滚不同,您可以在一个原子语句中执行此操作,而无需显式事务:

UPDATE jobs SET process_id = ? WHERE process_id IS NULL ORDER BY ID ASC LIMIT 1;
然后,根据您的具体情况,您可以选择括号[]表示可选的作业:

SELECT * FROM jobs WHERE process_id = ? [ORDER BY ID LIMIT 1];

下面是我使用的一个解决方案,在没有当前线程的进程id的情况下工作,或者锁定表

SELECT * from jobs ORDER BY ID ASC LIMIT 0,1;
在$row数组中获取结果,然后执行:

DELETE from jobs WHERE ID=$row['ID'];
然后获取受影响的行mysql\u受影响的行。如果存在受影响的行,请在$row数组中处理作业。如果有0个受影响的行,则表示其他进程已在处理所选作业。重复上述步骤,直到没有行为止

我已经用一个有10万行的“jobs”表测试了这一点,并生成了20个执行上述操作的并发进程。没有比赛条件发生。您可以修改上述查询,以使用处理标志更新行,并在实际处理后删除该行:

while(time()-$startTime<$timeout)
{
SELECT * from jobs WHERE processing is NULL ORDER BY ID ASC LIMIT 0,1;
if (count($row)==0) break;
UPDATE jobs set processing=1 WHERE ID=$row['ID'];
if (mysql_affected_rows==0) continue;
//process your job here
DELETE from jobs WHERE ID=$row['ID'];
}

不用说,对于此类工作,您应该使用适当的消息队列ActiveMQ、RabbitMQ等。不过我们不得不求助于这个解决方案,因为我们的主机在更新软件时经常会破坏东西,所以破坏的东西越少越好

您可以使用一个中间表来维护队列的偏移量

create table scan(
  scan_id int primary key,
  offset_id int
);
您可能还有多个扫描正在进行,因此每个扫描有一个偏移量。在扫描开始时初始化偏移量_id=0

begin;
select * from jobs where order by id where id > (select offset_id from scan where scan_id = 0)  asc limit 1;
update scan set offset_id = ? where scan_id = ?; -- whatever i just got
commit;
您所需要做的只是保持最后的偏移量。这也将为您节省大量空间p
每个记录的进程id。希望这听起来合乎逻辑。

在MySQL 8中,您可以使用新的NOWAIT和SKIP LOCKED关键字,通过特殊的锁定机制避免复杂性:

启动交易; 选择id,来自作业的消息 其中进程id为空 按id订购ASC限制1 对于更新跳过锁定; 更新作业 设置进程id=? 其中id=?; 犯罪 传统上,如果没有黑客攻击、不寻常的特殊表或列、不可靠的解决方案或丢失并发性,这是很难实现的

跳过锁定可能会导致大量使用者出现性能问题

但是,这仍然不能处理事务回滚时自动标记作业完成的问题。为此,您可能需要保存点数。然而,这可能无法解决所有情况。您可能真的希望设置一个在事务失败时执行的操作,但作为事务的一部分


将来可能会有更多的功能来帮助优化案例,例如更新也可以返回匹配的行。在更改日志中随时了解新特性和功能是很重要的。

我会尝试这样做:更新作业集process\u id=id\u arg其中id=SELECT MINid FROM process\u id为NULL的作业从process\u id=id\u arg的作业中选择字段您的出列设计受到竞争条件的影响。您需要将选择转换为选择。。。对于更新,您需要按照@pawstrong的建议首先执行更新,因为更新是原子的。在RabbitMQ中,如果工作进程和代理之间的连接断开,则任务将重复。如何使用MySQL实现这一点,我们是否应该添加一个字段:ping并在任务执行期间更新它?理论上,挂起的任务可能会有最大超时时间,但是如果您有长时间运行的任务,那么几个小时的任务不会立即重复。Hangfire.io也是一个很好的选择。非常好!如何使用PostgreSQL执行相同的操作?该方法存在性能或并发性问题。你可能会注意到,也可能不会注意到。例如,如果正在保存日志行,您可能没有意识到作业是否会时不时地被处理两次。该方法也可能存在死锁。它使用乐观锁定,所以它会退回到使用版本控制和冲突解决。两个并发查询可以获得相同的作业,而一个则会死锁或在更新时发生冲突,这也取决于一致性级别。或者,进程id被阻塞。它在很多时候都能工作,并且在某些实现中也能工作,但并非总是如此。