Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >设计一个短链接系统

设计一个短链接系统

作者头像
用户3467126
发布于 2020-12-01 03:06:23
发布于 2020-12-01 03:06:23
1.5K00
代码可运行
举报
文章被收录于专栏:爱编码爱编码
运行总次数:0
代码可运行

前言

在发送短信和微博等限定字数的场景下,短链接的需求就应运而生了。

原理

一张图概括了短链接干的事:

来源:孤独的烟

短链接设计关键在于:

短链接生成的算法:如何保证足够短且不冲突。

其中常用的算法有

  • 1、基于哈希的MurmurHash 算法
  • 2、十进制转62进制
  • 3、自增序列(Snowflake、Mysql 自增主键、类 uuid、redis

关于短链接的原理研究可以阅读这两位大佬的文章:

xbmchina.cn/AAAAAG

xbmchina.cn/AAAAAH

实践

基于上面的理论思想:

本文采用十进制转62进制的算法+Redis全局自增的方式实现短链接服务。

公众号:爱编码

1、十进制转62进制

短链接是由 a-z、A-Z 和 0-9 共 62 个字符。

我们可以讲十进制的数字id,转换为一个62进制的数,例如20201122就可以转换为WvOi。

十进制转62进制工具类如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制

public class Base62 {
    private static final char[] toBase62 = {'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',
            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};

    private static final int[] fromBase62 = new int[128];
    private static final int RADIX = 62;

    static {
        Arrays.fill(fromBase62, -1);
        for (int i = 0; i < toBase62.length; i++) {
            fromBase62[toBase62[i]] = i;
        }
    }

    private Base62() {

    }


    public static String encode(Long l) {
        StringBuilder stringBuilder = new StringBuilder();
        while (l > 0) {
            stringBuilder.append(toBase62[(int) (l % RADIX)]);
            l /= RADIX;
        }
        return stringBuilder.reverse().toString();
    }

    public static long decode(String s) {

        long l = 0L;
        long d = 1L;
        for (int i = s.length() - 1; i >= 0; i--) {
            l = l + d * fromBase62[s.charAt(i)];
            d *= RADIX;
        }
        return l;
    }


    /**
     * 根据自增id,生成短链接。
     *
     * @param id
     * @return
     */
    public static String generateLink(Long id, int size) {
        if (size < 4) {
            size = 4;
        }
        long maxId = (long) Math.pow(62, size);
        String encode = Base62.encode(id % maxId);
        if (encode.length() < size) {
            String minLink = "";
            for (int i = 0; i < size; i++) {
                minLink += "A";
            }
            return minLink.substring(0, size - encode.length()) + encode;
        }
        return encode;
    }

    public static void main(String[] args) {
        //生成4位长短链接
        String s = generateLink(20201122L, 4);
        System.out.println(s);
    }
}

2、Redis全局id自增

因为生成的短链接与自增id有非常密切的关系,redis的自增对于后面的分库分表有好处。只要搭建个redis集群,保证redis不挂基本上是没有问题。

代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  /**
     * redis生成全局自增ID
     */
    public long generateId(String key, int increment) {
        try {
            RedisAtomicLong counter = new RedisAtomicLong(key, redisUtil.getRedisTemplate().getConnectionFactory());
            return counter.addAndGet(increment);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return System.currentTimeMillis() + new Random(1000000).nextInt(1000000) + 1;
    }

3、分库分表

本文采用sharding-jdbc分库分表足以满足我的需求。

如果你对不是很懂,那就可以看一下官网中文使用教程:xbmchina.cn/AAAAAJ

表结构:short_url_xxx

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
CREATE TABLE `short_url_` (
  `id` bigint NOT NULL COMMENT '编号',
  `username` varchar(128) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '用户名',
  `url` varchar(512) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '长链接',
  `short_url` varchar(16) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL COMMENT '短链接',
  `client_count` int DEFAULT NULL COMMENT '点击次数',
  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
  `last_click_time` datetime DEFAULT NULL COMMENT '最新的点击时间',
  PRIMARY KEY (`id`) USING BTREE,
  KEY `short_url` (`short_url`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC;

以short_url_为模板创建a-z、A-Z 和 0-9 共 62 张表,存放短链接尾号一样的数据。注意字段short_url为utf8_bin编码区分大小写。

下面以2个库为例如何实现让数据均匀落到不同数据库对应尾号的表。

配置sharding-jdbc分库分表:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Configuration
public class DataSourceConfig {

    @Autowired
    DbProperties dbProperties;

    @Bean
    public DataSource buildDataSource() throws SQLException {

        // 配置真实数据源
        Map<String, DataSource> dataSourceMap = new HashMap<>();
        List<DsEntity> dsList = dbProperties.getDsList();
        for (int i = 0; i < dsList.size(); i++) {
            DsEntity dsEntity = dsList.get(i);
            DruidDataSource dataSource1 = new DruidDataSource();
            dataSource1.setDriverClassName(dsEntity.getDriverClassName());
            dataSource1.setUrl(dsEntity.getUrl());
            dataSource1.setUsername(dsEntity.getUsername());
            dataSource1.setPassword(dsEntity.getPassword());
            dataSourceMap.put("ds" + i, dataSource1);
        }


        // 配置short_url表规则
        TableRuleConfiguration shortUrlTableRuleConfig = new TableRuleConfiguration("short_url", "ds${0..1}.short_url_${[" +
                "'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'," +
                "'0', '1', '2', '3', '4', '5', '6', '7', '8', '9']}");
        // 配置分库 + 分表策略
        shortUrlTableRuleConfig.setDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("id", "ds${id % 2}"));
        shortUrlTableRuleConfig.setTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("short_url", new MyPreciseShardingAlgorithm()));

        // 配置min_short_url表规则
        TableRuleConfiguration minShortUrlTableRuleConfig = new TableRuleConfiguration("min_short_url", "ds${0..1}.min_short_url_${[" +
                "'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'," +
                "'0', '1', '2', '3', '4', '5', '6', '7', '8', '9']}");

        // 配置分库 + 分表策略
        minShortUrlTableRuleConfig.setDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration("id", "ds${id % 2}"));
        minShortUrlTableRuleConfig.setTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("short_url", new MyPreciseShardingAlgorithm()));

        // 配置分片规则
        ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
        shardingRuleConfig.getTableRuleConfigs().add(shortUrlTableRuleConfig);
        shardingRuleConfig.getTableRuleConfigs().add(minShortUrlTableRuleConfig);
        // 获取数据源对象
        DataSource dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig, new Properties());

        return dataSource;
    }
}

尾号分表策略:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
@Slf4j
public class MyPreciseShardingAlgorithm implements PreciseShardingAlgorithm<String> {

    /**
     * 根据短链接最后一位进行定位表名
     *
     * @param collection           表名
     * @param preciseShardingValue 列值
     * @return
     */
    @Override
    public String doSharding(Collection<String> collection, PreciseShardingValue<String> preciseShardingValue) {
        String value = preciseShardingValue.getValue();
        String flag = value.substring(value.length() - 1);
        for (String name : collection) {
            if (name.endsWith(flag)) {
                log.info("return name:" + name);
                return name;
            }
        }
        return null;
    }
}

总结

以上实现已上传到GitHub:xbmchina.cn/AAAAAI

如果对你有用或者启发的,麻烦点个赞呗。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-11-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 爱编码 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Sharding-JDBC的实践
数据分片是指按照某个维度将存放在单一数据库中的数据分散地存放至多个数据库或者表中以达到提升性能瓶颈以及可用性的效果。数据分片有效手段是对关系型数据库进行分库和分表。分表可以降低每个单表的数据阈值,同时还能够将分布式事务转化为本地事务的。分库可以有效的分散数据库单点的访问量。
码农飞哥
2021/08/18
5780
面试官:如何实现一个短链接服务?
原文链接:https://javadoop.com/post/url-shortener
cxuan
2020/12/17
2.9K0
面试官:如何实现一个短链接服务?
SpringBoot + Mybatis-Plus + Sharding-JDBC实现数据库分表以及读写分离
本文Java工程使用Maven搭建,基于SpringBoot框架,ORM框架使用Mybatis-Plus(建议自己先搭建下Demo工程)。
小小土豆dev
2024/01/19
7150
分库分表下,扩容数据免迁移方案
通过这个图,就大概可以理解业务需求了,短链平台就是将商家的长链转换为短链,商家决定向哪个平台投放广告,我们平台做出一个
Joseph_青椒
2023/08/04
8950
分库分表下,扩容数据免迁移方案
聊聊base62与tinyURL
base64大家肯定是很熟悉了,那base62是什么东东,它常被用来做短url的映射。
code4it
2018/09/17
1.9K0
短链系统设计-用户自定义短链
实现一个顾客短网址,使得顾客能创立他们自己的短网址。即你需要在前文基础上再实现一个 createCustom。
JavaEdge
2022/09/14
2.3K0
短链系统设计-用户自定义短链
如何实现一个短链接服务 | 短链接生成原理
短链接,通俗来说,就是将长的URL网址,通过程序计算等方式,转换为简短的网址字符串。
梦溪
2021/08/09
19.5K3
东半球最接地气的短链接系统设计
今天下午,烟哥和同事在厕所里排队等坑的时候(人多坑少)。想象一下一个场景,我正在一边排队,一边拿着手机撩妹。前面一个同事,拿着手机短信转过头来和我聊天。
Java3y
2019/11/12
6610
东半球最接地气的短链接系统设计
短连服务crud(第十八章/十九章/二十章/二十一章)海量数据处理-商用短链
第十八章 短链服务-业务需求和短链码解决方案讲解 第1集 短链服务介绍和应用场景讲解 简介: 短链服务介绍和应用场景讲解 什么是短链服务 业务背景:为啥需要短链 公司电商产品推广、
高大北
2022/09/23
6350
短连服务crud(第十八章/十九章/二十章/二十一章)海量数据处理-商用短链
1. 如何设计一个短链接系统
  a. 微博推文, 每次限制只能有140个字,如果连接字符很多, 那么可编辑的文字就少了
用户7798898
2020/09/27
2.2K0
1. 如何设计一个短链接系统
全网最通俗易懂的【短链接二维码】实战
昨天的文章推送中有一篇题为全网最通俗易懂的【短链接】入门, 让我觉得颇为有趣好玩,这不正好理论知识学完了,实操代码撸起来。如果有不了解的同学可以看看入门那篇的介绍,我这里直接从实战说起,代码中有超过的中文注释,让你更容易阅读理解。话不多说,上代码!
蒋老湿
2019/12/03
8850
全网最通俗易懂的【短链接二维码】实战
「System Design」设计一个短链接系统
短链接系统可以把比较长的 URL 网址转换成简短的网址字符串,短链接的优势是方便传播。适合在一些对字符串长度有要求的场景中使用,比如短信,微博等,比如
全球技术精选
2022/09/05
4390
「System Design」设计一个短链接系统
数据量大了一定要分表,分库分表Sharding-JDBC入门与项目实战
最近项目中不少表的数据量越来越大,并且导致了一些数据库的性能问题。因此想借助一些分库分表的中间件,实现自动化分库分表实现。调研下来,发现Sharding-JDBC目前成熟度最高并且应用最广的Java分库分表的客户端组件。
程序员白楠楠
2020/12/10
1.9K0
如何设计一个短链接系统
短链接是一种将长URL地址转换为较短、易于记忆的链接的技术。它通过使用特定的算法或服务将长链接压缩成更短的形式,以便在限制字符长度或需要更简洁的场景下使用。
柯柏技术笔记
2024/01/10
8740
如何设计一个短链接系统
短链接的实现
生活中,经常会在手机短信的广告中出现,因为短信服务本身对短信的长度有限制,如果使用一个非常长的链接,几百字符很快就能用完,关键信息的字符数被挤压,影响了服务方的广告价值同时也影响了消费者的观感,通过短链可以解决这个问题。
时光潜流
2023/10/22
6100
短链接的实现
面试官说:你来设计一个短链接生成系统吧
相信大家在生活中,特别是最近的双十一活动期间,会收到很多短信,而那些短信都有两个特征,第一个是几乎都是垃圾短信,这个特点此处可以忽略不计,第二个特点是**链接很短**,比如下面这个:
秦怀杂货店
2021/12/04
6240
短链接的设计与实现
短链接的实现在生活中比较常见,比如我们接受到的广告短信,短信会包含他们的活动链接。
梁规晓
2020/11/05
2.1K0
短链接的设计与实现
短链系统设计-存储设计
scalability 要求多高?存储和 qps 都不高,单机都能搞定。sql+1
JavaEdge
2022/09/14
5990
短链系统设计-存储设计
短链接的生成方式
短链接是一种 URL 简化服务, 比如:当你输入一个 URL https://www.xdull.com 时,它将返回一个简化的URL http://tinyurl.com/weuZn ,其中http://tinyurl.com/是提供服务的域名,后面的weuZn为简化后的URL的key值,通过这个key能还原成原来的真正的URL。
兜兜转转
2023/03/06
2.7K0
短链接服务Octopus的实现与源码开放
半年前(2020-06)左右,疫情触底反弹,公司的业务量不断提升,运营部门为了方便短信、模板消息推送等渠道的投放,提出了一个把长链接压缩为短链接的功能需求。当时为了快速推广,使用了一些比较知名的第三方短链压缩平台,存在一些问题:
Throwable
2020/12/29
1K0
相关推荐
Sharding-JDBC的实践
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档