过程如下:
-
浏览器发起请求: 你的浏览器输入一个短网址\比如
http://abcd
\然后向服务器发起一个请求。 -
短链接服务查找: 短链接服务收到这个请求后\会在数据库(这里是 MySQL)中查找这个短网址对应的长网址。它会执行一个查询语句:
SELECT * FROM url_map WHERE short_url = abcd
。 -
这意味着它在
url_map
表中查找short_url
列的值为abcd
的记录。 为了加快查找速度\数据库在short_url
列上建立了一个索引 (idx_short_uri
)。 -
数据库返回结果: 数据库查找到对应的记录后\会把结果返回给短链接服务。这个结果包含了短网址对应的长网址。
-
重定向: 如果短链接服务在数据库中找到了对应的长网址、它会给你的浏览器返回一个状态码为
302 Found
的响应。这个响应里会包含一个Location
头、这个头的值就是原始的长网址。 -
浏览器重定向: 你的浏览器收到
302 Found
响应后、会自动根据Location
头里的长网址、发起一个新的请求。 -
原始地址服务: 浏览器现在请求的是原始的长网址对应的服务器。
-
返回响应内容: 原始地址服务处理这个请求、然后把网页的内容返回给你的浏览器。
如何将一个长的URL地址转换成一个短的URL地址的过程。
-
浏览器提交长链接: 用户通过浏览器、以POST请求的方式、将一个长URL地址(
http://xxxxxxxx
)提交给短链接服务。请求的URL可能是https://xxx/shorten
,并且长URL作为数据(data
)一起发送。 -
短链接服务查找: 短链接服务收到长URL后、会在数据库(这里是 MySQL)中查找是否已经存在这个长URL对应的短链接。它会执行一个查询语句:
SELECT * FROM url_map WHERE long_url = '
http://xxxxxxxx
'
。为了加快查找速度,数据库在long_url
列上建立了一个索引 (idx_long_url
)。 -
数据库返回查询结果: 数据库返回查询结果给短链接服务。
-
判断是否存在短链接: 短链接服务根据数据库返回的结果判断、这个长URL是否已经有对应的短链接。
-
如果存在: 直接返回已有的短链接给浏览器。
-
如果不存在: 使用某种算法生成一个新的短链接、并将长URL和新生成的短链接对应关系插入到数据库的
url_map
表中。
-
-
返回短链接地址: 短链接服务将生成的短链接地址返回给浏览器。
面试参考回答
面试官:请你谈谈如何设计一个短链接系统
我: 面试官您好、我来说一下如何设计一个短链接系统。
我的思路是这样的:在开始设计之前、我认为最重要的是需求对齐。我们需要明确以下几个关键问题:
-
QPS(每秒查询率): 生成短链的 QPS 和访问短链的 QPS 大概是多少?
-
容量: 预计需要存储多少短链?
-
对应关系: 一个长链接是否允许对应多个短链接?
-
有效期: 短链是否需要设置过期时间?
了解这些信息可以帮助我们更好地评估系统的复杂度和风险、并选择合适的技术方案。例如:
-
存储方案选择: 如果访问短链的 QPS 只有几千,单机 MySQL 可能就足够了。如果达到几万甚至更高,就需要考虑 MySQL 集群 + Redis 缓存的方案。
-
短链长度选择: 如果预计需要存储 1 亿条短链,那么使用 Base62 编码,5 位短链就足够了。
在明确了需求之后、我会进行难点分析。我认为短链接系统主要面临以下几个难点:
-
短链生成算法: 如何设计一个高效、唯一的短链生成算法?
-
高并发: 如何应对高并发的短链访问请求?
-
数据一致性: 如何保证长链接和短链接的映射关系的一致性?
首先、我会从整体架构入手。短链接系统可以分为以下几个层次:
-
接入层: 负责接收用户的请求、进行初步的校验和限流。
-
服务层: 处理核心的业务逻辑、例如短链生成、短链解析等。
-
数据层: 负责数据的存储和访问、例如 MySQL、Redis 等。
每一层都可以独立扩展和优化,降低系统的耦合度。
然后我会考虑关键技术点。我认为以下几个技术点在短链接系统中至关重要:
-
短链生成算法: 我会选择 Base62 编码。具体来说、我会使用自增 ID 作为长链接的唯一标识、然后将自增 ID 转换为 Base62 编码、得到短链。
-
面试官可能会问:为什么选择 Base62 编码?
-
我: 因为 Base62 编码可以有效地缩短短链的长度、并且可以保证短链的唯一性。
-
-
缓存: 为了提高短链访问速度,我会使用 Redis 缓存热点短链和长链接的映射关系。
-
面试官可能会问:如果 Redis 挂了怎么办?
-
我: Redis 的可靠性确实不如 MySQL。为了应对这种情况,我会采取以下措施:
-
持久化: 开启 Redis 的持久化功能、例如 RDB 或 AOF,防止数据丢失。
-
主从复制: 使用 Redis 主从复制、提高系统的可用性。
-
最终一致性: 即使 Redis 出现故障、导致少量短链无法访问,也可以通过后续的补偿机制来解决。
-
-
-
数据库设计: 我会创建一个名为
url_map
的表,包含以下字段:-
id
:自增主键,bigint unsigned 类型。 -
long_url
:长链接,varchar(255) 类型、需要建立唯一索引。 -
short_url
:短链接,varchar(10) 类型、需要建立唯一索引。
-
接下来我会详细描述短链访问流程:
-
用户访问短链接。
-
接入层接收请求,进行初步的校验和限流。
-
服务层首先查询 Redis 缓存、如果缓存命中、直接返回长链接。
-
如果缓存未命中,查询 MySQL 数据库、如果查询到长链接、更新 Redis 缓存并返回长链接。
-
如果数据库中也未查询到长链接、返回 404 错误。
最后我谈谈难点与解决方案:
-
高并发: 通过限流、缓存等手段缓解压力。
-
数据一致性: 通过 Redis 缓存和 MySQL 数据库的配合、保证数据的一致性。
-
短链生成算法: 选择合适的短链生成算法、保证短链的唯一性和高效性。
总的来说设计短链接系统需要综合考虑存储、算法、性能、扩展性等多个方面。通过合理的技术选型和架构设计、可以构建一个高性能、高可用的短链接系统。
我认为还有以下几个可以优化的点:
-
CDN 加速: 使用 CDN 加速静态资源的访问、例如短链页面。
-
监控: 实时监控系统性能、及时发现和解决问题。
-
弹性伸缩: 根据流量情况动态调整服务器资源。
这些优化措施可以进一步提高短链接系统的性能和可用性。
面试对话版本
下面我将把上面的内容转换成一个更自然的面试对话、并加入一些互动和追问的环节。
面试官:请你谈谈如何设计一个短链接系统
我: 好的面试官。在开始设计之前、我想先和您确认一些关键的需求、这样可以帮助我更好地理解问题的范围和重点。
面试官:好的没问题。
我: 首先、我想了解一下这个短链接系统预计的 QPS(每秒查询率)大概是多少?包括生成短链的 QPS 和访问短链的 QPS。这个数据对我们选择存储方案和缓存策略非常重要。
面试官:嗯,假设生成短链的 QPS 大概在 1000 左右、访问短链的 QPS 大概在 10000 左右。
我: 好的明白了。另外,您预计我们需要存储多少条短链、这个数据会影响我们选择短链的长度(位数)。
面试官:嗯预计需要存储 1 亿条短链。
我: 好的。还有一个问题、一个长链接是否允许对应多个短链接?
面试官:不允许、我们希望一个长链接只对应一个短链接。
我: 好的明白了。最后一个问题、短链是否需要设置过期时间?
面试官:暂时不需要。
我: 好的感谢您提供的信息。基于这些信息、我的设计思路是这样的:
架构设计:
我会设计一个 URLService
,负责短链的生成和解析。它主要有两个方法:encode(long_url)
用于将长链接编码为短链接,decode(short_url)
用于将短链接解码为长链接。
面试官:嗯这个服务的设计很清晰。那你会如何设计 API 接口呢?
我: 我会设计两个 API 接口:
-
POST /shorten
:接收长链接、返回短链接。请求体是 JSON 格式、包含long_url
字段,响应也是 JSON 格式,包含short_url
字段。 -
GET /{short_url}
:接收短链接、然后 302 重定向到长链接。
面试官:302 重定向、为什么选择 302 而不是 301 呢
我: 这是一个好问题。我选择 302 是因为 302 是临时重定向、这意味着搜索引擎不会将短链接替换为长链接,有利于保护长链接的权重。而 301 是永久重定向、搜索引擎会认为短链接就是长链接、可能会影响长链接的 SEO。
面试官:嗯,考虑得很周全。那在存储方面、你会如何选择呢?
我: 考虑到访问短链的 QPS 较高(10000 左右)、我会选择 MySQL 集群 + Redis 缓存的方案。MySQL 用于持久化存储长链接和短链接的映射关系、Redis 用于缓存热点数据,提高访问速度。
面试官:那你能详细说说 MySQL 的表结构吗?
我: 当然。我会创建一个名为 url_map
的表,包含以下字段:
-
id
:自增主键、bigint unsigned
类型。 -
long_url
:长链接,varchar(255) 类型、需要建立唯一索引。 -
short_url
:短链接,varchar(10) 类型、需要建立唯一索引。
面试官:为什么 long_url
和 short_url
都要建立唯一索引呢
我: 建立唯一索引是为了保证数据的唯一性、避免重复生成短链。同时索引可以提高查询效率、无论是根据长链接查找短链接、还是根据短链接查找长链接、都可以快速定位到数据。
面试官:那 Redis 缓存你会如何设计呢
我: 我会在 Redis 中存储两类数据:
-
short_url -> long_url
:用于短链到长链的快速查找。 -
long_url -> short_url
:用于长链到短链的快速查找,避免重复生成短链。
面试官:嗯这个设计很合理。那你会选择什么样的短链生成算法呢
我: 我会选择 Base62 编码。具体来说、我会使用自增 ID 作为长链接的唯一标识,然后将自增 ID 转换为 Base62 编码、得到短链。
面试官:为什么选择 Base62 编码呢
我: 因为 Base62 编码可以有效地缩短短链的长度。使用 5 位 Base62 编码就可以表示 62^5 ≈ 9.16 亿条短链,足够满足我们 1 亿条短链的需求。
面试官:如果自增 ID 达到了上限怎么办
我: 这是一个好问题。如果自增 ID 达到了上限、我们可以考虑以下方案:
-
更换 ID 类型: 将 ID 类型从
int
改为bigint
、可以扩大 ID 的范围。 -
分段生成: 将 ID 分成多个段,每个段都有自己的自增起始值。
-
使用其他 ID 生成算法: 例如
Snowflake
算法。
面试官:嗯考虑得很全面。那如果系统 QPS 很高,你会如何优化呢?
我: 我会从以下几个方面进行优化:
-
缓存: 使用 Redis 缓存热点短链和长链的映射关系、提高访问速度。
-
CDN: 使用 CDN 加速静态资源的访问、例如短链页面。
-
负载均衡: 使用负载均衡器将请求分发到多个服务器、提高系统的并发处理能力。
-
数据库优化: 对 MySQL 数据库进行优化、例如使用索引、优化 SQL 语句、读写分离等。
面试官:如果 Redis 挂了怎么办
我: Redis 的可靠性确实不如 MySQL。为了应对这种情况,我会采取以下措施:
-
持久化: 开启 Redis 的持久化功能、例如 RDB 或 AOF、防止数据丢失。
-
主从复制: 使用 Redis 主从复制,提高系统的可用性。
-
最终一致性: 即使 Redis 出现故障,导致少量短链无法访问,也可以通过后续的补偿机制来解决。
面试官:你对短链接系统设计有什么总结吗?
我: 总的来说,设计短链接系统需要综合考虑存储、算法、性能、扩展性等多个方面。通过合理的技术选型和架构设计、可以构建一个高性能、高可用的短链接系统。