我需要在数据库中存储1000个(可能很快就会有100,000个甚至数百万个)12个字符的独特随机字符串。每次我必须生成一个新代码(实际上是以10,000 + s批量完成)我需要将它与现有数据库进行比较以确保不会有重复 - 但是当代码被用户“兑换”时,我需要确保它存在。
这两项任务都可能非常缓慢,所以我希望尽可能简化它们。首先,我确保字符串以BINARY格式存储在DB上,并带有INDEX。这显然比CHAR,VARCHAR和VARBINARY更快。
我正在考虑尝试进一步改进,我提出了这个简单的想法:将第一个字符存储为索引列中的TINYINT,并首先进行比较 - 因此希望能够更快地找到匹配的记录。
例如:
public function getFirstCharAsNum($code) {
$firstChar = substr($code, 0);
$firstCharHex = bin2hex($firstChar);
$prefix = hexdec($firstCharHex);
return $prefix;
}
public function isDuplicate($generatedCode) {
$result = false;
$params["code"] = $generatedCode;
$params["prefix"] = getFirstCharAsNum($generatedCode);
$STH = $this->_db->prepare("SELECT count(*) FROM codes
WHERE prefix = :prefix AND code = :code;");
try {
$result = $STH->execute($params);
} catch (PDOException $e) {
throw new Exception($e->getMessage());
}
$result = $STH->fetch(PDO::FETCH_COLUMN);
if($result) {
return true;
} else {
return false;
}
}
这个想法是它只会在找到匹配项时尝试AND操作的第二部分,并且搜索TINYINTs应该比整个BINARY(12)列快得多。
这实际上更快吗?或者是添加额外的查找会让我慢下来?
感谢。
答案 0 :(得分:1)
如果你这样做,那么代码生成将随着时间逐渐减慢,需要搜索更大的数据库,以及更大数据集上的更多冲突。
您可以使用预先生成的随机代码来准备表格。然后,记住Codes
表中的偏移量。无论何时需要新代码,只需从代码表中获取 offset - 行并将 offset 增加1;这当然需要原子地完成,READ LOCK
。
独立的线程可以在适当的时候生成随机代码(例如,每当系统负载足够低;晚上;等等),并INSERT IGNORE
将它们放入Codes
表中:
CREATE TABLE Codes (
offset INTEGER PRIMARY KEY NOT NULL AUTO_INCREMENT,
sequence BINARY(12)
);
要“生成”代码,您现在必须只执行一个在O(1)中执行的查询,因为它是一个固定地址的提取。如果将地址存储到代码偏移零中,可能会有两个查询:
LOCK TABLES test WRITE;
SELECT datum.sequence FROM Codes AS datum
JOIN Codes AS ndx ON ( datum.offset = ndx.sequence AND ndx.offset = 0 );
UPDATE Codes SET sequence = sequence + 1 WHERE offset = 0;
UNLOCK TABLES;
插入新代码的线程会遇到减速,但不是很多(它还会在LOCK TABLES LOW PRIORITY WRITE
s的每个块上使用INSERT
。但是,所有需要 new 代码的流程都会非常快速。
当然,“补货”主题将从COUNT(*)
表读取当前偏移量和Codes
,如果有超过给定数量的代码,则拒绝运行。
为此,我们可以添加一个“已兑换”的布尔列。要进一步提高速度,可以使用水平partitioning,将代码表划分为N个哈希分区。这样,不仅任何搜索都只能在一小部分数据上进行(不是比b树索引更好的改进......),但锁定和更新可以在表之间传播
您也可以“手动”并根据代码的第一个字母在不同服务器之间传播表格。这样,只要你提供足够的服务器,你就可以扩展到十亿个代码并且速度仍然很快。
答案 1 :(得分:1)
我需要在数据库中存储1000个(可能很快就会有100,000个甚至数百万个)12个字符的独特随机字符串
如果它们是真正随机的,则发生碰撞的可能性是{实际记录数} / {可能记录数}}
即使如果您从仅中选择的字符集包含数字,那么,有1000万条现有记录,发生碰撞的概率为10,000,000 / 1,000,000,000,000 = 1 / 100,000,因此,你所描述的实际上是浪费时间。在数据库中的值上添加唯一索引 - 如果您尝试添加新值时遇到唯一约束违规,则重新生成该值。
(有36个字符的曲目,碰撞的概率约为1 / 473,838,000,000)