创建自己的TinyURL

时间:2009-07-02 16:36:08

标签: php mysql

我刚发现这个great tutorial,因为它是我需要的东西。

然而,看了之后,这似乎效率低下。它的工作方式是,首先生成一个唯一的密钥,然后检查它是否存在于数据库中以确保它确实是唯一的。但是,数据库越大,函数越慢,对吧?

相反,我在想,有没有办法为这个功能添加排序?所以必须要做的就是检查数据库中的上一个条目并增加密钥。所以它永远是独一无二的?

function generate_chars()

{

    $num_chars = 4; //max length of random chars
    $i = 0;
    $my_keys = "123456789abcdefghijklmnopqrstuvwxyz"; //keys to be chosen from
    $keys_length = strlen($my_keys);
    $url  = "";
    while($i<$num_chars)
    {
        $rand_num = mt_rand(1, $keys_length-1);
        $url .= $my_keys[$rand_num];
        $i++;
    }
    return $url;
}

function isUnique($chars)

{
    //check the uniqueness of the chars
    global $link;
    $q = "SELECT * FROM `urls` WHERE `unique_chars`='".$chars."'";
    $r = mysql_query($q, $link);
    //echo mysql_num_rows($r); die();
    if( mysql_num_rows($r)>0 ): 
        return false;
    else: 
        return true;
    endif;
}

13 个答案:

答案 0 :(得分:13)

微小的网址人们喜欢使用随机令牌,因为那样你就不能仅仅使用微小的网址链接。 “#2去哪了?” “哦,很酷!” “#3去哪了?” “更酷!”您可以键入随机字符,但不太可能达到有效值。

由于密钥相当稀疏(4个值各有36 *个可能性给你1,679,616个唯一值,5个给你60,466,176)碰撞的机会很小(实际上,它是设计的一个理想部分)和一个好的SQL索引将使查找变得微不足道(实际上,它是URL的主要查找,因此它们围绕它进行优化)。

如果你真的想避免查找并且只是不使用自动增量,你可以创建一个函数,将一个整数转换成一串看似随机的字符,并具有转换回来的能力。所以“1”变成“54jcdn”而“2”变成“pqmw21”。与Base64编码类似,但不使用连续字符。

(*)我实际上喜欢使用少于36个字符 - 单个字符,没有元音,没有类似字符(1,l,I)。这可以防止意外发誓,并且还可以让某人更容易向其他人说出价值。我甚至将相似的字符映射到彼此,接受“0”表示“O”。如果您完全基于机器,则可以使用大写和小写以及所有数字以获得更大的可能性。

答案 1 :(得分:9)

在数据库表中,unique_chars字段上有一个索引,所以我不明白为什么会这么慢或效率低。

UNIQUE KEY `unique_chars` (`unique_chars`)

不要急于对你认为可能很慢的事情进行过早优化。

此外,网址缩短服务可能会产生一些好处,即生成随机网址而不是连续网址。

答案 2 :(得分:8)

我不知道为什么你会打扰。本教程的前提是创建一个“随机”URL。如果随机空间足够大,那么你可以简单地依靠纯粹,愚蠢的运气。如果随机字符空间为62个字符(A-Za-z0-9),则给定合理的随机数生成器时,它们使用的4个字符在62 ^ 4中为1,在14,776,336中为1。 916,132,832中有5个字符是1。因此,冲突实际上是“十亿分之一”。

显然,随着文件填写,你的赔率几率会增加。

有10,000个文件,91,613中有1个,近10个中有1个(圆形数字)。

这意味着,对于每个新文档,你有一个91,613的机会再次击中数据库再次拉动老虎机。

这不是确定性的。这是随机的。这很幸运。从理论上讲,你可以击中一串真的,真的,坏运气,碰撞后碰撞后碰撞。而且,它最终会填满。您计划散列多少个网址?

但是,如果91,613赔率中的1分不够好,将它提升到6个字符会使得它在10,000个文档中超过5分钟。我们这里谈的几乎是LOTTO赔率。

简单地说,让密钥足够大(7个字符?8?),问题几乎“希望”自己不存在。

答案 3 :(得分:3)

无法在生成URL时将URL编码为Base36,然后在访问时对其进行解码 - 这样可以完全删除数据库吗?

Channel9的摘录:

  

公式很简单,只需转动即可   我们的帖子的条目ID,这是一个很长的   由Base-36编成一个短串   编码然后坚持   “http://ch9.ms/”就在它前面。   这会产生相当短的网址,   并且可以在任何一端计算   无需任何数据库外观   起来。结果,一个URL就好了   然后使用http://ch9.ms/A49H   创建推特链接。

答案 4 :(得分:2)

我通过实现一个用于在 base36 中逐个生成序列号的alogirthm解决了类似的问题。我有自己的 oredring of base36 characters 所有这些都是独一无二的。由于它是连续生成数字,我不必担心重复。数字的复杂性和随机性取决于base36数字[字符]的排序......对于公众而言,这也只是因为我的应用程序它们是序列号:)

答案 5 :(得分:2)

查看这些人的功能 - http://www.pgregg.com/projects/php/base_conversion/base_conversion.php来源 - http://www.pgregg.com/projects/php/base_conversion/base_conversion.inc.phps

您可以使用任何您喜欢的基础,例如将554512转换为基础62,调用

$tiny = base_base2base(554512, 10, 62);,评估为$tiny = '2KFk'

因此,只需传入数据库记录的唯一ID即可。

在一个项目中,我在$sChars字符串中删除了几个字符时使用了这个字符,并使用了基数58.如果您希望值不易猜测,也可以重新排列字符串中的字符

答案 6 :(得分:1)

您当然可以通过简单地为网址编号来添加排序:

http://mytinyfier.com/1
http://mytinyfier.com/2

等等。但是如果哈希键在数据库中被索引(显然应该是这样),性能提升最多也是最小的。

答案 7 :(得分:1)

我不打算做有序枚举,原因有两个:

1)SQL服务器在检查此类哈希冲突(给定正确的索引)时非常有效

2)这可能会损害隐私,因为用户可以轻松找出其他用户的微观内容。

答案 8 :(得分:0)

这可能有效,但解决问题的最简单方法可能是哈希。从理论上讲,散列运行在O(1)时间内,因为它只需要执行散列,然后只对数据库执行一次实际命中以检索值。然后,您将引入检查哈希冲突的复杂性,但似乎这可能是大多数微型提供商所做的。并且,一个好的哈希函数并不是很难写。

答案 9 :(得分:0)

在数据库上使用自动增量,并按http://www.acuras.co.uk/articles/24-php-use-mysqlinsertid-to-get-the-last-entered-auto-increment-value

所述获取最新的ID

答案 10 :(得分:0)

也许这有点无法回答,但是,我创建始终唯一键的一般规则是简单的md5(time()* 100 + rand(0,100));如果两个人在同一秒内使用相同的服务,他们将获得相同的结果(不可能),有十分之一的机会。

那就是说,md5(rand(0,n))也可以。

答案 11 :(得分:0)

我还创建了小型的tinyurl服务。

我在Python中编写了一个脚本,该脚本生成密钥并存储在名为tokens的MySQL表中,状态为U(未使用)。

但是,我是在离线模式下进行的。我的VPS上有玉米工作。它每10分钟运行一次脚本。脚本检查表中是否有少于1000个键,如果它们是唯一的并且在表中不存在,它会继续生成键并插入它们,直到键计数到1000为止。

对于我的服务,10分钟的1000个密钥绰绰有余,您可以根据需要设置生成密钥的时间或数量。

现在,当我的网站上需要创建任何小网址时,我的PHP脚本只需获取表中未使用的任何密钥,并将其状态标记为T(已采用)。 PHP脚本不必担心其独特性,因为我的python脚本只填充了唯一键。

答案 12 :(得分:-1)

难道你不能把哈希修剪到你想要的长度吗?

$tinyURL = substr(md5($longURL . time()),0,4);

当然,这可能无法提供与使用整个字符串长度一样多的伪随机性。但是,如果您对与time()连接的长URL进行哈希处理,这不足够吗?关于使用这种方法的想法?谢谢!