我正在做一个项目,在该项目中,我应该至少生成70000个包含8个字母数字字符的代码。密码必须唯一。目前,我正在使用php通过以下功能生成这些代码:
function random_unique_serial($length, PDO $conn) { $codeCheck=FALSE; while (!$codeCheck) { $characters = '0123456789abcdefghijklmnopqrstuvwxyz'; $charactersLength = strlen($characters); $randomCode = ''; for ($i = 0; $i < $length; $i++) { $randomCode .= $characters[rand(0, $charactersLength - 1)]; } $sql = "SELECT * FROM codes WHERE code=:code"; $st = $conn->prepare($sql); $st->bindvalue(":code", $randomCode, PDO::PARAM_STR); $st->execute(); $count = $st->rowcount(); if ($count==0) { $codeCheck=TRUE; } else { $codeCheck=FALSE; } } return $randomCode; }
如您所见,此代码检查数据库中是否生成了每个代码,以确保它不是重复的。这在理论上应该起作用。但是,这非常慢,并且导致请求超时。我尝试增加执行时间,但这也无济于事。
然后,我决定使用数据库端方法并使用此解决方案: 使用MySQL生成随机且唯一的8个字符串
这也非常慢,并且某些生成的代码长度少于8个字符。
您能否提出更好的解决方案?
创建表结构:
CREATE TABLE t (code CHAR(8) UNIQUE CHARACTER SET ascii COLLATE ascii_general_ci NOT NULL UNIQUE);
定义一个PHP函数以生成一个随机字符串:
function random_string(integer $length = 8): string { return bin2hex(mcrypt_create_iv(ceil($length/2), MCRYPT_DEV_URANDOM)); }
使用PHP构建一个多值INSERT语句,将其插入数据库,计算插入的数目,然后重复进行直到插入所需的数目:
function insert_records(\PDO $pdo, integer $need = 70000): null { $have = 0; while ($have < $need) { // generate multi value INSERT $sql = 'INSERT IGNORE INTO t VALUES '; for ($i = 1; $i < $need; $i++) { $sql .= sprintf('("%s"),', random_string()); } $sql .= sprintf('("%s");', random_string()); // pass to database and ask how many records were inserted $result = $pdo->query($sql); $count = $result->rowCount(); // adjust bookkeeping values so we know how many we have and how many // we need $need -= $count; $have += $count; } }
在我的机器(Amazon Linux c2.small)上,70k条记录的运行时间约为2秒:
real 0m2.136s user 0m1.256s sys 0m0.212s
为了使代码更快,此代码中的相关技巧为:
发送生成所需记录数所需的最少数量的SQL语句。使用多值插入- INSERT INTO ... VALUES (), (), ... ();
确实有帮助,因为它最大程度地减少了MySQL必须执行的语句处理总量并且可以告诉我们插入了多少条记录而无需执行其他查询。
使用 INSERT IGNORE
以避免检查的每一个代码我们插入,这是真的很昂贵的存在。
使用最快的字符串生成功能,我们可以满足我们的需求。在我的经验中,mcrypt_create_iv
是一种具有加密安全性的快速生成器,因此可以在安全性和性能之间实现理想的平衡。
使用ASCII字符集和固定宽度CHAR
来消除不必要的字节开销并UNIQUE
强制执行重复数据删除。