我目前正在从事一个项目,其中对API的访问仅限于注册用户。API 本身已经完成并按预期工作。限制对 API 的访问也变得相当简单。但是,我的问题(或问题(是如何确保注册,验证和/或丢失和查找过程的数据库交互的效率。
以下是当前发生的情况的示例:
- 用户通过输入其电子邮件地址来请求 API 密钥
- 向用户发送验证电子邮件
- 用户单击电子邮件中的链接,php 根据数据库检查哈希
- 验证哈希后,将生成、存储并通过电子邮件发送 API 密钥
- 如果用户忘记/丢失了API密钥,可以再次通过电子邮件发送
- 如果未收到验证电子邮件,可以再次通过电子邮件发送
下面是数据库结构的示例: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 个不同页面:第一页用于注册/请求,第二页用于验证电子邮件,第 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.'&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.'&hash='.$hash.'&active=0">[Resend Verification Email]</a>';
}
}
我的主要问题是:对于这样一个(看似(简单的函数,有这么多的查询,这会产生比它解决的更多的问题吗?出于显而易见的原因,我真的需要确保没有任何重复的验证哈希或 API 密钥。但是,估计有 50k 人使用此功能,这是否会由于 SQL 查询量而使服务器陷入困境?主要关注点是由于 while(( 循环用于在插入之前检查生成内容的唯一性。
我知道这不是幕后发生的事情的完整图片,但它确实提供了有关其余页面如何工作的线索。如果需要有关整个流程的更多信息,我很乐意发布它。
感谢您提供的任何见解!
解决此问题的一种方法是不检查重复项,而只是确保它们永远不会发生。 因此,只需对用户表进行版本控制(为版本添加一个字段(。 这将只是一个 int,每当用户的行被更改时都会前进。
然后,当您生成随机密钥时,在存储密钥之前附加user_id并user_version到它。
例:
11ap0w9jfoaiwej203989wesef
其中第一个 1 是user_id,第二个 1 是用户版本。
然后,即使一个大键生成两次的统计概率很小,它也将始终是唯一的,因为您的用户 ID 将是唯一的。
我会考虑使用 UUID 而不是滚动您自己的随机字符串。出于所有实际目的,这将是一个独特的价值。
http://dev.mysql.com/doc/refman/5.5/en/miscellaneous-functions.html#function_uuid