短链是很常见的,在互联网营销场景和移动信息传播场景中发挥着重要作用。同时,也是经常用来检验玩家系统设计水平的场景。对于服务端研发,关于前端访问时的长度转换,其实只要知道有30X重定向,基本就够了。与重定向相比,我更关注短链生成方式选择、存储选择、系统性能响应的方案和设计。Part1短链系统分析短链系统最根本的能力:可以根据长链计算出短链,方便外部访问:如果判断对应的短链存在,则直接返回判断对应的短链不存在,再生成一个短链,并存储长链->短链映射关系,也可以根据短链映射到长链,寻找真正的服务地址提供服务:根据短链->长链查询存储,得到对应的长链,条条大路通罗马,系统有很多解决方案,但是哪一种最合适需要结合存储策略和访问性能来考虑~Part2实现方案分析Hash是最容易想到的实现策略之一,那么Hash方法的优缺点是什么?如何改进?Hash策略要点分析首先,如果短链是通过hash方法生成的,短链是无法通过hash码破译长链的。所以必须存储短链和长链的关联关系;其次,长链的长度一般都很长,不方便索引构建。需要生成一条固定且唯一的短字符串长链,以辅助存储和查询。(如32位MD5压缩,加密算法一般不利于压缩,压缩算法一般不可逆);再次,hash难免会产生冲突,需要在原长链的末端拼接一个或多个固定的字符串来消除冲突。因此,当接入链条较长时,也需要剪断固定弦。如果用mysql进行结构化存储,Hash策略的存储设计比较简单:id|短链|长链MD5|长链|优点是结构化查询,结构清晰,可以设置索引提高效率;缺点是高并发下的性能需要额外注意,保存的数据会过期,理论上需要额外处理;如果使用redis等非结构化kv存储,需要存储多个关系进行查询:longchainMD5->shortchain|shortchain->longchainMD5|长链MD5->长链。维护多个KV关系有点麻烦。改进——自增ID+高阶系统如果说长链MD5标识的生成和存储是不可或缺的,那么可以优化的点只能从短链->长链MD5的转换开始。有什么办法可以节省短链->长链MD5的存储吗?如果我们让唯一标识和短链通过计算相互编解码,是不是就可以了!?不仅节省了一部分存储,还节省了查询耗时的存储,本地的简单计算总是比查询外部存储快很多。经常使用基数转换,我们可以知道,基数越高,它所占的位数就越少。16进制转换例子这个时候用MD5是不合适的,不容易参与计算。所以,我们使用纯数字分布式ID,而不是MD5字符串(一般公司应该都有现成的分布式ID生成服务吧)。虽然使用baseconversion编码成短链很方便,但是有时候我们不希望短链很容易被解码,导致服务器被遍历。因此,我们需要考虑对基础转换进行加密。编码实现带加密的十六进制转换首先需要选择高位基数:我们启用0-9a-zA-Z的所有数字和字母,共62位,即支持62进制转换privatestaticfinalString="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";//字节数组privatestaticfinalbyte[]DIGITAL;static{DIGITAL=DIGITAL_STRING.getBytes(StandardCharsets.US_ASCII);}十进制转62的编码实现:publicstaticStringencode(longdistributedid){//复制内部longvalue=id;//为需要的短链长度创建空间,这里可以限制短链的长度最大12位,最小6位ByteBufferbuf=ByteBuffer.allocate(12);//最多执行12次转换for(inti=0;i<12;i++){//分布式ID模62intmod=(int)(value%62);//加密,然后取模intpos=(mod+(OFFSET<=6){中断;}}//从ByteBuffer中获取结果集byte[]result=newbyte[buf.position()];buf.rewind();buf.get(结果);//反转顺序ArrayUtils.reverse(result);returnnewString(result);}关注(mod+(OFFSET<
