首页 > 其他分享 >4 短网址设计

4 短网址设计

时间:2023-02-04 11:12:03浏览次数:67  
标签:shortURL URL 网址 62 QPS 设计 短链 id

短网址设计

Scenario

需求

Apply Short URL

graph LR A(用户) --> |http://www.jiuzhang.com| B(http://bit.ly) B --> |http://bit.ly/1UloQB6| A

根据 Short URL 还原 Long URL, 并跳转

graph LR A(用户) --> |"http://bit.ly/1UloQB6"| B(http://bit.ly) B --> |"Please redirect to www.jiuzhang.com"| A A --> |"Request"| C(http://www.jiuzhang.com)

两个需要和面试官确认的问题:

  • Long URL 和 Short URL 必须是一一对应的关系么

  • Short URL 长时间没人用需要释放么

不同的要求设计出来的系统完全不一样.

QPS

询问面试官日活跃用户: 比如weibo, 日活 100M, 假设每个用户每天发 0.1 条带分享链接的微博. Avarage Write QPS: 100M x 0.1 / 86400 = 100. Peak Write QPS = 100 x 3 = 300. 推算点击一条 Short URL 的 QPS, 假设每个用户每天平均点击 1 次, Avarage Read QPS = 100M x 1 / 86000 = 1k, Peak Read QPS = 1k x 3 = 3k.

推算每天产生的新的 URL 所占的存储: 100M x 0.1 = 10M 条. 每一条 URL 平均长度为 100 bit, 一共 1G.

Service

只用一个 URL Service. 301 Moved Permanetly, 302 Moved temporary.

算法1 使用 Hash Function 后截取

比如说取 Long URL 的 MD5 的最后 6 位, 这样肯定是有问题的, 不同的 Long URL 经过这么操作很有可能得到相同的结果.

算法2 随机生成 short URL + 数据库去重

伪代码如下:

func LongToShort(longURL string) string {
    for {
        var shortURL = randomURL()
        if !database.filter(shortURL).exists() {
            database.create(shortURL, longURL)
            return shortURL
        }
    }
}

优点: 实现简单.

缺点: 短链网址会生成的越来越多, 所以遇到冲突的情况会越来越多. 这样就可能需要循环很多次才能找到.

算法3 进制转换 Base62

将 6 位的 Short URL 看作一个 62 进制数 (0-9, a-z, A-Z). 每个 short URL 对应一个整数, 该整数对应数据库表的自增 ID, 可以作为主键也可以不做主键. 这里有一个严重的问题, 如果数据库要分片的话, 在多台服务器上维护一个全局自增 ID 很麻烦.

6 位可以表示的 URL 有多少: 62 ^ 6 = 570 亿

// 将62进制的数转换为10进制的数
func ShortURLToID(shortURL string) int {
    shortURLBytes := []byte(shortURL)
    id := 0
    for i := 0; i < len(shortURLBytes); i++ {
        id = id * 62 + toBase62(shortURLBytes[i])
    }
    return id
}

// 将10进制数转换为6位62进制的数
func IDToShortURL(id int) {
    bytes := []byte("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
    shortURL := ""
    for id > 0 {
        shortURL = bytes[id % 62] + shortURL // 向高位累加
        id = id / 62 // 下一位
    }
    for len(shortURL) < 6 { // 没到6位
        shortURL = "0" + shortURL // 在高位占位
    }
    return shortURL
}

优点: 效率高. 缺点: 依赖于全局的递增ID, 只能依赖于单个表, 无法扩展.

Storage

最重要的部分!!!

SQL vs NoSQL 到底怎么选呢?

  • 不需要支持事务: NoSQL + 1

  • 不需要丰富的 SQL query: NoSQL + 1

  • NoSQL 实现并不复杂: NoSQL + 1

  • QPS 不高: SQL + 1

Scale

提高响应速度

这是一个读多写少的系统, 所以使用 Cache 来进行优化. Cache 中存储着两种数据, 一种是 long to short , 在生成 short url 之前看看之前是否已经生成过了. 一种是 short to long, 查询 short url 对应的 long url.

利用地理位置加速: 使用 DNS 使得不同地区 Web Server 不一样, 将缓存分布在各个地区.

面试官说, 单机 SQL 搞不定了

什么时候需要多台 MySQL

  • Cache 资源可能不够了

  • 写操作越来越多

增加多台 MySQL 可以优化什么:

  • 解决存不下的问题: Storage

  • 解决忙不过的问题: QPS

短链系统明显是忙不过来的问题.

那如何扩展呢?

  • 垂直拆分: 字段就两个, 不能拆分了

  • 水平拆分:

    • 如果用 ID/ShortURL 做 Sharding Key

      • 长链转换为短链的时候需要广播给 N 台数据库来查询, 因为创建的时候需要避免重复创建.

      • 短链转换为长链只需要用 ShortURL 来 hash 就行. 这样就查找一台 MySQL 就能找到数据.

    • 如果用 Long URL 做 Sharding Key

      • 短链转换为长链的时候需要广播给 N 台数据库来查询

      • 长链转换为短链的时候只需要用 LongURL 来 hash 就行. 这样查找一台 MySQL 就能找到数据.

选 Sharding Key

如果一个 LongURL 可以对应多个 ShortURL, 那么直接用 ShortURL 即可, LongURL 在创建的过程中不需要保证一一对应.

如果一个 LongURL 只能对应一个 ShortURL:

  • 如果使用随机生成的 Short URL 的算法: 选 ShortURL 吧, 因为短链转换为长链的需求肯定大于长链转换为短链

  • 如果使用 Bash62 进制转换法: 这里有个非常严重的问题, 如何在多台机器上维护一个全局自增 ID?

在多台机器上维护一个全局的自增 ID, 可以使用 Etcd, Zookeeper 等分布式 k-v 数据库.

使用 Hash(LongURL) % 62 作为 Sharding Key. 并将 Hash(LongURL) % 62 直接放入到 shortURL 的最高位上, 将其变为 7 位. 这样我们在查找的时候无论是 shortToLong 还是 longToShort 都可以得到 Sharding Key 从而找到具体的位置.

shortURL 最高位即是 Sharding Key, hash(longURL) % 62 即是 Sharding Key.

这种做法的缺点是机器的数目 <= 62.

还有可以优化的么

可以根据地域来优化

用户自定义 URL

Base62: 不能在 URL Table 里面新增一个字段, 因为这样会导致空间大量浪费. 可以新增一个表.

随机生成: 完全兼容, 直接插入就好.

标签:shortURL,URL,网址,62,QPS,设计,短链,id
From: https://www.cnblogs.com/geraldkohn/p/17091099.html

相关文章

  • Java程序设计基础复习题
    Java程序设计基础1-6一、Java语言概述1.Java语言有哪些特点?简单易学。Java去掉了C/C++语言支持的单个不易理解和掌握的数据类型(指针pointer、联合体unions、结构体stru......
  • 深度剖析 Linux 伙伴系统的设计与实现
    在上篇文章《深入理解Linux物理内存分配全链路实现》中,笔者为大家详细介绍了Linux内存分配在内核中的整个链路实现:但是当内核执行到get_page_from_freelist函数,......
  • 设计模式
    一、单例模式因为在编程开发中经常会遇到这样⼀种场景,那就是需要保证⼀个类只有⼀个实例哪怕多线程同时访问,并需要提供⼀个全局访问此实例的点。综上以及我们平常的开......
  • day03-203.移除链表元素|707.设计链表|206.反转链表
    203.移除链表元素leetcode题目:https://leetcode.cn/problems/remove-linked-list-elements/题目描述:给你一个链表的头节点head和一个整数val,请你删除链表中所有满足......
  • 亲身开发一个Windows软件(三)界面设计
    现在,我们已经准备好了,要开始开发了。首先我们要进行界面设计。从某种方面来说,这比写代码更重要,因为用户只会感觉到界面,而不会在乎代码,所以一个好的用户界面,是一个优秀软件......
  • 《Vue.js 设计与实现》读书笔记 - 第8章、挂载与更新
    第8章、挂载与更新8.1挂载子节点和元素的属性扩展子元素的类型可以为数组,并判断如果是数组的话,就先依次挂载所有的子元素。同时新增节点属性。属性可以通过el.setAttr......
  • LeetCode刷题,代码随想录算法训练营Day3| 链表理论基础 203.移除链表元素 707.设计链
    链表理论基础链表是通过指针串联在一起的线性结构,每个节点由一个数据域和一个指针域构成。链表的类型单链表双链表有两个指针域,一个指向下一个节点,一个指向上一个节......
  • 【RUST程序设计语言】第八章 常见集合练习题 Pig Latin
    题目摘录:给定一系列数字,使用vector并返回这个列表的中位数(排列数组后位于中间的值)和众数(mode,出现次数最多的值;这里哈希map会很有帮助)。将字符串转换为PigLatin,也......
  • 如何设计可向后兼容的RPC协议
    HTTP协议(本文HTTP默认1.X)跟RPC协议又有什么关系呢?都属于应用层协议。1HTTP协议浏览器收到命令后会封装一个请求,并把请求发送到DNS解析出来的IP上,抓包:2协议的作用没有协议......
  • 【Spring事物三千问】DataSource的设计和常用实现——Hikari、Druid
    javax.sql.DataSourcejavax.sql.DataSource是jdk提供的接口,各个连接池厂商和Spring都对DataSource进行了设计和实现。javax.sql.DataSource是连接到物理数据源的......