【发布时间】:2021-03-27 22:34:35
【问题描述】:
我编写了一个简单的系统来收集有关我网站中用户的数据。
对于每个新用户,我的客户端代码 (Javascript) 会创建一个唯一 ID,将其存储在 cookie 中,然后使用此唯一 ID 标识符将所有数据发送到服务器。
在服务器上,我有以下 PHP 代码检查此用户是否存在于 USERS 表中,如果存在,它将使用该用户 ID 进行其他数据插入,如果不存在,它将创建一个新用户:
$stmt = $conn->prepare("SELECT User_ID FROM USERS WHERE Unique_ID=:uid");
$stmt->bindValue(':uid', $_GET['unique_id'], PDO::PARAM_STR);
$stmt->execute();
$row = $stmt->fetch();
if ($row) {
///// This user exists in USERS Table
$User_ID = $row['User_ID'];
}
else
{
/////INSERT a new user to USERS table with this Unique_ID
}
在实时站点上运行此程序后,有时我会在 USERS 表中获得多个具有相同 Unique_ID 的条目。
我进一步检查,这些重复的用户行是用不到 1 秒的设备写入的。所以我认为当客户端的浏览器一次发送 2 个数据请求时会发生这种情况(这对我的应用程序逻辑来说是正常且典型的)。
我尝试将 USERS 表引擎从 INNODB 更改为 MISAM - 强制执行表锁定而不是行锁定 - 它没有帮助。
为了防止重复值,我知道我可以向 Unique_ID 字段添加唯一索引或执行“INSERT IGNORE ON DUPLICATE” - 但是当我需要时,这些选项不会返回正确的用户行。
知道我应该在这里做什么吗?
谢谢!
【问题讨论】:
-
但是当我需要时,这些选项不会返回正确的用户行。 INSERT 永远不会返回行。这与插入的原始值或重复值无关。
-
这并不奇怪,你的 php 脚本在同一时间有 2 个调用,并且都失败了初始
SELECT并运行else块。它可能不会经常发生,但它会在某个时候发生 -
您可以在 3 个地方处理它:客户端(间隔您的请求)、php 端(信号量/监视器或调度)和数据库端(唯一索引或事务/存储过程)。 IMO(并且根据 cmets)最好和最简单的是唯一索引,但是您需要稍微更改 php 脚本
-
这个想法是
select,如果没有找到用户:insert ignore,如果插入有效:获取lastInsertID,否则再次select(1 查询如果一切顺利,3 用于最差的情况)。或者在脚本开头总是insert ignore,然后是select(总是2个查询,即使用户已经存在) -
如果您更改您的 php 代码以遵循我上次评论的两个“算法”之一,您永远不会遇到
$User_ID未定义的情况。 (编辑:抱歉,我不能聊天,我得走了:)
标签: php mysql sql-insert innodb myisam