Php MySQL事务与用于用户注册的表锁

Php MySQL事务与用于用户注册的表锁,php,mysqli,transactions,locking,user-registration,Php,Mysqli,Transactions,Locking,User Registration,我是MySQL的新手,我读过很多我在这里和网上发现的聊天后事务和表锁定,所以我认为我的问题不应该是多余的 我正在尝试优化查询,主要用于用户注册和会话。 Web应用程序是用PHP/MySQL(i)编写的,我使用的是InnoDB引擎 我不使用$_SESSIONS来存储用户会话,但我使用DB上的一个表,在该表中存储有关用户会话的一些信息,这些信息通过cookie保持活动状态。此方法要求我为每个用户页面请求检查数据库上的用户会话。这样做的同时,我还加入了select会话查询中的“users”表,以便每次

我是MySQL的新手,我读过很多我在这里和网上发现的聊天后事务和表锁定,所以我认为我的问题不应该是多余的

我正在尝试优化查询,主要用于用户注册和会话。 Web应用程序是用PHP/MySQL(i)编写的,我使用的是InnoDB引擎

我不使用$_SESSIONS来存储用户会话,但我使用DB上的一个表,在该表中存储有关用户会话的一些信息,这些信息通过cookie保持活动状态。此方法要求我为每个用户页面请求检查数据库上的用户会话。这样做的同时,我还加入了select会话查询中的“users”表,以便每次都获取新的用户信息

关于用户注册查询,我不确定哪种方法更好:性能、代码质量安全性,以避免多个用户同时注册同一用户名/电子邮件时出现问题

用户表格:

user_id (primary, AI)
username (unique)
email (unique)
password
field 1
field 2
...
如果查询失败,因为用户名或电子邮件已被占用,我需要知道哪个已被占用

方法A:锁定用户表

LOCK TABLES users WRITE;
// users provided username and email are already taken?
SELECT COUNT(username) username, (SELECT COUNT(email) FROM users WHERE email = 'batman@heroes.com') email FROM users WHERE username = 'batman'

$res = fetch_array; 
if($res['username'] == 0 && $res['email'] == 0){
   INSERT INTO users (username,email,password) VALUES ('batman','batman@heroes.com','imtheonlytruehero')

   if(affected_rows == 1)
      $username = null;
      $email= null;
      $registration = true;
   }
   else {
      $username = null;
      $email= null;
      $registration = false;
   }
}
else {
   $username = ($res['username'] > 0) ? false : true;
   $email = ($res['email'] > 0) ? false : true;
   $registration = false;
}    
UNLOCK TABLES;
START TRANSACTION;
INSERT INTO users (username,email,password) VALUES ('batman','0','imtheonlytruehero');

if(affected_rows > 0) {
   $username = true;
   UPDATE users SET email='batman@heroes.com' WHERE username='batman';
   if(affected_rows > 0) {
      $email = true;
      $registration = true;
      COMMIT;
   }
   else {
      $email = false;
      $registration = true;
      ROLLBACK;
   }
}
else {
   $username = false;
   $email = null;
   $registration = false;
   ROLLBACK;
}
方法B:插入+更新的交易

LOCK TABLES users WRITE;
// users provided username and email are already taken?
SELECT COUNT(username) username, (SELECT COUNT(email) FROM users WHERE email = 'batman@heroes.com') email FROM users WHERE username = 'batman'

$res = fetch_array; 
if($res['username'] == 0 && $res['email'] == 0){
   INSERT INTO users (username,email,password) VALUES ('batman','batman@heroes.com','imtheonlytruehero')

   if(affected_rows == 1)
      $username = null;
      $email= null;
      $registration = true;
   }
   else {
      $username = null;
      $email= null;
      $registration = false;
   }
}
else {
   $username = ($res['username'] > 0) ? false : true;
   $email = ($res['email'] > 0) ? false : true;
   $registration = false;
}    
UNLOCK TABLES;
START TRANSACTION;
INSERT INTO users (username,email,password) VALUES ('batman','0','imtheonlytruehero');

if(affected_rows > 0) {
   $username = true;
   UPDATE users SET email='batman@heroes.com' WHERE username='batman';
   if(affected_rows > 0) {
      $email = true;
      $registration = true;
      COMMIT;
   }
   else {
      $email = false;
      $registration = true;
      ROLLBACK;
   }
}
else {
   $username = false;
   $email = null;
   $registration = false;
   ROLLBACK;
}
使用方法A时,我关心的问题是:当新用户正在注册且其查询正在锁定users表时,当尝试验证其会话以进行导航(记住“会话检查”也包括join users表)时,其他已登录用户(假设他们很多)会发生什么情况?缓慢?暂停?或者查询(选择和插入)是否足够轻,不会影响性能

方法B产生死锁的几率高吗

我能做什么?选择其中一种方法?混合它们?把一切都毁了然后重新开始


谢谢你的帮助。谢谢。

锁定整个表只能在例外情况下使用,方法A会妨碍并发。 假设用户名和电子邮件都有唯一的索引 方法B可以改写为下面提供的示例

这种方法应该适用于mysql和postgresql(伪代码)中的读取提交可重复读取


锁定整个表只应在例外情况下使用,方法A会妨碍并发性。 假设用户名和电子邮件都有唯一的索引 方法B可以改写为下面提供的示例

这种方法应该适用于mysql和postgresql(伪代码)中的读取提交可重复读取


如有可能,应避免使用锁台。对于方法A没有理由锁定任何内容或使用事务

这就是处理方法A的方法(注意,如果插入用户数据,应使用准备好的语句)


如有可能,应避免使用锁台。对于方法A没有理由锁定任何内容或使用事务

这就是处理方法A的方法(注意,如果插入用户数据,应使用准备好的语句)


你真正应该做的是在用户名和电子邮件上放一个唯一的索引。这样,您就不可能有任何副本,数据库会为您处理这些副本。事务不会阻止重复的值,锁定可能会起作用,但可能会导致在锁定表时无法执行任何操作的问题,这对性能不利。一个唯一的索引可以防止重复的行,并且可以作为异常被捕获。谢谢你的回答。我已经提供了在用户名和电子邮件列上设置唯一索引的方法,如本文开头所述。但我的疑问是,如果我只是做了插入,并且用户使用了表中已经设置的用户名和电子邮件,我无法告诉他这两者是否都被使用,因为mysql错误报告只出现了第一个重复键,所以在这种情况下,我不知道电子邮件是否也已被使用,使用户尝试另一次插入,以了解电子邮件也已被接收。你知道有什么方法来面对这个“问题”吗?非常感谢你!首先查询以查看数据库中是否已经存在任何一个。然后你可以告诉他们他们正在使用。唯一索引只是为了确保没有重复项。没有别的了。您应该事先进行检查,使其更易于用户使用,而不是依赖数据库索引来捕获错误。他们是一个安全网。你真正应该做的是在用户名和电子邮件上放一个唯一的索引。这样,您就不可能有任何副本,数据库会为您处理这些副本。事务不会阻止重复的值,锁定可能会起作用,但可能会导致在锁定表时无法执行任何操作的问题,这对性能不利。一个唯一的索引可以防止重复的行,并且可以作为异常被捕获。谢谢你的回答。我已经提供了在用户名和电子邮件列上设置唯一索引的方法,如本文开头所述。但我的疑问是,如果我只是做了插入,并且用户使用了表中已经设置的用户名和电子邮件,我无法告诉他这两者是否都被使用,因为mysql错误报告只出现了第一个重复键,所以在这种情况下,我不知道电子邮件是否也已被使用,使用户尝试另一次插入,以了解电子邮件也已被接收。你知道有什么方法来面对这个“问题”吗?非常感谢你!首先查询以查看数据库中是否已经存在任何一个。然后你可以告诉他们他们正在使用。唯一索引只是为了确保没有重复项。没有别的了。您应该事先进行检查,使其更易于用户使用,而不是依赖数据库索引来捕获错误。它们是一张安全网。