哈希算法题目:设计一个基于哈希的分布式短链生成与解析系统(支持高并发和防冲突)
字数 1860 2025-12-07 09:38:08
哈希算法题目:设计一个基于哈希的分布式短链生成与解析系统(支持高并发和防冲突)
题目描述
设计一个短链服务,能够将任意长URL(字符串)映射为一个全局唯一的、固定长度的短字符串(短链),并能通过短链快速解析回原始URL。系统需支持高并发的生成与查询请求,并确保不同URL映射到相同短链的概率(哈希冲突)极低。同时,需考虑短链的字符空间(如仅包含数字和字母)和长度限制(例如7位)。请给出核心算法与数据结构设计,并解释如何解决分布式环境下的并发与冲突问题。
解题过程
第一步:明确需求与约束
- 功能:
generate(longUrl: str) -> str:输入长URL,返回短链(如https://short.url/abc12De)。resolve(shortKey: str) -> str:输入短链的键(如abc12De),返回原始长URL,若不存在则返回空或错误。
- 核心要求:
- 短链键(如
abc12De)需全局唯一,且固定长度(如7位)。 - 高并发下生成短链需高效且避免冲突。
- 原始URL与短链键需双向可查。
- 短链键(如
- 字符集:假设使用
[0-9, a-z, A-Z],共62个字符。7位短键有 \(62^7 \approx 3.5 \times 10^{12}\) 种组合,足够避免短期内耗尽。
第二步:基础哈希映射设计
最简单的思路是使用哈希函数(如MD5、SHA-256)将长URL映射为固定长度的哈希值,再截取前若干位作为短键。例如:
- 计算
SHA256(longUrl),得到64位十六进制字符串。 - 取前28位(十六进制),每4位可编码为1个Base62字符(\(16^4=65536\) 种值映射到62个字符),得到7位短键。
问题:
- 不同URL可能哈希到相同短键(冲突)。
- 直接截取会导致概率性冲突,需检测并处理。
第三步:解决冲突的策略
- 查询-重试机制:
- 生成短键后,先在全局存储(如分布式数据库)中查询该键是否已映射到其他URL。
- 如果未使用,则存储
{短键: longUrl}并返回。 - 如果已使用,则给原URL添加随机后缀(如加时间戳或随机数)重新哈希,直到找到未使用的短键。
- 优化重试效率:
- 使用布隆过滤器预先判断短键是否可能被占用,减少数据库查询。
- 对同一URL,可先检查是否已生成过短键(通过维护
{longUrl: 短键}缓存)。
第四步:分布式唯一ID生成辅助
为彻底避免冲突,可将短键生成与哈希解耦:
- 使用分布式唯一ID生成器(如雪花算法)产生全局唯一数字ID。
- 将数字ID转换为Base62字符串作为短键。例如,ID
123456789转换为"8M0kX"(补足到7位)。 - 优点:无需处理哈希冲突,ID本身唯一。
- 存储映射:
{短键: longUrl}和{longUrl: 短键}双表,支持双向查找。
第五步:具体步骤分解
生成短链流程:
- 输入长URL,先查缓存(
longUrl->短键),若存在则直接返回。 - 若不存在,用分布式ID生成器获取唯一整数ID。
- 将ID转换为Base62字符串,并填充或截断为固定长度(如7位)。
- 存储到数据库:
- 主表:
short_key(主键) ->long_url - 反向索引表:
long_url_hash(对长URL做哈希索引) ->short_key
- 主表:
- 返回完整短链:
https://short.url/{短键}。
解析短链流程:
- 从短链中提取短键(如从
https://short.url/abc12De提取"abc12De")。 - 用短键查询主表,得到原始URL。
- 若不存在,返回错误(如"404 Not Found")。
第六步:高并发与存储优化
- 数据库分片:
- 按短键的字符前缀或ID范围分片存储,分散读写压力。
- 缓存加速:
- 使用Redis缓存热点短键到URL的映射,提高解析速度。
- 防重复生成:
- 对同一长URL,用分布式锁(如基于Redis)确保并发下只生成一个短键。
- Base62转换算法示例:
BASE62 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" def to_base62(num: int, length=7) -> str: chars = [] while num > 0: num, rem = divmod(num, 62) chars.append(BASE62[rem]) # 反转并填充到固定长度 result = ''.join(reversed(chars)).rjust(length, '0') return result[-length:] # 确保长度精确
第七步:总结关键点
- 核心思路:用分布式唯一ID替代哈希截取,避免冲突。
- 存储:双映射表(短键→URL、URL哈希→短键)支持高效生成与解析。
- 并发:结合分布式锁、缓存、数据库分片保证高可用。
- 扩展:可添加TTL过期、访问统计、自定义短链等功能。
此设计在保证唯一性的同时,兼顾了分布式环境下的性能与可靠性。