生成非重复数字,警惕数据库
Generate Non-Repeating Numbers, Wary of Database
我希望根据 MySQL 数据库生成一个没有重复的随机数。我如何更改以下函数,以便它检查数据库是否生成的数字已经存在,如果不存在,则将它们插入 table。还有,said table 应该怎么排?我不是最好的设计行使它们尽可能小(和实用)。
函数:
function genNonRepNum($min, $max, $quantity) {
$numbers = range($min, $max);
shuffle($numbers);
return array_slice($numbers, 0, $quantity);
}
使用函数:
print_r(genNonRepNum(1000, 10000, 3));
returns:
Array ( [0] => 8586 [1] => 9666 [2] => 8169 )
太棒了,但我只希望它检查数据库以查看它是否存在,如果不存在则将其插入。提前致谢。
我认为这不是您应该采用的方法,只需使用 php openssl-random-pseudo-bytes 即可为您提供一串随机字节(任何加密安全的随机 generator/hash 函数可以创建不可预测的安全 ID 就可以了)-这将提供肯定不同的密钥(或者理论上极不可能相同)或者如果您想要一个不错的数字,请使用 auto-increment 并获得 lastinsert_id由您的 driver 提供,例如:
mysqli_insert_id()
PDO::lastInsertId()
查询执行后并在您的软件中使用它
手动检查是否插入并重试既昂贵又糟糕,应该避免
请尽量减少访问数据库的次数!!!!!!!
首先,通常你不会这样做 - 你可以设计一个足够大的数字,碰撞的概率可以忽略不计,然后使用强大的随机生成器来生成那个大小的随机数。
否则,因为无论如何您都必须存储数字(除非您实施某种深奥的加密方案——例如,您可以存储一个计数器并使用 AES 对其进行加密;当然数字不会是真正的 "random",但它们也不容易预测),并且你必须在开始时定义随机范围,你可以创建一个 table 已经插入的随机数:填充一个 table带有序列号,然后插入到另一个具有唯一的 auto_increment 键排序的 RAND().
现在,当您需要一个随机数时,您可以获取 table 的第 i 个元素并读取其 value
列,然后递增 i 以确保您不会重复使用该数字。如果两个进程都需要一个随机数,您将需要使用锁定和事务。
这样还有一个好处就是可以提前知道号码池什么时候会用完;届时您可以插入新号码。这将稍微不那么随机(前百万个数字将在 0-999999 范围内随机,第二个百万个数字将在 1000000-1999999 范围内随机),但也许它足以满足您的目的。
或者您可以使用两个 UNIQUE 列
创建随机 table
CREATE TABLE randompool (
id integer not null primary key auto_increment,
value integer
);
CREATE UNIQUE INDEX randompool_uniq ON randompool(value);
并使用辅助进程来检查全局变量何时保存在某处,NEXT_ID,据说在 COUNT(*) FROM randompool
的 10% 以内 - 这意味着随机池的容量下降到 10% - 并且,如果是,生成一些随机数并尝试插入它们
INSERT IGNORE INTO randompool (value) VALUES (?),(?),(?),...
当然,randompool
越大,这个操作的效率就越低。当randompool
包含2亿个数字时,生成一个随机的正符号32位数字将有10%的概率重复并被拒绝,因此插入1000个新随机数的成本将比开始时按比例增加;还考虑到索引重复查找将花费更多。与其说更多,不如说更多。但是如果进程是独立的并且在系统负载不太大的时候运行,这很可能不是问题。
随机数的选取还是会用NEXT_ID计数器直接从table中取出来,这样读出来的数字会很便宜。
此处的解决方案是对自动递增的数字使用加密。作为我的意思的一个例子,想象一下你有一个加密算法,它接受 8 位,一个密钥并吐出 8 位加密数据。如果您使用相同的密钥并加密 0 到 255 之间的值,您将获得 0 到 255 之间的所有值作为输出,但顺序不同。您无法获得任何重复项,因为根据定义,加密是可逆的,这意味着两个不同的值无法使用相同的密钥和算法加密为相同的值,因为您无法对其进行解密。由于雪崩效应等加密特性,数字序列会显得随机。所以基本上,您只需要根据您的质量与速度需求使用您选择的算法,使用密钥加密一个自动递增的数字。这就是生成新信用卡号的方式,保证卡号尚未发行。有关更多信息,请查看 "format preserving encryption".
我希望根据 MySQL 数据库生成一个没有重复的随机数。我如何更改以下函数,以便它检查数据库是否生成的数字已经存在,如果不存在,则将它们插入 table。还有,said table 应该怎么排?我不是最好的设计行使它们尽可能小(和实用)。
函数:
function genNonRepNum($min, $max, $quantity) {
$numbers = range($min, $max);
shuffle($numbers);
return array_slice($numbers, 0, $quantity);
}
使用函数:
print_r(genNonRepNum(1000, 10000, 3));
returns:
Array ( [0] => 8586 [1] => 9666 [2] => 8169 )
太棒了,但我只希望它检查数据库以查看它是否存在,如果不存在则将其插入。提前致谢。
我认为这不是您应该采用的方法,只需使用 php openssl-random-pseudo-bytes 即可为您提供一串随机字节(任何加密安全的随机 generator/hash 函数可以创建不可预测的安全 ID 就可以了)-这将提供肯定不同的密钥(或者理论上极不可能相同)或者如果您想要一个不错的数字,请使用 auto-increment 并获得 lastinsert_id由您的 driver 提供,例如:
mysqli_insert_id()
PDO::lastInsertId()
查询执行后并在您的软件中使用它
手动检查是否插入并重试既昂贵又糟糕,应该避免
请尽量减少访问数据库的次数!!!!!!!
首先,通常你不会这样做 - 你可以设计一个足够大的数字,碰撞的概率可以忽略不计,然后使用强大的随机生成器来生成那个大小的随机数。
否则,因为无论如何您都必须存储数字(除非您实施某种深奥的加密方案——例如,您可以存储一个计数器并使用 AES 对其进行加密;当然数字不会是真正的 "random",但它们也不容易预测),并且你必须在开始时定义随机范围,你可以创建一个 table 已经插入的随机数:填充一个 table带有序列号,然后插入到另一个具有唯一的 auto_increment 键排序的 RAND().
现在,当您需要一个随机数时,您可以获取 table 的第 i 个元素并读取其 value
列,然后递增 i 以确保您不会重复使用该数字。如果两个进程都需要一个随机数,您将需要使用锁定和事务。
这样还有一个好处就是可以提前知道号码池什么时候会用完;届时您可以插入新号码。这将稍微不那么随机(前百万个数字将在 0-999999 范围内随机,第二个百万个数字将在 1000000-1999999 范围内随机),但也许它足以满足您的目的。
或者您可以使用两个 UNIQUE 列
创建随机 tableCREATE TABLE randompool (
id integer not null primary key auto_increment,
value integer
);
CREATE UNIQUE INDEX randompool_uniq ON randompool(value);
并使用辅助进程来检查全局变量何时保存在某处,NEXT_ID,据说在 COUNT(*) FROM randompool
的 10% 以内 - 这意味着随机池的容量下降到 10% - 并且,如果是,生成一些随机数并尝试插入它们
INSERT IGNORE INTO randompool (value) VALUES (?),(?),(?),...
当然,randompool
越大,这个操作的效率就越低。当randompool
包含2亿个数字时,生成一个随机的正符号32位数字将有10%的概率重复并被拒绝,因此插入1000个新随机数的成本将比开始时按比例增加;还考虑到索引重复查找将花费更多。与其说更多,不如说更多。但是如果进程是独立的并且在系统负载不太大的时候运行,这很可能不是问题。
随机数的选取还是会用NEXT_ID计数器直接从table中取出来,这样读出来的数字会很便宜。
此处的解决方案是对自动递增的数字使用加密。作为我的意思的一个例子,想象一下你有一个加密算法,它接受 8 位,一个密钥并吐出 8 位加密数据。如果您使用相同的密钥并加密 0 到 255 之间的值,您将获得 0 到 255 之间的所有值作为输出,但顺序不同。您无法获得任何重复项,因为根据定义,加密是可逆的,这意味着两个不同的值无法使用相同的密钥和算法加密为相同的值,因为您无法对其进行解密。由于雪崩效应等加密特性,数字序列会显得随机。所以基本上,您只需要根据您的质量与速度需求使用您选择的算法,使用密钥加密一个自动递增的数字。这就是生成新信用卡号的方式,保证卡号尚未发行。有关更多信息,请查看 "format preserving encryption".