当Web服务器收到我的PHP脚本请求时,我假设服务器创建了一个运行脚本的专用进程。 如果在脚本退出之前,对同一个脚本发出另一个请求,另一个进程启动 - 我是否正确,或者第二个请求将在服务器中排队等待第一个请求退出? (问题1)
如果前者是正确的,即同一个脚本可以在不同的进程中同时运行,那么他们将尝试访问我的数据库。
当我在脚本中连接数据库时:
$DB = mysqli_connect("localhost", ...);
查询它,进行或多或少冗长的计算并更新它,我不希望数据库的内容被正在运行的脚本的另一个实例修改。
问题2:这是否意味着自从连接到数据库直到关闭它:
mysqli_close($DB);
是否阻止数据库访问其他软件组件?如果是这样,它会有效地防止脚本实例同时运行。
更新: @OllieJones亲切地解释说数据库没有被阻止。
让我们考虑以下情况。第一个进程中的脚本在Users表中发现符合条件的用户,并开始准备要在Counter表中为该用户追加的数据。此时,另一个进程中的脚本抢占并删除Users表中的用户和Counter表中的关联数据;然后它被第一个脚本抢占,该脚本不再为用户写入数据。这些数据处于头部分离状态,即无法访问。
如何防止此类争用?
答案 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会延迟该请求,直到第一个请求获得COMMIT
或ROLLBACK
。
如果另一个客户端使用不同的用户ID,则操作可以同时进行。
您需要使用InnoDB访问方法来使用事务:MyISAM不支持它们。
答案 1 :(得分:0)
可以同时进行多次读取,如果有写入操作,则会阻止所有其他操作。读取将阻止所有写入。