我正在编写一个PHP脚本,它只是使用下面的查询
从MYSQL表中返回数据"SELECT * FROM data where status='0' limit 1";
在读取数据后,我通过使用以下查询获取特定行的Id来更新状态
"Update data set status='1' WHERE id=" . $db_field['id'];
对一个客户来说,事情是有益的。现在我愿意为多个客户创建这个特定的页面。有超过20个客户端将在几乎相同的时间连续访问同一页面(24/7)。两个或更多客户端是否有可能从表中读取相同的数据?如果是,那么如何解决呢?
由于
答案 0 :(得分:1)
考虑并发性是正确的。除非你只有一个PHP线程响应客户端请求,否则实际上没有什么可以阻止它们从data
分发同一行进行处理 - 实际上,因为它们每个都会运行相同的查询,所以它们都会每个人几乎肯定会分出同一行。
解决该问题的最简单方法是锁定,如接受的答案所示。如果PHP服务器线程运行SELECT...FOR UPDATE
或LOCK TABLE ... UNLOCK TABLES
(非事务性)所花费的时间最小,这可能有效,这样其他线程可以在每个线程运行此代码时等待(它仍然是浪费,因为他们可能正在处理其他一些数据行,但稍后会更多。)
有一个更好的解决方案,但它需要更改架构。想象一下,你有一个这样的表:
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,同时要处理相同的记录。然后在闲暇时,你可以
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,处理状态等,以帮助验证数据是否真正完成了处理。
还有其他解决方案 - 消息队列可能是理想的,允许您直接(或通过php脚本)将每个未处理的数据对象的ID排队到客户端,然后为要检索的数据提供接口并单独标记处理从“下一个”数据的排队。但就“仅限mysql”的解决方案而言,我在这里展示的背后的概念应该能为你提供很好的服务。
答案 1 :(得分:0)
答案 2 :(得分:0)
我建议你使用session
来...
你可以将id
保存到会话中......
所以你可以检查如果一个客户端正在检查该记录,而不是允许其他客户端访问它 ...