在插入数据库之前检查项的唯一性

时间:2012-05-03 20:14:11

标签: php mysql performance

我目前正在开展一个项目,在该项目中,只能向注册用户访问API。 API本身已经完成并按预期工作。限制对API的访问也相当简单。但是,我的问题(或者问题是)如何确保注册,验证和/或丢失和找到过程的数据库交互效率。

以下是目前发生的一个例子:

  1. 用户通过输入电子邮件地址
  2. 来请求API密钥
  3. 向用户发送验证邮件
  4. 用户点击电子邮件中的链接,php检查数据库哈希
  5. 验证哈希后,将生成,存储和通过电子邮件发送API密钥
  6. 如果用户忘记/丢失API密钥,可以再次通过电子邮件发送
  7. 如果未收到验证电子邮件,则可以再次通过电子邮件发送
  8. 以下是数据库结构的示例: http://s13.postimage.org/h8ao5oo2v/dbstructure.png

    正如您可能想象的那样,在此过程中的每个特定步骤后面都会有很多数据库交互。我想知道效率的一个步骤是检查某些项目的唯一性。显然,我们不希望任何重复的API密钥浮动,我们也不希望任何重复的电子邮件验证哈希值。

    所以,我编写了一个简单的死函数,在将数据库插入数据库之前检查数据库。然而,这个项目的规模比我以前任何时候都要大几百倍。我之前已经建立并维护了为500到1,000名用户提供服务的项目......但是这个项目估计每天至少为50,000名用户提供服务。我很高兴我终于找到了一个大型项目,但在它的规模上变得越来越沮丧。

    无论如何,这里是我写的与数据库交互的函数,用于在存储项目之前检查项目的唯一性。

    function isUnique($table, $col, $data) {
      mysql_connect("localhost", "root", "") or die(mysql_error());  
      mysql_select_db("api") or die(mysql_error());
      $check = mysql_query("SELECT ".$col." FROM ".$table." WHERE ".$col."='".$data."'");
      $match = mysql_num_rows($check);
      if($match < 1) {
        return true;
      }
      return false;
      mysql_close('localhost');
    }
    

    此函数与另一个函数结合使用,该函数仅生成电子邮件验证哈希的随机40位数字0-9,a-z和A-Z字符串以及API密钥本身。 (以下功能)

    function makeRandom($length = 40) {
      $characters = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
      $randomString = '';
      for($i = 0; $i < $length; $i++) {
        $randomString .= $characters[mt_rand(0, strlen($characters) - 1)];
      }
      return $randomString;
    }
    

    然后,这两个功能的组合用于与API密钥发布相关的3个不同页面:第1页用于注册/请求,第2页用于验证电子邮件,第3页用于丢失密钥或未接收的电子邮件。现在,它在实践中:

    $hash   = makeRandom();
    $unique = isUnique('users', 'hash', $hash);
    if($unique == false) {
      while($unique == false) {
        $hash   = makeRandom();
        $unique = isUnique('users', 'hash', $hash);
      }
    }
    else {
      $searchactive   = mysql_query("SELECT email, active FROM users WHERE email='".$email."' AND active='1'") or die(mysql_error());
      $matchactive    = mysql_num_rows($searchactive);
      $searchinactive = mysql_query("SELECT email, active FROM users WHERE email='".$email."' AND active='0'") or die(mysql_error());
      $matchinactive  = mysql_num_rows($searchinactive);
    
      if($matchactive > 0) {
        $hash = mysql_query("SELECT hash FROM users WHERE email='".$email."' AND active='1'") or die(mysql_error());
        $hash = mysql_fetch_assoc($hash);
        $hash = $hash['hash'];
        $msg = 'The email address you entered is already associated with an active API key. <a href="lost.php?email='.$email.'&amp;hash='.$hash.'&active=1">[Recover Lost API Key]</a>';
      }
      elseif($matchinactive > 0) {
        $hash = mysql_query("SELECT hash FROM users WHERE email='".$email."' AND active='0'") or die(mysql_error());
        $hash = mysql_fetch_assoc($hash);
        $hash = $hash['hash'];
        $msg = 'The email address you entered is already pending verification. <a href="lost.php?email='.$email.'&amp;hash='.$hash.'&active=0">[Resend Verification Email]</a>';
      }
    }
    

    我的主要问题是:有了这么多查询,只是针对这样一个(看似简单的)简单函数,这会产生比解决的问题更多的问题吗?出于显而易见的原因,我确实需要确保没有任何重复的验证哈希值或API密钥。但是,估计有5万人使用此功能,由于SQL查询的数量,这是否会使服务器陷入困境?主要问题是由于while()循环用于在插入之前检查生成内容的唯一性。

    我知道这不是幕后发生的事情的完整画面,但它确实提供了其余页面如何工作的线索。如果需要有关整个过程的更多信息,我将很乐意发布它。

    感谢您提供的任何见解!

2 个答案:

答案 0 :(得分:2)

解决这个问题的一种方法是不检查重复项,但只是确保它们从未发生过。因此,只需为您的用户表版本添加(为版本添加一个字段)。这只是一个int,它会在用户的行被更改时随时前进。

然后,当您生成随机密钥时,在存储密钥之前将user_id和user_version附加到其中。

示例:

11ap0w9jfoaiwej203989wesef

第一个是user_id,第二个是用户版本。

然后,即使在两次生成大密钥的统计上很小的可能性,它也将始终是唯一的,因为您的用户ID将是唯一的。

答案 1 :(得分:1)

我会考虑使用UUID而不是滚动自己的随机字符串。出于所有实际目的,这将是一个独特的价值。

http://dev.mysql.com/doc/refman/5.5/en/miscellaneous-functions.html#function_uuid

相关问题