前不久做了一个优惠劵的分享功能,其中一个功能就是生成一个优惠劵分享短链接。生成的短链接要求每个链接都是唯一的,并且长度尽可能短。在网上查了一下相关的思路,发现了一个不错的算法。这个算法的思路就是用[a-zA-Z0-9]建立一个长度为62的矩阵,然后把矩阵打乱,再生成一个全局唯一的数字,再把这个数字用矩阵内的元素表示转换成62进制,生成的链接长度最大才11位。所以短链接的生成关键点就变成了如何生成一个全局唯一的数字和实现进制的转换。

1、生成全局唯一的数字

  这本质是一个分布式ID的问题。如果简单处理的话可以借用redis的incr操作这样每次取到的ID都是单调递增且唯一的。另外一种方式是借用mysql,这里不是借用mysql的主键的auto_incr特性。而是每一台应用来请求时分配一个范围比如 s1 [100-200], s2 来请求的时候就分配 [201-301],本质是利用乐观锁进行一个cas操作。

     如果不想借助外部去生成ID的话,可以用UUID算法。UUID长度12个字节组成由,以下几个部分组成。

  • 4个字节表示的Unix timestamp,
  • 3个字节表示的机器的ID
  • 2个字节表示的进程ID
  • 3个字节表示的计数器

    UUID是一类算法的统称,具体有不同的实现。优点是每台机器可以独立产生ID,理论上保证不会重复,所以天然是分布式的,缺点是生成的ID太长,不仅占用内存,而且索引查询效率低。

还有一个叫Twitter Snowflake算法,本质上看起来与UUID有些类似

   总的来说redis,mysql解决方案就比较简单直接可以满足大部分的场景,如果要保证高性能和高可用的话UUID和Twitter Snowflake算法就更合适,实现起来相对复杂一些。

2、进制转换

这个操作就相对简单了。直接上代码 ~

/**
* 短链接生成
*/
public class TinyURL { public static final char[] array =
{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', 'a', 's', 'd',
'f', 'g', 'h', 'j', 'k', 'l', 'z', 'x', 'c', 'v', 'b', 'n', 'm',
'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', 'A', 'S', 'D',
'F', 'G', 'H', 'J', 'K', 'L', 'Z', 'X', 'C', 'V', 'B', 'N', 'M'}; public static Map<Character, Integer> charValueMap = new HashMap<Character, Integer>(); //初始化map
static {
for (int i = 0; i < array.length; i++) charValueMap.put(array[i], i);
} public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
long number = Long.MAX_VALUE - i;
String decimalStr = numberConvertToDecimal(number, 62);
System.out.println(number + " 转换成 " + decimalStr);
long toNumber = decimalConvertToNumber(decimalStr, 62);
System.out.println(decimalStr + " 转换成 " + toNumber);
}
} /**
* 把数字转换成相对应的进制,目前支持(2-62)进制
*
* @param number
* @param decimal
* @return
*/
public static String numberConvertToDecimal(long number, int decimal) {
StringBuilder builder = new StringBuilder();
while (number != 0) {
builder.append(array[(int) (number - (number / decimal) * decimal)]);
number /= decimal;
}
return builder.reverse().toString();
} /**
* 把进制字符串转换成相应的数字
* @param decimalStr
* @param decimal
* @return
*/
public static long decimalConvertToNumber(String decimalStr, int decimal) {
long sum = 0;
long multiple = 1;
char[] chars = decimalStr.toCharArray();
for (int i = chars.length - 1; i >= 0; i--) {
char c = chars[i];
sum += charValueMap.get(c) * multiple;
multiple *= decimal;
}
return sum;
} }

  这里面有个小优化就是用charValueMap记录每个字符对应的数值,这是一个用空间换时间的策略优化,把O(n)的时间降为O(1)。

另外通常我们要记录短网址与长网址的对应的关系,相对于直接存储短网址的而言,存储对应的数值ID会更省空间。

短网址服务(TinyURL)生成算法的更多相关文章

  1. Knative 实战:三步走!基于 Knative Serverless 技术实现一个短网址服务

    短网址顾名思义就是使用比较短的网址代替很长的网址.维基百科上面的解释是这样的: 短网址又称网址缩短.缩短网址.URL 缩短等,指的是一种互联网上的技术与服务,此服务可以提供一个非常短小的 URL 以代 ...

  2. 最近做了一个短网址服务 di81.com

    最近做了一个短网址服务:   di81.com 项目中有一处需求,需要把长网址缩为短网址,把结果通过短信.微信等渠道推送给客户.刚开始直接使用网上现成的开放服务,然后在某个周末突然手痒想自己动手实现一 ...

  3. Clojure实现的简单短网址服务(Compojure、Ring、Korma库演示样例)

    用clojure写了一个简单的短网址服务(一半抄自<Clojure 编程>).在那基础上增加了数据库,来持久化数据. 功能 用Get方法缩短一个网址: 然后在短网址列表就能够查看了, 接下 ...

  4. 新浪短网址最新api接口

    1,雨林短网址 网站链接:http://yldwz.cn 雨林短网址采用新浪.腾讯官方API接口,强大的多功能API,简单易用,质量高官 网提供强技术支持,99.9% SLA服务稳定安全可靠的校验机制 ...

  5. 如何做系列(4)-微博URL短网址生成算法原理(java版、php版实现实例)

    短网址(Short URL),顾名思义就是在形式上比较短的网址.通常用的是asp或者php转向,在Web 2.0的今天,不得不说,这是一个潮流.目前已经有许多类似服务,借助短网址您可以用简短的网址替代 ...

  6. URL短网址系统的算法设计及实践

    在通常情况下,URL是由系统生成的,通常包括URI路径,多个查询参数,可以对参数进行加密和解密.当人们要分享某个URL,比如短信,邮件,社交媒体,这就需要短URL. 而短网址,顾名思义就是在长度上比较 ...

  7. 长网址 短网址(http://www.zhihu.com/question/19852154?rf=21975802)

    短网址(Short URL),顾名思义就是在形式上比较短的网址.通常用的是asp或者php转向,在Web 2.0的今天,不得不说,这是一个潮流.目前已经有许多类似服务,借助短网址您可以用简短的网址替代 ...

  8. php短网址生成算法

    <?php //短网址生成算法 class ShortUrl { //字符表 public static $charset = "0123456789ABCDEFGHIJKLMNOPQ ...

  9. URL短网址生成算法原理和php实现案例

    短网址(Short URL),顾名思义就是在形式上比较短的网址. 短链接的好处:1.内容需要:2.用户友好:3.便于管理为什么要这样做的,原因我想有这样几点:微博限制字数为140字一条,那么如果我们需 ...

随机推荐

  1. Linux - 网络检测

    linux 利用bmon/nload/iftop/vnstat/iptraf实时查看网络带宽状况 .添加yum源方便安装bmon # rpm -Uhv http://apt.sw.be/redhat/ ...

  2. 矩阵NumPy

    常量: np.pi π 创建矩阵数组 import numpy as np # array=np.array([[1,2,3],[5,6,7]]) #定义一个2行3列的矩阵数组.2行=2维 # pri ...

  3. CSS —— 选择器

    选择器种类 标签选择器 id选择器 类选择器 通配符 交集选择器 并集选择器 后代选择器 子代选择器 选择器设置样式优先级 默认样式 < 继承样式 < 通配符设置样式 < 标签选择器 ...

  4. adb环境变量配置

    针对win10系统: 搜索“高级系统设置”,点击“环境变量”按钮: 找到“path”双击: 双击“path”,在弹出的环境变量列表中新建,填入adb的文件路径 检查配置是否成功,运行命令adb,出现如 ...

  5. Android开发技巧——ViewPager加View情况封装PagerAdapter的实现类

    ViewPager是Android的support库中的一个控件. ViewPager + Fragment的使用,已经有FragmentAdapter的实现可以帮助我们快速进行开发了: ViewPa ...

  6. Java泛型方法与泛型类的使用------------(五)

    泛型的本质就是将数据类型也参数化, 普通方法的输入参数的值是可以变的,但是类型(比如: String)是不能变的,它使得了在面对不同类型的输入参数的时候我们要重载方法才行. 泛型就是将这个数据类型也搞 ...

  7. 【Linux】Linux下统计当前文件夹下的文件个数、目录个数

    统计当前文件夹下文件的个数,包括子文件夹里的 ls -lR|grep "^-"|wc -l 统计文件夹下目录的个数,包括子文件夹里的 ls -lR|grep "^d&qu ...

  8. 图文并茂的Python教程-numpy.pad

    图文并茂的Python教程-numpy.pad np.pad()常用与深度学习中的数据预处理,可以将numpy数组按指定的方法填充成指定的形状. 声明: 需要读者了解一点numpy数组的知识np.pa ...

  9. linux 查看用户上次修改密码的日期【转】

    1.找到以下文件: cat /etc/shadow 第三段字符就是最近一次密码修改的天数,此数字是距离1970年1月1日的天数.   2.用以下命令计算: date -u -d "1970- ...

  10. (常用)subprocess模块 详情官方

    subprocess包中定义有数个创建子进程的函数,这些函数分别以不同的方式创建子进程,所以我们可以根据需要来从中选取一个使用.另外subprocess还提供了一些管理标准流(standard str ...