并发数据库访问

时间:2014-10-21 18:03:48

标签: php mysql transactions webserver

当Web服务器收到我的PHP脚本请求时,我假设服务器创建了一个运行脚本的专用进程。 如果在脚本退出之前,对同一个脚本发出另一个请求,另一个进程启动 - 我是否正确,或者第二个请求将在服务器中排队等待第一个请求退出? (问题1)

如果前者是正确的,即同一个脚本可以在不同的进程中同时运行,那么他们将尝试访问我的数据库。

当我在脚本中连接数据库时:

$DB = mysqli_connect("localhost", ...);

查询它,进行或多或少冗长的计算并更新它,我不希望数据库的内容被正在运行的脚本的另一个实例修改。

问题2:这是否意味着自从连接到数据库直到关闭它:

mysqli_close($DB);

是否阻止数据库访问其他软件组件?如果是这样,它会有效地防止脚本实例同时运行。

更新: @OllieJones亲切地解释说数据库没有被阻止。

让我们考虑以下情况。第一个进程中的脚本在Users表中发现符合条件的用户,并开始准备要在Counter表中为该用户追加的数据。此时,另一个进程中的脚本抢占并删除Users表中的用户和Counter表中的关联数据;然后它被第一个脚本抢占,该脚本不再为用户写入数据。这些数据处于头部分离状态,即无法访问。

如何防止此类争用?

2 个答案:

答案 0 :(得分:2)

在现代Web服务器中,有一个处理来自用户请求的进程池(或可能是线程)。对同一脚本的并发请求可以并发运行。每个请求处理程序都有自己的与DBMS的连接(它们实际上是在池中维护的,但这是另一天的故事)。

当各个请求处理程序正在使用它时,数据库被阻止,除非您通过锁定表或执行SELECT ... FOR UPDATE之类的请求来显式阻止它。有关这个深层主题的更多信息,请阅读有关交易的信息。

因此,编写数据库查询的方式非常重要,这样他们就不会互相干扰。例如,如果您需要在插入行后立即了解自动递增列的值,则应使用LAST_INSERT_ID()mysqli_insert_id()而不是尝试查询数据库:另一个用户可能有在此期间插入另一行。

扩大网站的系统测试规则通常涉及严格的负载测试,以消除所有这些并发性。

如果您正在对特定实体进行大量工作,在您的情况下是用户,则使用交易。

首先你做

 BEGIN

开始交易。然后你做

  SELECT whatever FROM User WHERE user_id = <<whatever>> FOR UPDATE

选择用户并将该用户的行标记为忙于更新。然后,您需要完成所有工作,以填写与该用户相关的各种表中的各行。

最后你做了

 COMMIT

如果你把事搞砸了,或者不想进行改变,你就

ROLLBACK

并且所有更改都将恢复到SELECT ... FOR UPDATE之前的状态。

为什么这样做?因为如果另一个客户端执行相同的SELECT .... FOR UPDATE,MySQL会延迟该请求,直到第一个请求获得COMMITROLLBACK

如果另一个客户端使用不同的用户ID,则操作可以同时进行。

您需要使用InnoDB访问方法来使用事务:MyISAM不支持它们。

答案 1 :(得分:0)

可以同时进行多次读取,如果有写入操作,则会阻止所有其他操作。读取将阻止所有写入。