PHP脚本中需要的逻辑帮助

时间:2014-03-03 04:49:17

标签: php mysql localhost

我正在编写一个PHP脚本,它只是使用下面的查询

从MYSQL表中返回数据
"SELECT * FROM data where status='0' limit 1";

在读取数据后,我通过使用以下查询获取特定行的Id来更新状态

"Update data set status='1' WHERE id=" . $db_field['id'];

对一个客户来说,事情是有益的。现在我愿意为多个客户创建这个特定的页面。有超过20个客户端将在几乎相同的时间连续访问同一页面(24/7)。两个或更多客户端是否有可能从表中读取相同的数据?如果是,那么如何解决呢?

由于

3 个答案:

答案 0 :(得分:1)

考虑并发性是正确的。除非你只有一个PHP线程响应客户端请求,否则实际上没有什么可以阻止它们从data分发同一行进行处理 - 实际上,因为它们每个都会运行相同的查询,所以它们都会每个人几乎肯定会分出同一行。

解决该问题的最简单方法是锁定,如接受的答案所示。如果PHP服务器线程运行SELECT...FOR UPDATELOCK 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)

您寻求的答案可能是使用交易。我建议你阅读以下帖子及其接受的答案:

PHP + MySQL transactions examples

如果没有,你应该看一下表锁定:

13.3.5 LOCK TABLES and UNLOCK TABLES

答案 2 :(得分:0)

我建议你使用session来... 你可以将id保存到会话中...... 所以你可以检查如果一个客户端正在检查该记录,而不是允许其他客户端访问它 ...