[英]Logical Help Needed in a PHP Script
我正在写一个小的PHP脚本,它只是使用下面的查询从MYSQL表返回数据
"SELECT * FROM data where status='0' limit 1";
读取数据后,我通过使用以下查询获取特定行的ID来更新状态
"Update data set status='1' WHERE id=" . $db_field['id'];
对于单个客户来说,一切都很好。 现在,我愿意为多个客户制作此特定页面。 超过20个客户端将几乎连续(24/7)在同一时间访问同一页面。 两个或多个客户端是否有可能从表中读取相同的数据? 如果是,那怎么解决呢?
谢谢
您可以考虑并发。 除非您只有1个PHP线程来响应客户端请求,否则实际上没有什么可以阻止它们各自从要处理的data
分发相同的行-实际上,由于它们每个都将运行相同的查询,因此它们几乎肯定会在同一排。
解决该问题的最简单方法是锁定,如公认的答案所示。 如果PHP服务器线程运行SELECT...FOR UPDATE
或LOCK TABLE ... UNLOCK TABLES
(非事务性)所需的时间极短,这样其他线程可以等待每个线程运行此代码,则这可能会起作用SELECT...FOR UPDATE
仍然很浪费,因为他们可能正在处理其他一些数据行,但稍后会再介绍)。
有一个更好的解决方案,尽管它需要更改架构。 假设您有一个这样的表:
CREATE TABLE `data` (
`data_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`data` blob,
`status` tinyint(1) DEFAULT '0',
PRIMARY KEY (`data_id`)
) ENGINE=InnoDB;
您没有任何方法来事务更新“下一个已处理的记录”,因为您唯一需要更新的字段是status
。 但是想象一下您的表看起来像这样:
CREATE TABLE `data` (
`data_id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`data` blob,
`status` tinyint(1) DEFAULT '0',
`processing_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`data_id`)
) ENGINE=InnoDB;
然后,您可以编写类似这样的查询来更新要使用“处理ID”处理的“下一个”列:
UPDATE data
SET processing_id = @unique_processing_id
WHERE processing_id IS NULL and status = 0 LIMIT 1;
任何值得一试的SQL引擎都将确保您没有2个不同的处理ID,这些ID表示要同时处理同一条记录。 然后,您可以在闲暇时
SELECT * FROM data WHERE processing_id = @unique_processing_id;
并且知道您每次都获得独特的记录。
这种方法也很好地解决了耐久性问题。 您基本上可以确定每个data
行的批处理运行,这意味着您可以考虑每个批处理作业,而在此之前,您可能只考虑数据行。
我可能会通过为该元数据添加第二张表来实现@unique_processing_id(自动递增键是真正的窍门,但可以添加其他数据处理元数据):
CREATE TABLE `data_processing` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`data_id` int(10) unsigned DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
并将其用作唯一ID的来源,您可能会得到如下结果:
INSERT INTO data_processing SET date=NOW();
SET @unique_processing_id = (SELECT LAST_INSERT_ID());
UPDATE data
SET processing_id = @unique_processing_id
WHERE status = 0 LIMIT 1;
UPDATE data
JOIN data_processing ON data_processing.id = data.processing_id
SET data_processing.data_id = data.data_id;
SELECT * from data WHERE processing_id = @unique_processing_id;
-- you are now ready to marshal the data to the client ... and ...
UPDATE data SET status = 1
WHERE status = 0
AND processing_id = @unique_processing_id
LIMIT 1;
这样就解决了并发性问题,并根据设置data_processing
表的方式使您处于更好的状态来审核持久性; 您可以跟踪线程ID,处理状态等,以帮助验证数据是否确实已在处理中。
还有其他解决方案-消息队列可能是理想的选择,允许您将每个未处理的数据对象的ID直接(或通过php脚本)排队到客户端,然后提供一个接口,供该数据的检索和标记处理与排队分开“下一个”数据。 但是就“仅mysql”解决方案而言,我在这里向您展示的内容背后的概念应该可以很好地为您服务。
我建议您为此使用session
...您可以将该id
保存到会话中...以便可以检查一个客户端是否正在检查该记录,而不是不允许另一个客户端访问它 ...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.