SWE · OOD / System Design

TikTok Short URL:Class 接口、短码唯一性与分布式取舍

从shorten/resolve合同出发,区分哈希碰撞、数据库唯一约束和不可猜短码,推导存储与读扩展。

题目重点:从shorten/resolve合同出发,区分哈希碰撞、数据库唯一约束和不可猜短码,推导存储与读扩展。

题意与假设

这道练习题保留了面经中值得训练的核心约束,并将需要澄清的条件单独列出。现场拿到变体时,先和面试官确认这些条件。

  • 训练接口shorten(longUrl,requestId)返回短码,resolve(code)返回长URL;仅接受HTTP(S),不主动抓取目标内容。
  • 默认每次新请求可创建新短码,同一requestId重复必须返回同结果;是否同长URL自动去重由业务另定。
  • 不假设固定QPS、保留年限、TTL或自定义alias;容量算式用变量及明确示例。
  • 本篇先给类职责与生产存储合同,无唯一的公司指定短码策略;防猜与过期需求需面试时确认。

解题路线

  1. 类可把生成器、存储库和校验器注入,shorten负责校验、查幂等、分配code、事务插入,resolve只按code读状态和longUrl。不要把hash(longUrl)直接作为无需存储的可逆映射:有限短码无法恢复任意原URL。
  2. 表结构可为links(code PK,long_url,created_at,expires_at,status,owner_id),另有requests(owner_id,request_id,payload_hash,code)唯一键。后者将幂等范围与账号绑定;同键不同URL应报冲突,不能静默返回另一条链接。
  3. 单机递增ID经base62编码是确定唯一映射,code容量为62^L;无需哈希碰撞重试,但号码可被枚举,且扩容要解决ID分配。可以按业务接受枚举,或选安全随机码与数据库唯一索引,不能把编码当加密。
  4. 随机码必须以唯一索引作为最终仲裁。先查不存在再insert有并发竞态;正确做法生成后直接尝试插入,冲突重试并设有界失败策略。近似碰撞概率随累计创建数平方增长,不因每个字符随机便为零。
  5. 分布式唯一ID可由数据库序列、分段租赁或带节点/时间字段生成。序列简单却可能成写瓶颈;分段减少协调但允许空洞;时钟型方案必须处理节点冲突和时钟回退。选型要对齐创建速率、可猜性与故障合同。
  6. 长URL去重不能只拿截断hash当身份,碰撞时仍应比较规范化后原文或保留冲突桶;查询参数顺序、尾斜杠、fragment能否归一化由业务定义。不同用户所有权和过期策略也可能意味着同URL应保留不同记录。
  7. redirect读路径按code查缓存/数据库,成功返回Location;选择临时或永久重定向要考虑后续是否能改目标、过期或撤销,因为客户端/中间缓存可能绕过后来服务端校验。不存在、过期和禁用状态分开定义。
  8. 热点短链可缓存、CDN或读副本扩展,但禁用与过期需控制缓存TTL和失效传播;最终一致副本可能让刚创建的短链短暂404,可用主库回退或read-your-writes处理。恶意URL、枚举和扫描的防护不能用短码唯一性替代。
  9. 容量设创建速率w、保留秒数T、每条含索引字节b,则活跃存储约wTb,读流量按resolve QPS估计。示例100次/秒保留一年约31.5亿条,不是100次/秒乘一天;副本、索引、幂等记录另计。

手动走一遍

两个并发请求生成同code:唯一索引只允许一个成功,另一方重新生成,而不是互相覆盖long_url。请求A创建成功但响应丢失,重试同requestId应返回原code。若改用顺序ID编码,ID 1与2天然不同,但访问者可能猜出邻近code,这与碰撞是两个独立问题。

规模与取舍

code点查在哈希型内存索引平均O(1),数据库B树通常O(log N)且受IO影响;URL验证/哈希至少O(U),U为URL长度。总存储O(N·(U+元数据+索引)),不能只计几位短码;随机重试的期望成本依赖码空间占用率。

易错点与追问

容易失分的边界

  • hash冲突后覆盖原URL。
  • 先查再写却没有唯一约束。
  • 把base62说成加密或防枚举。
  • 永久缓存策略与可撤销短链冲突。

继续深挖

  • 自定义alias怎样抢占并保证所有权?
  • 要求不可猜code选多大空间?
  • 链接撤销怎样同步边缘缓存?
  • 多区域写怎样确保唯一与幂等?
✓已复制微信号!