现在的各种App如脉脉、微博基本不会让你发太长的网址,会给你转成短链。在面试的时候也经常会被问到如何实现一个支持高并发的短链接系统。
根据一个 long url 生成一个short url。
如 http://www.javadaily.cn => http://bit.ly/1ULoQB6
根据 short url 还原 long url,并跳转:
需和面试官确认的问题:
long url和short url必须一一对应吗?
Short url长时间没人用,需要释放吗?
1 . 问日活,如微博100M
2 . 推算产生一条 tiny url 的 qps
a . 假设每个用户平均每天 0.1(发10 条,有一条有链接) 条带 URL 的微博
b . 平均写 QPS = 100M * 0.1 / 86400 = 100
c . 峰值写 qps = 100 * 2 = 200
3 . 推算点击一条tiny url的 qps
a . 假设每个用户平均点 1 个tiny url
b . 平均写 QPS = 100M * 1 / 86400 = 1k
c . 峰值读 qps = 1k * 2 = 2k
4 . deduce 每天产生的新 URL 所占存储
a . 100M * 0.1 = 10M 条
b . 每条 URL 长度平均按 100 算,共 1G
c . 1T 硬盘能用 3 年
由2、3 分析可知,并不需要分布式或者 sharding,支持 2k QPS,一台 SSD MySQL 即可。
该系统其实很简单,只需要有一个 service即可:URL Service。由于 tiny url只有一个 UrlService:
UrlService.encode(long_url):编码方法
UrlService.decode(long_url):解码方法
访问端口设计,当前有如下两种常用主流风格:
Return a http redirect resonse
Return a short url
那么,你们公司的短链系统是选择哪种服务设计呢?
需要事务吗?No,nosql+1
需要丰富的 sql query 吗?no,nosql+1
想偷懒吗?tiny url需要写的代码不复杂,nosql+1
qps高吗?2k,不高。sql+1
scalability 要求多高?存储和 qps 都不高,单机都能搞定。sql+1
- sql 需要自己写代码来 scale
- nosql,这些都帮你做了
是否需要 sequential ID?取决于你的算法
long ur 转成一个 6 位的 short url。给出一个长网址,返回一个短网址。
实现两个方法:
longToShort(url)
把一个长网址转换成一个以http://tiny.url/
开头的短网址shortToLong(url)
把一个短网址转换成一个长网址标准:
[a-zA-Z0-9]
. 比如: abcD9E
用两个哈希表:
短网址是固定的格式: "http://tiny.url/" + 6个字符, 字符可任意。
为避免重复, 我们可以按照字典序依次使用, 或者在随机生成的基础上用一个集合来记录是否使用过。
如取 long url的 MD5 的最后 6 位:
随机取一个 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;
}
}
优点:实现简单
缺点:生成短链接的速度,随着短链接越多而越慢
关系型数据库表:只需Short key和 long url两列,并分别建立索引
也可使用 nosql,但需要建立两张表:
Base62:
6 位可表示的不同 URL:
优点:效率高
缺点:强依赖于全局的自增 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);
}
}
因为要用到自增 id,所以只能用关系型 DB 表:
id主键、long url(索引)
如何提高响应速度,和直接打开原链接一样的效率。
明确,这是个读多写少业务。
缓存需存储两类数据:
利用地理位置信息提速。
优化服务器访问速度:
优化数据访问速度
cache 资源不够或命中率低
写操作过多
越来越多请求无法通过 cache 满足
多台DB服务器可以优化什么?
那么 tiny url 的主要问题是啥?存储是没问题的,重点是 qps。那么,如何 sharding 呢?
垂直拆分:将多张表分别分配给多台机器。对此不适用,只有两列,无法再拆分。
横向拆分:
若id、shortURL 做分片键:
用 long url 做分片键:
short2long 查询时,只能广播给 N 台 DB 查询。
若使用随机生成算法
两张表,一张存储 long2short,一张存储short2long
每个映射关系存两份,则能同时支持 long2short short2long 查询
若使用 base62 进制转换法
有个严重问题,多台机器之间如何维护一个全局自增的 id?
一般关系型DB只支持在一台机器上实现这台机器上全局自增的 id
该 DB不存储真实数据,也不负责其他查询。
为避免单点故障,可能需要多台 DB。
但使用全局自增 id 不是解决 tiny url最佳方案。Generating a Distributed Sequence Number
Hash(long_url)%62作为分片键
并将 hash(long_url)%62直接放到 short url
若原来的 short key 是 AB1234,则现在的 short key 是
这样,就能同时通过 short、long 得到分片键。
缺点:DB 的机器数目不能超过 62。
所以,最后最佳架构:
web server 和 database 之间的通信。
中心化的服务器集群和跨地域的 web server 之间通信较慢:如中国的 Server 需访问美国的 DB。
为何不让中国的 Server 访问中国的 DB 呢?
若数据重复写到中国 DB,如何解决一致性问题?很难解决!
思考用户的习惯:
中国用户访问时,会被 DNS 分配中国的服务器
中国用户访问的网站一般都是中国的网站
所以可按网站的地域信息来 sharding
如何获得网站的地域信息?只需将用户常访问的网站汇总在一张表。
中国用户访问美国网站咋办?
就中国 server 访问美国 db,也不会慢太多
中访中是用户主流,优化系统就是针对主要需求
于是,得到最终架构:
还可以维护一份域名白名单,访问对应地域的 DB。
实现一个顾客短网址,使得顾客能创立他们自己的短网址。即你需要在前文基础上再实现一个 createCustom
。
需实现三个方法:
long2Short(url)
把一个长网址转换成一个以http://tiny.url/
开头的短网址short2Long(url)
把一个短网址转换成一个长网址createCustom(url, key)
设定一个长网址的短网址为 http://tiny.url/
+ key
注意:
long2Short
生成的短网址的key的长度应该等于6 (不算域名和反斜杠)。可以使用的字符只有 [a-zA-Z0-9]
。如: abcD9E
createCustom
不能完成用户期望的设定, 那么应该返回 "error"
, 反之如果成功将长网址与短网址对应,应该返回这个短网址在URLTable里,直接新增一列custom_url记录对应的custom url是否可行?
不可行!对于大部分数据,该列其实都为空,就会浪费存储空间。
新增一个表,存储自定义 URL:CustomURLTable。
创建自定义短链接:在 CustomURLTable 中查询和插入
根据长链接创建普通短链接:
同前文一样,用两个哈希表处理长网址和短网址之间的相互映射关系。需额外处理的是用户设定的网址与已有冲突时,需返回 "error"。注意:若用户设定的和已有恰好相同,则同样应该返回短网址。
public class TinyUrl2 {
private HashMap<String,String> s2l = new HashMap<String,String>();
private HashMap<String,String> l2s = new HashMap<String,String>();
private int cnt = 0;
private final StringBuffer tinyUrl = new StringBuffer("http://tiny.url/");
private final String charset = "qwertyuiopasdfghjklzxcvbnm1234567890QWERTYUIOPASDFGHJKLZXCVBNM";
private String newShortUrl() {
StringBuffer res = new StringBuffer();
for (int i = 0, j = cnt; i < 6; i++, j /= 62)
res.append(charset.charAt(j % 62));
cnt++;
return tinyUrl + res.toString();
}
/*
* @param long_url: a long url
* @param key: a short key
* @return: a short url starts with http://tiny.url/
*/
public String createCustom(String long_url, String key) {
String short_url = tinyUrl + key;
if (l2s.containsKey(long_url)) {
if (l2s.get(long_url).equals(short_url))
return short_url;
else
return "error";
}
if (s2l.containsKey(short_url))
return "error";
l2s.put(long_url, short_url);
s2l.put(short_url, long_url);
return short_url;
}
/*
* @param long_url: a long url
* @return: a short url starts with http://tiny.url/
*/
public String longToShort(String long_url) {
if (l2s.containsKey(long_url))
return l2s.get(long_url);
String short_url = newShortUrl();
l2s.put(long_url, short_url);
s2l.put(short_url, long_url);
return short_url;
}
/*
* @param short_url: a short url starts with http://tiny.url/
* @return: a long url
*/
public String shortToLong(String short_url) {
if (s2l.containsKey(short_url))
return s2l.get(short_url);
return "error";
}
}
无需做任何改动,直接把custom url当short url创建即可!
本文由哈喽比特于2年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/gRUTAassGHVFcZrUq1xFZQ
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。