如何为数据库中的每个帖子创建一个简短的唯一ID?

时间:2010-12-01 05:49:27

标签: mysql database algorithm hash computer-science

mydomain.com/show/?id=sf32JFSVANMfaskjfh

通常我只生成一个长度为25个字符的随机字符串并以这种方式访问​​我的帖子。但就今天而言,短网址是必要的。

如果我想要3个字母的ID ...我不能只生成随机字符。它会在某个时候发生冲突。

我该怎么办?

8 个答案:

答案 0 :(得分:4)

  

<强> PHP:

运行:

rand_uniqid(9007199254740989);

将返回'PpQXn7COf'和:

rand_uniqid('PpQXn7COf',true);

将返回'9007199254740989'


如果你想让rand_uniqid长度至少为6个字母,请使用$ pad_up = 6参数


您可以通过在函数体顶部的$ index var中添加字符来支持更多字符(使得生成的rand_uniqid更小)。


<?php
function rand_uniqid($in, $to_num = false, $pad_up = false, $passKey = null)
{
    $index = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    if ($passKey !== null) {
        // Although this function's purpose is to just make the
        // ID short - and not so much secure,
        // you can optionally supply a password to make it harder
        // to calculate the corresponding numeric ID

        for ($n = 0; $n<strlen($index); $n++) {
            $i[] = substr( $index,$n ,1);
        }

        $passhash = hash('sha256',$passKey);
        $passhash = (strlen($passhash) < strlen($index))
            ? hash('sha512',$passKey)
            : $passhash;

        for ($n=0; $n < strlen($index); $n++) {
            $p[] =  substr($passhash, $n ,1);
        }

        array_multisort($p,  SORT_DESC, $i);
        $index = implode($i);
    }

    $base  = strlen($index);

    if ($to_num) {
        // Digital number  <<--  alphabet letter code
        $in  = strrev($in);
        $out = 0;
        $len = strlen($in) - 1;
        for ($t = 0; $t <= $len; $t++) {
            $bcpow = bcpow($base, $len - $t);
            $out   = $out + strpos($index, substr($in, $t, 1)) * $bcpow;
        }

        if (is_numeric($pad_up)) {
            $pad_up--;
            if ($pad_up > 0) {
                $out -= pow($base, $pad_up);
            }
        }
        $out = sprintf('%F', $out);
        $out = substr($out, 0, strpos($out, '.'));
    } else {
        // Digital number  -->>  alphabet letter code
        if (is_numeric($pad_up)) {
            $pad_up--;
            if ($pad_up > 0) {
                $in += pow($base, $pad_up);
            }
        }

        $out = "";
        for ($t = floor(log($in, $base)); $t >= 0; $t--) {
            $bcp = bcpow($base, $t);
            $a   = floor($in / $bcp) % $base;
            $out = $out . substr($index, $a, 1);
            $in  = $in - ($a * $bcp);
        }
        $out = strrev($out); // reverse
    }

    return $out;
}

echo rand_uniqid(1);
?>

  

<强>的PostgreSQL:

<?php 

CREATE OR REPLACE FUNCTION string_to_bits(input_text TEXT) 
RETURNS TEXT AS $$
DECLARE
    output_text TEXT;
    i INTEGER;
BEGIN
    output_text := '';


    FOR i IN 1..char_length(input_text) LOOP
        output_text := output_text || ascii(substring(input_text FROM i FOR 1))::bit(8);
    END LOOP;


    return output_text;
END;
$$ LANGUAGE plpgsql; 


CREATE OR REPLACE FUNCTION id_to_sid(id INTEGER) 
RETURNS TEXT AS $$
DECLARE
    output_text TEXT;
    i INTEGER;
    index TEXT[];
    bits TEXT;
    bit_array TEXT[];
    input_text TEXT;
BEGIN
    input_text := id::TEXT;
    output_text := '';
    index := string_to_array('0,d,A,3,E,z,W,m,D,S,Q,l,K,s,P,b,N,c,f,j,5,I,t,C,i,y,o,G,2,r,x,h,V,J,k,-,T,w,H,L,9,e,u,X,p,U,a,O,v,4,R,B,q,M,n,g,1,F,6,Y,_,8,7,Z', ',');

    bits := string_to_bits(input_text);

    IF length(bits) % 6 <> 0 THEN
        bits := rpad(bits, length(bits) + 6 - (length(bits) % 6), '0');
    END IF;

    FOR i IN 1..((length(bits) / 6)) LOOP
        IF i = 1 THEN
            bit_array[i] := substring(bits FROM 1 FOR 6);
        ELSE
            bit_array[i] := substring(bits FROM 1 + (i - 1) * 6 FOR 6);
        END IF;

        output_text := output_text || index[bit_array[i]::bit(6)::integer + 1];
    END LOOP;


    return output_text;
END;
$$ LANGUAGE plpgsql; 

 ?>

  

<强> JavaScript的:

<script>
/*jslint white: true, browser: true, onevar: true, undef: true, nomen: true, eqeqeq: true, newcap: true, immed: true */

/*global Crypto:true */

if (typeof Crypto === 'undefined') {
    Crypto = {};
}

Crypto.random = (function () {
    var index = [
        'b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm',
        'n', 'p', 'q', 'r', 't', 'v', 'w', 'x', 'y', 'z',
        '_', '-', '0', '1', '2', '3', '4', '5', '6', '7',
        '8', '9', 'B', 'C', 'D', 'F', 'G', 'H', 'J', 'K',
        'L', 'M', 'N', 'P', 'Q', 'R', 'T', 'V', 'W', 'X',
        'Y', 'Z'
    ], base = index.length;

    return {
        encode: function (i) {
            var out = [],
                t = Math.floor(Math.log(i) / Math.log(base)),
                bcp,
                a;

            while (t >= 0) {
                bcp = Math.pow(base, t);
                a = Math.floor(i / bcp) % base;
                out[out.length] = index[a];
                i -= a * bcp;

                t -= 1;
            }

            return out.reverse().join('');
        },
        decode: function (i) {
            var chars = i.split(''),
                out = 0,
                el;

            while (typeof (el = chars.pop()) !== 'undefined') {
                out += index.indexOf(el) * Math.pow(base, chars.length);
            }

            return out;
        }
    };
}());
</script>

示例:

<script>
alert(Crypto.random.encode(101010101));
alert(Crypto.random.decode('XMzNr'));
</script>

答案 1 :(得分:2)

为什么不使用自动增量数字,如5836?每次插入新行时,该列将递增1。例如,如果最新的行是5836,则下一行将是5837,依此类推。

只使用长度为15的INT类型列。或者,如果它不是由常规成员添加的行类型,请使用小型int或中型int类型。

答案 2 :(得分:1)

作为一般规则,较短的哈希值会导致更高的冲突率。较长的哈希值也会导致冲突,但这种冲突的可能性会降低。

如果您想要更短的哈希值,则应实施冲突解决策略。

答案 3 :(得分:1)

如果你的每个帖子都有一个id,并且它是数字的,你可以使用任意基数对它们进行编码。想想有点像十六进制,但数字更大..

查看leah culver的这个网址...

http://blog.leahculver.com/2008/06/tiny-urls-based-on-pk.html

了解更多想法。我过去曾经使用过它,效果很好。在leah的帖子中它是base 56,所以只需拿走你的主键(整数)并将它编码到你的新基础56中,你就可以了。

答案 4 :(得分:1)

  

短网址是必要的

真的?为什么?您是否打算让用户手动输入?

我认为,因为它们几乎肯定是来自其他地方的链接,所以URL的大小几乎无关紧要。

担心用于访问帖子的网址大小的用户正在浪费时间。无论如何,如果你愿意,可以将一个大整数编码到base64或类似的东西,但我个人认为这是浪费时间,因为“你可能会做的事情可能会带来更大的投资回报”。 / p>


在Twitter上评论你的评论,我只是像现在一样为实际帖子分配连续的25个字符(或者更短的,如果你想要的话),然后使用缩短的版本来减少你需要的URL。例如,该25个字符的ID的最后4个字符。

然后将该4个字符的ID映射到最新的等效的25个字符的ID(意味着有两个URL(短和长)可以到达该帖子)。这意味着你的消息将有效,直到你翻身,但仍然会给你大量的活动消息(在base64,64 4 或超过1600万条消息)。

完整尺寸的网址将能够直接发布(实际上,因为25个字符可以为您提供大约10条 45 消息)。

答案 5 :(得分:0)

NOID: Nice Opaque Identifier (Minter and Name Resolver)旨在为图书馆系统执行此操作

但它的基本设计是它创建一个id并检查它是否已被使用,如果没有那么它可以在后台生成使用并向用户分发id可以避免创建它们的开销

答案 6 :(得分:0)

我要说你必须看到外侧和内侧。您不能拥有“仅3”字母的唯一数字,或者至少只能在短时间内使用。所以你可以使用内部长的identfiers,我想到的就是使用UUIDS,然后你必须弄清楚如何使这样的UUIDS使用漂亮或可读的URL。例如,如果我们看一下类似的帖子 YYYY.mm.dd.nnn可能会这样做。我建议查看REST方法......

答案 7 :(得分:0)

AFAIK大多数语言都有自己的方法。例如在PHP中,您可以使用内置函数uniqid()。 但是,使用此方法,还必须将生成的uniqid保存在数据库中,以便可以在SQL中的“where”子句中使用它。据我所知,没有办法解密这些所谓的GUID,因此他们不会告诉你任何关于何时制作或任何东西的信息。

<?php
//For a basic unique id based on the current microtime:
$uniq_id = uniqid();
//If you want it to be even more uniq, you can play around with rand().
function rlyUniqId(){
    $letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghyjklmnopqursuvwxyz1234567890";
    $letter_count = strlen($letters);
    $prefix_letter_count = rand(1,4);
    for($i=1;$i<=$prefix_letter_count;$i++){
        $letter_pos = rand(0,$letter_count)-1;
        $prefix .= substr($letters,$letter_pos);
    }
    return uniqid($prefix);
}
$rly_uniq_id = rlyUniqId();
?>

前面提到的另一个建议是使用标准生成的自动增量id,然后在你的URL中对它进行base64编码,这样它看起来很花哨。这可以再次解码,这意味着您只需解码url中的id即可调用数据库中的id。另一方面,任何人都可以这样做,所以如果要隐藏实际的id,这种方式绝不是理想的。此外,如果您有时需要手动编码链接,有时这种方法有点烦人。 从好的方面来说,它提供了比前面提到的uniqid()更短的网址。

<?php
$data_from_db = array("id"=>14,"title"=>"Some data we got here, huh?");
$url_id = base64_encode($data_from_db["id"]);
echo '<a href="readmore.php?id='.$url_id.'>'.$data_from_db["title"].'</a>';
//when reading the link, simply do:
$original_id = base64_decode($url_id);
?>

我知道这有点晚了,但我希望有一天会帮助某个人:P GL HF!