设为首页 - 加入收藏
广告 1000x90
您的当前位置:主页 > 网络营销 > 正文

如何创建一个短链接系统?

来源:引流技巧 编辑:爱短链 时间:2025-08-12

短链接,通俗的讲,就是通过程序计算等方法,将一个长URL转换成一个短URL字符串。


人们经常会收到一些莫名其妙的营销短信,其中包含一个非常短的链接供您跳转。新浪微博因为字数限制,经常会看到一些不像网址的网址。短链的兴起应该是微博字数的限制激发了大家的创造力。


如果创建一个短链接系统,我们应该怎么做?

把长链接变成短链接;访问短链接的用户会跳转到正确的长链接。

找到对应的长URL,跳转到对应的页面。

短链生成方法

短代码一般由62个字母或数字组成[a - z, A - Z, 0 - 9]。短码的长度也可以定制,但一般不超过8位。最常用的是6位创建一个短链系统,我们应该做什么呢?(图) ,6位短码可以有568亿个组合:(26+26+10)^6 = 56800235584,已经满足了大部分使用场景。

目前比较流行的短码生成方法有:自增id、摘要算法、普通随机数。

自动递增 ID

该方法是一种无碰撞方法。原理是每次添加一个短码,最后一次添加的短码的id加1,然后将十进制的id值转换成62位的id值。细绳。

一般使用数据表中的自增id来完成:每次查询数据表中自增id的最大值时,对应插入的长URL的自增id值为max +1, max+1 转换成 62 的短码短链接系统服务,可以通过十六进制得到。

但是,短代码 id 从一位数的长度开始递增。短码的长度不是固定的,但是可以通过从指定的数字开始增加id来处理,保证所有的短码长度相同。同时生成的短码是有序的,可能存在安全问题。生成的短码id可以和长URL等其他关键字进行md5操作,生成最终的短码。


摘要算法

摘要算法也称为哈希算法,是指任意长度的输入数据和固定长度的输出数据。相同的输入数据总是得到相同的输出,不同的输入数据试图得到不同的输出。


算法过程:

从长URL md5生成一个32位的签名串短链接系统,分为4段,每段8字节;循环处理这四个段短链接系统,取8个字节,把它当作一个16进制字符串和0x3fffffff(30位1)与运算,即忽略30位以上的处理;这30位分为6个段,每个 5 位数字作为字母表的索引,得到一个特定的字符,依次得到 6 位字符串;总共 md5 字符串可以得到 4 个 6 位字符串;取其中任意一个作为这个长url的短url地址;

虽然这个算法会产生 4 个,但仍有重复的机会。

虽然概率很小,但是这种方法还是有冲突的可能,解决冲突会比较麻烦。但是这种方法生成的短码数量是固定的,连续生成的短码没有先后顺序。

普通随机数

方法是从62个字符串中随机选择一个6位短码的组合,然后到数据库中检查该短码是否已经存在。如果已经存在,则继续循环该方法再次获取短码,否则直接返回。

这种方法是最简单的实现,但是由于Math.round()方法生成的随机数是伪随机数,碰撞的可能性不小。在数据量很大的情况下,可能会重复多次生成不冲突的短码。

算法分析

我们将一一分析上述算法的优缺点。

如果你使用自增id算法,就会出现不法分子可以穷举你的短链地址的问题。原理是将十进制数转换为62,这样别人就可以用同样的方法遍历你的短链,得到对应的原始链接。例如:and,这两个短链网站,分别从a3300-a3399,可以尝试多次返回正确的url。因此,以这种方式生成的短链实际上对用户来说并不安全。

抽象算法实际上是一种哈希算法。说到散列,大家可能觉得很低,但实际上散列可能是最优解。比如发现不断生成的url没有规律,很有可能是用hash算法来实现的。

普通随机数算法,这种算法生成的东西和摘要算法一样,但是碰撞的概率会更高。因为摘要算法毕竟是对URL进行hash,随机数算法就是简单的随机生成,一旦数字上来,难免会导致重复。

基于以上,我选择了最低的算法:摘要算法。

完成

存储解决方案

数据库存储解决方案

短网址的基础数据以域名和后缀的形式分开存储。另外,域名需要区分HTTP和HTTPS,哈希方案对整个链接进行哈希处理,而不是对域名以外的链接进行哈希处理。域名单独保存,可用于分析当前域名下的链接使用情况。

添加当前链接有效性字段。一般来说,短链需求可能是相关活动或热点事件。这条短链会在一段时间内非常活跃,一段时间后景气度会继续下降。所以没有必要永久保留这种链接,增加每次查询的负担。

对于过期数据的处理,可以在添加新的短链时判断当前短链的过期日期,并为每天到达过期日期的数据在HBase中创建单独的表。每天只处理当天HBase表中的无效数据。

数据库的基本表如下:

base_urlsuffix_urlshot_codetotal_click_countfull_urlexpiration_date

/搜索/12345

edfg3s

/aiCheck/getResult/123

Fe9dq

/文库/12354

lcfr53

字段定义:

base_url:域名

suffix_url:链接除域名外的后缀

full_url:完整链接

shot_code:当前suffix_url链接的短代码

expiration_date:到期日期

total_click_count:当前链接的总点击次数

expire_date:当前链接过期时间


缓存方案

个人觉得缓存上百G数据不适合,所以有一个折中的方案:把最近3个月的查询或者新增的URL放入缓存,使用LRU算法进行热更新. 这样发送最近使用的概率会命中缓存,所以不用去库了。找不到的时候去图书馆更新缓存。

对于新的链接,首先检查缓存是否存在,如果缓存不存在,再检查数据库。数据库已经分表了,查询效率不会很低。

查询要求是用户持有短链查询对应的真实地址,那么缓存的key只能是短链,可以以KV的形式存储。

杂耍

其实也可以考虑其他的存储方案,比如HBase,作为NOSQL数据库,HBase在性能上仅次于redis,但是存储成本比redis低很多数量级。存储基于 HDFS。当内存已满时,数据将被刷新到 HFile。读取数据也更快,因为它使用 LSM 树结构而不是 B 或 B+ 树。HBase 会使用 LRU 算法将最近读取的数据放入缓存中。如果想增强读取能力,可以增加blockCache。

其次,也可以使用ElasticSearch,适当的索引规则的效果并不逊色于缓存方案。

是否需要分库分表?

单条数据小于10b,1亿条数据总容量约953G。一张表肯定不能支持这么大的量,所以需要分表。如果你有信心在2年内服务可以达到这个规模,那么你从设计之初就可以考虑分表的方案。

那么如何定义分表的规则呢?

如果按照单表500万条记录计算,一共可以分成20张表,那么单表的容量是47G,还是蛮大的,所以考虑分表的key和单表的容量,如果分成100张表,那么单表的容量是10G,通过数字后缀路由到表也比较容易。您可以对 short_code 进行编码以生成数字类型,然后进行路由。

怎么跳

当我们在浏览器中输入

DNS第一次解析得到的IP地址 当DNS得到IP地址时(例如:12.34.5.32),会向这个发送HTTPGET请求地址,查询的是短码a3300【服务器会通过短码a3300获取对应的长URL,通过HTTP301请求去对应的长URL, 25197,26089617-5013871,00.html.


短链接系统


这里有个小知识点,为什么用301跳而不是302跳?

知识点:为什么用302跳而不是301跳?

301是永久重定向,302是临时重定向。短地址一旦生成就不会改变,所以使用 301 符合 http 语义。但是如果使用301、谷歌、百度等搜索引擎,搜索时会直接显示真实地址,那么我们无法统计短地址被点击的次数,也无法收集用户的Cookie、User Agent等信息。可以用来做很多有趣的大数据分析,也是短网址系统服务商的主要利润来源。

引用自知乎-武林的回答,原文链接

附上两个算法:

总结算法:

import org.apache.commons.lang3.StringUtils; import javax.xml.bind.DatatypeConverter; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.concurrent.atomic.AtomicLong; import static com.alibaba.fastjson.util.IOUtils.DIGITS; /** * @author rickiyang * @date 2020-01-07 * @Desc TODO */ public class ShortUrlGenerator {    public static void main(String[] args) {        String sLongUrl = "http://www.baidu.com/121244/ddd";        for (String shortUrl : shortUrl(sLongUrl)) {            System.out.println(shortUrl);        }    }    public static String[] shortUrl(String url) {        // 可以自定义生成 MD5 加密字符传前的混合 KEY        String key = "dwz";        // 要使用生成 URL 的字符        String[] chars = new String[]{"a", "b", "c", "d", "e", "f", "g", "h",                "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",                "u", "v", "w", "x", "y", "z", "0", "1", "2", "3", "4", "5",                "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H",                "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T",                "U", "V", "W", "X", "Y", "Z"        };        // 对传入网址进行 MD5 加密        String sMD5EncryptResult = "";        try {            MessageDigest md = MessageDigest.getInstance("MD5");            md.update((key + url).getBytes());            byte[] digest = md.digest();            sMD5EncryptResult = DatatypeConverter.printHexBinary(digest).toUpperCase();        } catch (NoSuchAlgorithmException e) {            e.printStackTrace();        }        String[] resUrl = new String[4];        //得到 4组短链接字符串        for (int i = 0; i < 4; i++) {            // 把加密字符按照 8 位一组 16 进制与 0x3FFFFFFF 进行位与运算            String sTempSubString = sMD5EncryptResult.substring(i * 8, i * 8 + 8);            // 这里需要使用 long 型来转换,因为 Inteper .parseInt() 只能处理 31 位 , 首位为符号位 , 如果不用 long ,则会越界            long lHexLong = 0x3FFFFFFF & Long.parseLong(sTempSubString, 16);            String outChars = "";            //循环获得每组6位的字符串            for (int j = 0; j < 6; j++) {                // 把得到的值与 0x0000003D 进行位与运算,取得字符数组 chars 索引(具体需要看chars数组的长度   以防下标溢出,注意起点为0)                long index = 0x0000003D & lHexLong;                // 把取得的字符相加                outChars += chars[(int) index];                // 每次循环按位右移 5 位                lHexLong = lHexLong >> 5;            }            // 把字符串存入对应索引的输出数组            resUrl[i] = outChars;        }        return resUrl;    } }

将数字转换为 base62 算法:

/** * @author rickiyang * @date 2020-01-07 * @Desc TODO ** 进制转换工具,最大支持十进制和62进制的转换 * 1、将十进制的数字转换为指定进制的字符串; * 2、将其它进制的数字(字符串形式)转换为十进制的数字 */ public class NumericConvertUtils {    public static void main(String[] args) {        String str = toOtherNumberSystem(22, 62);        System.out.println(str);    }    /**     * 在进制表示中的字符集合,0-Z分别用于表示最大为62进制的符号表示     */    private static final char[] digits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',            'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',            'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'};    /**     * 将十进制的数字转换为指定进制的字符串     *     * @param number 十进制的数字     * @param seed   指定的进制     * @return 指定进制的字符串     */    public static String toOtherNumberSystem(long number, int seed) {        if (number < 0) {            number = ((long) 2 * 0x7fffffff) + number + 2;        }        char[] buf = new char[32];        int charPos = 32;        while ((number / seed) > 0) {            buf[--charPos] = digits[(int) (number % seed)];            number /= seed;        }        buf[--charPos] = digits[(int) (number % seed)];        return new String(buf, charPos, (32 - charPos));    }    /**     * 将其它进制的数字(字符串形式)转换为十进制的数字     *     * @param number 其它进制的数字(字符串形式)     * @param seed   指定的进制,也就是参数str的原始进制     * @return 十进制的数字     */    public static long toDecimalNumber(String number, int seed) {        char[] charBuf = number.toCharArray();        if (seed == 10) {            return Long.parseLong(number);        }        long result = 0, base = 1;        for (int i = charBuf.length - 1; i >= 0; i--) {            int index = 0;            for (int j = 0, length = digits.length; j < length; j++) {                //找到对应字符的下标,对应的下标才是具体的数值                if (digits[j] == charBuf[i]) {                    index = j;                }            }            result += index * base;            base *= seed;        }        return result;    } } 以上就是关于《如何创建一个短链接系统?》的全部内容了,感兴趣的话可以点击右侧直接使用哦!》》在线短链接生成器

相关推荐:

栏目分类

微商引流技巧网 www.yinliujiqiao.com 联系QQ:1716014443 邮箱:1716014443@qq.com

Copyright © 2019-2024 强大传媒 吉ICP备19000289号-9 网站地图 rss地图

Top