auto_incrementを使用したPHPの短い一意のIDの生成?
-
22-07-2019 - |
質問
衝突をチェックせずに、短い一意のIDを生成したい。
私は現在このようなことをしていますが、現在生成しているIDはランダムであり、ループ内の衝突のチェックは面倒であり、レコードの数が大幅に増加するとコストが高くなります。
通常、衝突を心配することは問題ではありませんが、生成する一意のIDは、tinyurlのように、英数字の5〜8文字の一意の短い文字列です。
編集:5文字から始めて、6000万エントリに達したら、6。に進みます。などなど。
この目的のために、ユーザーから隠されているauto_increment値を使用し、代わりにMD5
または他の方法でそれらから一意の文字列を生成できると考えていました。
生成された文字列は線形に見えるべきではないので、auto_incremented IDをbase 36
[0-9A-Z]に変換するのは少し単純すぎますが、そのような機能がこれに対応しています。
編集:セキュリティは情報の保護に使用されないため、セキュリティは問題ではありません。これは、単に長い文字列へのショートカットです。 ありがとう。
ご提案いただきありがとうございます。遅れて申し訳ありません。歯科医..
解決
構築によって正しいもの、つまり置換関数が必要になります:これは、1つの整数(シーケンシャルカウンター)から別の整数への1対1の可逆マッピングを行う関数です。 いくつかの例(これらの任意の組み合わせも機能するはずです):
- ビットの一部を反転する(f.i. XORを使用、PHPで^)
- ビットの場所を入れ替える(($ i <!> amp; 0xc)<!> gt; <!> gt; 2 |($ i <!> amp; 0x3)<!> lt; <!> lt ; 2)、または単にすべてのビットの順序を逆にする
- 最大範囲を法とする定数値を追加します(これを上記のものと組み合わせる場合は、2倍にする必要があります)
例:この関数は、0、1、2、3、5、..を13、4、12、7、15、..に変換します(最大15):
$i=($input+97) & 0xf;
$result=((($i&0x1) << 3) + (($i&0xe) >> 1)) ^ 0x5;
編集
線形合同ジェネレーター(LCG、通常は乱数の生成に使用)を使用する簡単な方法は、次の形式の式で定義されます。
X_n+1 = (a * X_n + c) mod m
a、c、mの適切な値、X_0、X_1のシーケンス。 。X_m-1には、0〜m-1のすべての数値が1回だけ含まれます。これで、線形に増加するインデックスから開始し、LCGシーケンスの next 値を<!> quot; secret <!> quot;として使用できます。キー。
EDIT2
実装: 独自のLCGパラメーターを設計できますが、間違えた場合、全範囲(したがって重複している)であるため、ここでから公開および試行した一連のパラメーターを使用します。この論文:
a = 16807, c = 0, m = 2147483647
これにより、2 ** 31の範囲が得られます。 pack()を使用すると、結果の整数を文字列として取得できます。base64_encode()は、読み取り可能な文字列(最大6文字、1バイトあたり6ビット)にするため、これが関数になります。
substr(base64_encode(pack("l", (16807 * $index) % 2147483647)), 0, 6)
他のヒント
おそらく現在の日時/乱数のMD5ハッシュを生成し、必要な長さ(5〜8文字)に切り捨てて、idフィールドとして保存できます。
この情報をデータベースに保存する場合、衝突チェックを行うためにforループを使用する必要はありませんが、selectステートメントを実行するだけでかまいません
SELECT count(1) c FROM Table WHERE id = :id
where:idは、新しく生成されたIDです。 cが0より大きい場合、すでに存在していることがわかります。
編集
これは最善の方法ではないかもしれません。しかし、私はそれを試してみるので、あなたが必要なのは、何らかの方法で数字を一意の短い文字列に変換することであり、それは順番になっていないと思います。
おっしゃるように、base64エンコーディングはすでに短い文字列への変換を行っています。シーケンスの問題を回避するために、自動生成されたIDを<!> quot; random <!> quot;にマッピングすることができます。値(一意のマッピング)。次に、この一意の値をbase64でエンコードできます。
次のようにこのマッピングを生成できます。 1〜10,000,000の値を一時テーブルに保存します。ランダムに並べ替えて、マップテーブルに保存します。
INSERT INTO MappingTable (mappedId) SELECT values FROM TemporaryTable ORDER BY RAND()
MappingTableには、2つのフィールドID(自動生成されたIDがこれに対して検索されます)とmappedId(base64エンコーディングを生成するもの)があります。
10,000,000に近づいたら、上記のコードを再度実行し、一時テーブルの値を10,000,001-20,000,000などで変更できます。
ビット単位のXORを使用して、一部のビットをスクランブルできます。
select thefield ^ 377 from thetable;
+-----+---------+
| a | a ^ 377 |
+-----+---------+
| 154 | 483 |
| 152 | 481 |
| 69 | 316 |
| 35 | 346 |
| 72 | 305 |
| 139 | 498 |
| 96 | 281 |
| 31 | 358 |
| 11 | 370 |
| 127 | 262 |
+-----+---------+
IDをハイジャックするには、短い一意の文字列の背後にある暗号化方法を見つけるだけでよいため、これは決して安全ではないと思います。ループ内の衝突を確認することは、あなたの設定で本当に問題がありますか?
増加する番号のMD5 うまくいくはずですが、私は MD5(これは 通常128ビット)5-8まで 文字、あなたはほぼ確実になります として機能する能力を損なう 一意の署名...
完全に正しい。特に、80%の衝突の可能性に達すると、切り捨てられたMD5は、それ自体で一意性を保証するために任意の乱数と同等になります。つまり、価値がありません。
しかし、とにかくデータベースを使用しているので、なぜUNIQUE INDEXを使用しないのですか?このように、一意性チェックはMySQL自体によって(ループを使用するよりもはるかに効率的な方法で)行われます。 MD5で生成されたキーを使用してINSERTを実行してみて、失敗した場合は再試行してください...
自動インクリメントフィールドを使用できず、絶対に一意の値が必要な場合は、 UUID 。 (自動インクリメント以外に)他のものを使用することにした場合、衝突をチェックしないのは愚かなことです。
このブログ投稿には、あなたが望んでいるものに近いものがあります。
増加する数字のMD5は問題ないはずですが、MD5(通常は128ビット)を5〜8文字に切り捨てると、ほぼ間違いなく、一意の署名...