
1. 短链系统概述短链服务是现代互联网中不可或缺的基础设施之一。它通过将冗长的URL转换为简短易记的短链接解决了社交媒体分享、短信发送等场景下URL过长的问题。一个典型的短链系统需要处理每天数百万甚至上亿次的请求这对系统的设计和实现提出了很高的要求。我在最近的一个个人项目中从零开始构建了一个完整的短链系统。这个系列文章将详细记录整个开发过程分享我在架构设计、技术选型和性能优化等方面的实践经验。Day01主要聚焦于系统的基础架构设计和核心功能实现。2. 核心需求分析2.1 功能需求一个完整的短链系统需要满足以下核心功能长URL转短链将任意长度的URL转换为固定长度的短码短链跳转用户访问短链时自动重定向到原始长URL访问统计记录每个短链的访问次数、时间等数据自定义短码允许用户指定特定的短码如果可用有效期管理设置短链的过期时间2.2 非功能需求除了基本功能外系统还需要考虑高可用性99.9%以上的可用性保证低延迟跳转响应时间控制在100ms以内高并发支持每秒数万次的跳转请求数据持久化确保短链数据不丢失安全性防止恶意短链和滥用3. 技术架构设计3.1 整体架构基于上述需求我采用了分层架构设计客户端层 → 接入层 → 服务层 → 存储层客户端层Web界面和API调用接入层Nginx反向代理和负载均衡服务层核心业务逻辑处理存储层MySQL Redis组合3.2 关键组件选型3.2.1 短码生成算法比较了几种常见的短码生成方案自增IDBase62编码优点实现简单无冲突缺点可预测安全性较低哈希算法如MD5截取优点分布均匀缺点可能存在哈希冲突预生成随机码优点高性能缺点需要预生成和存储最终选择了方案1因为实现简单且能满足初期需求。Base62编码使用字符集0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz3.2.2 存储设计MySQL表结构设计CREATE TABLE short_url ( id BIGINT PRIMARY KEY AUTO_INCREMENT, short_code VARCHAR(10) NOT NULL UNIQUE, original_url VARCHAR(2048) NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, expires_at TIMESTAMP NULL, user_id INT NULL, status TINYINT DEFAULT 1, INDEX idx_short_code (short_code) );Redis用于缓存热点数据采用String类型存储key: s:{short_code} value: {original_url}3.2.3 跳转实现HTTP 302重定向是最常用的跳转方式app.route(/short_code) def redirect(short_code): original_url cache.get(fs:{short_code}) if not original_url: original_url db.query_url(short_code) cache.set(fs:{short_code}, original_url, ttl3600) return redirect(original_url, code302)4. 核心功能实现4.1 短链生成服务def generate_short_url(original_url, custom_codeNone, user_idNone, ttlNone): if custom_code: if db.exists_short_code(custom_code): raise ValueError(Custom code already exists) short_code custom_code else: id db.get_next_id() short_code base62_encode(id) expires_at datetime.now() timedelta(secondsttl) if ttl else None db.create_short_url( short_codeshort_code, original_urloriginal_url, user_iduser_id, expires_atexpires_at ) cache.set(fs:{short_code}, original_url, ttlmin(3600, ttl) if ttl else 3600) return fhttps://short.url/{short_code}4.2 跳转服务优化为提高性能采用多级缓存策略Nginx层缓存对热点短链设置1分钟缓存Redis缓存所有短链缓存1小时本地缓存服务进程内缓存高频访问的短链lru_cache(maxsize10000) def get_original_url(short_code): original_url cache.get(fs:{short_code}) if not original_url: original_url db.query_url(short_code) if original_url: cache.set(fs:{short_code}, original_url, ttl3600) return original_url5. 性能优化实践5.1 存储优化使用连接池管理数据库连接对短码字段创建唯一索引对大表进行分库分表设计5.2 缓存策略热点数据预加载缓存穿透防护def get_url(short_code): # 布隆过滤器快速判断是否存在 if not bloom_filter.might_contain(short_code): return None # 缓存空值防止穿透 original_url cache.get(fs:{short_code}) if original_url is None: if cache.add(flock:{short_code}, 1, timeout10): original_url db.query_url(short_code) cache.set(fs:{short_code}, original_url or , ttl300 if not original_url else 3600) cache.delete(flock:{short_code}) else: time.sleep(0.1) return get_url(short_code) return original_url if original_url else None6. 监控与统计实现基础的访问统计功能CREATE TABLE access_log ( id BIGINT PRIMARY KEY AUTO_INCREMENT, short_code VARCHAR(10) NOT NULL, access_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP, referrer VARCHAR(512), user_agent VARCHAR(512), client_ip VARCHAR(45), INDEX idx_short_code_time (short_code, access_time) );为避免高频写入影响性能采用异步批处理方式background_task def log_access(short_code, request): access_log { short_code: short_code, referrer: request.headers.get(Referer), user_agent: request.headers.get(User-Agent), client_ip: request.remote_addr } redis.rpush(access_log_queue, json.dumps(access_log)) # 后台消费者 def consume_access_log(): while True: logs redis.lpop(access_log_queue, count100) if logs: db.bulk_insert_access_logs(logs) time.sleep(1)7. 安全防护措施恶意URL检测检查黑名单域名使用第三方安全API验证限制单个用户创建频率防爬虫限制单个IP访问频率设置合理的Robots.txt对异常流量进行人机验证数据安全敏感操作记录审计日志数据库定期备份关键信息加密存储8. 部署架构采用容器化部署方案Docker容器封装应用Kubernetes集群管理Nginx Ingress处理流量Prometheus Grafana监控ELK日志收集# Kubernetes Deployment示例 apiVersion: apps/v1 kind: Deployment metadata: name: short-url-service spec: replicas: 3 selector: matchLabels: app: short-url template: metadata: labels: app: short-url spec: containers: - name: app image: short-url:v1.0 ports: - containerPort: 8000 resources: limits: cpu: 1 memory: 512Mi9. 测试方案9.1 单元测试使用pytest编写核心逻辑测试def test_base62_encode(): assert base62_encode(0) 0 assert base62_encode(61) z assert base62_encode(62) 10 def test_short_url_creation(): url https://example.com short_url generate_short_url(url) assert len(short_url.split(/)[-1]) 69.2 性能测试使用Locust模拟高并发场景from locust import HttpUser, task class ShortUrlUser(HttpUser): task def redirect(self): self.client.get(/abc123) task(3) def create(self): self.client.post(/api/shorten, json{ url: https://example.com/ random_string(20) })测试指标创建接口QPS ≥ 1000跳转接口QPS ≥ 5000平均延迟 50msP99延迟 200ms10. 遇到的问题与解决方案10.1 短码冲突问题初期实现中自定义短码和自动生成短码可能冲突。解决方案使用单独的命名空间添加前缀区分如c-表示自定义查询时优先检查自定义短码10.2 缓存一致性问题当原始URL更新时缓存可能过期不及时。解决方案更新数据库时同步删除缓存设置较短的缓存TTL使用消息队列通知各节点失效缓存10.3 数据库热点问题自增ID导致写入集中在单个数据节点。解决方案使用分段ID生成改用UUID或雪花算法提前批量生成ID缓冲池11. 项目演进方向增加API限流和鉴权支持批量创建短链添加更丰富的统计报表实现短链分组和标签管理开发浏览器插件和移动端应用支持域名自定义和HTTPS增加团队协作功能这个短链系统虽然基础但涵盖了Web开发的许多核心概念和技术点。在Day01的实现中我特别注重了系统的可扩展性和性能表现为后续功能迭代打下了良好基础。