• 短链系统设计-存储设计


    3 Storage 数据存取(最能体现实践经验)

    1. select 选存储结构
    2. scheme 细化数据表

    3.1 SQL V.S NoSQL

    需要事务吗?No,nosql+1

    需要丰富的 sql query 吗?no,nosql+1

    想偷懒吗?tiny url需要写的代码不复杂,nosql+1

    qps高吗?2k,不高。sql+1

    scalability 要求多高?存储和 qps 都不高,单机都能搞定。sql+1

    - sql 需要自己写代码来 scale
    - nosql,这些都帮你做了
    
    • 1
    • 2

    是否需要 sequential ID?取决于你的算法

    • sql 提供 auto_increment 的 sequencetial ID,即 1,2,3
    • nosql 的 ID 不是 sequential

    3.2 算法

    long ur 转成一个 6 位的 short url。给出一个长网址,返回一个短网址。

    实现两个方法:

    • longToShort(url) 把一个长网址转换成一个以http://tiny.url/开头的短网址
    • shortToLong(url) 把一个短网址转换成一个长网址

    标准:

    1. 短网址的key的长度应为6 (不算域名和反斜杠)。 可用字符只有 [a-zA-Z0-9]. 比如: abcD9E
    2. 任意两个长的url不会对应成同一个短url,反之亦然。

    用两个哈希表:

    • 一个是短网址映射到长网址
    • 一个是长网址映射到短网址

    短网址是固定的格式: “http://tiny.url/” + 6个字符, 字符可任意。

    为避免重复, 我们可以按照字典序依次使用, 或者在随机生成的基础上用一个集合来记录是否使用过。

    使用哈希函数(不可行)

    如取 long url的 MD5 的最后 6 位:

    • 难以设计一个无哈希冲突的哈希算法
    随机生成 shortURL+DB去重

    随机取一个 6 位的 shortURL,若没使用过,就绑定到改 long url。

    public String long2Short(String url) {
      while(true) {
        String shortURL = randomShortURL();
        if (!databse.filter(shortURL=shortURL).exists()) {
          database.create(shortURL=shortURL, longURL=url);
          return shortURL;
        }
      }
      
    }
    
    public class TinyUrl {
        
        public TinyUrl() {
            long2Short = new HashMap<String, String>();
            short2Long = new HashMap<String, String>();
        }
    
        /**
         * @param url a long url
         * @return a short url starts with http://tiny.url/
         */
        public String longToShort(String url) {
            if (long2Short.containsKey(url)) {
                return long2Short.get(url);
            }
    
            while (true) {
                String shortURL = generateShortURL();
                if (!short2Long.containsKey(shortURL)) {
                    short2Long.put(shortURL, url);
                    long2Short.put(url, shortURL);
                    return shortURL;
                }
            }
        }
    
        /**
         * @param url a short url starts with http://tiny.url/
         * @return a long url
         */
        public String shortToLong(String url) {
            if (!short2Long.containsKey(url)) {
                return null;
            }
    
            return short2Long.get(url);
        }
    
        private String generateShortURL() {
            String allowedChars = "0123456789" + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    
            Random rand = new Random();
            String shortURL = "http://tiny.url/";
            for (int i = 0; i < 6; i++) {
                int index = rand.nextInt(62);
                shortURL += allowedChars.charAt(index);
            }
    
            return shortURL;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62

    优点:实现简单

    缺点:生成短链接的速度,随着短链接越多而越慢

    关系型数据库表:只需Short key和 long url两列,并分别建立索引

    也可使用 nosql,但需要建立两张表:

    • 根据 long 查询 short
      key=longurl 列=shorturl value=null or timestamp
    • 根据 short 查询 long
      key=shorturl 列=longurl value=null or timestamp

    进制转换 Base32(微博实现方案)

    Base62:

    • 将 6 位 short url 看做一个 62 进制数(0-9,a-z,A-Z)
    • 每个 short url 对应到一个整数
    • 该整数对应 DB 表的主键

    6 位可表示的不同 URL:

    • 5 位 = 62^5=0.9B= 9亿
    • 6 位 = 62^6=57B= 570亿
    • 7 位 = 62^7=3.5T= 35000亿

    优点:效率高

    缺点:强依赖于全局的自增 id

    public class TinyUrl {
        public static int GLOBAL_ID = 0;
        private HashMap<Integer, String> id2url = new HashMap<Integer, String>();
        private HashMap<String, Integer> url2id = new HashMap<String, Integer>();
    
        private String getShortKey(String url) {
            return url.substring("http://tiny.url/".length());
        }
    
        private int toBase62(char c) {
            if (c >= '0' && c <= '9') {
                return c - '0';
            }
            if (c >= 'a' && c <= 'z') {
                return c - 'a' + 10;
            }
            return c - 'A' + 36;
        }
    
        private int shortKeytoID(String short_key) {
            int id = 0;
            for (int i = 0; i < short_key.length(); ++i) {
                id = id * 62 + toBase62(short_key.charAt(i));
            }
            return id;
        }
    
        private String idToShortKey(int id) {
            String chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
            String short_url = "";
            while (id > 0) {
                short_url = chars.charAt(id % 62) + short_url;
                id = id / 62;
            }
            while (short_url.length() < 6) {
                short_url = "0" + short_url;
            }
            return short_url;
        }
    
        /**
         * @param url a long url
         * @return a short url starts with http://tiny.url/
         */
        public String longToShort(String url) {
            if (url2id.containsKey(url)) {
                return "http://tiny.url/" + idToShortKey(url2id.get(url));
            }
            GLOBAL_ID++;
            url2id.put(url, GLOBAL_ID);
            id2url.put(GLOBAL_ID, url);
            return "http://tiny.url/" + idToShortKey(GLOBAL_ID);
        }
    
        /**
         * @param url a short url starts with http://tiny.url/
         * @return a long url
         */
        public String shortToLong(String url) {
            String short_key = getShortKey(url);
            int id = shortKeytoID(short_key);
            return id2url.get(id);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64

    因为要用到自增 id,所以只能用关系型 DB 表:

    id主键、long url(索引)

  • 相关阅读:
    MCE | 免疫检查点大组团
    Sentinel控制台配置 持久化到nacos
    非技术人员也能快速实现Wordpress网页开发
    实时测试工具 Visual Studio 扩展 NCrunch 4.18 Crack
    Azure Kubernetes Service中重写规则踩坑小记录
    手绘板的制作——画布保存(6)
    浅谈微服务-SpringCloud以及框架发展史
    【k8s学习2】二进制文件方式安装 Kubernetes之etcd集群部署
    Vue的生命周期的详解
    Redis与分布式:哨兵模式
  • 原文地址:https://blog.csdn.net/qq_33589510/article/details/126834017