- design-seckill.md: 秒杀系统设计 - design-shorturl.md: 短链接系统设计 - design-lbs.md: LBS附近的人系统设计 - design-im.md: 即时通讯系统设计 - design-feed.md: 社交信息流系统设计 Each document includes: - Requirements analysis and data volume assessment - Technical challenges - System architecture design - Database design - Caching strategies - Scalability considerations - Practical project experience - Alibaba P7 level additional points Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
14 KiB
14 KiB
LBS 附近的人系统设计
需求分析和数据量评估
需求分析
- 核心功能:位置搜索、附近的人、距离计算、实时更新
- 业务场景:社交软件、外卖服务、打车应用
- QPS评估:日查询10亿次,峰值QPS 5万+
- 数据规模:用户1亿+,日均位置更新1000万+
数据量评估
- 用户位置表:1亿条,实时更新频率高
- 位置历史表:100亿+条,存储轨迹信息
- 地理索引表:全球空间索引,数据量大
- 社交关系表:10亿+条,好友关系数据
核心技术难点
1. 海量数据处理
- 亿级用户位置数据存储
- 实时数据写入性能要求
- 空间索引构建和维护
2. 实时性要求
- 位置信息实时更新
- 查询响应毫秒级
- 数据一致性问题
3. 距离计算优化
- 快速计算两点间距离
- 批量距离计算优化
- 空间索引查询优化
4. 隐私保护
- 用户位置隐私
- 数据脱敏处理
- 访问权限控制
系统架构设计
总体架构
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 移动端APP │ │ Web管理后台 │ │ 第三方API │
│ (iOS/Android)│ │ (PC) │ │ (SDK) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
└───────────────────────┼───────────────────────┘
│
┌─────────────────────┼───────────────────────┐
│ │ │
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ API网关 │ │ 负载均衡 │ │ CDN加速 │
│ (Gateway) │ │ (Nginx) │ │ (Edge) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
┌─────────────────────┼───────────────────────┐
│ │ │
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 位置服务 │ │ 搜索服务 │ │ 计算服务 │
│ (微服务) │ │ (微服务) │ │ (微服务) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
│ │ │
└─────────────────────┼───────────────────────┘
│
┌─────────────────────┼───────────────────────┐
│ │ │
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Redis集群 │ │ 数据库集群 │ │ 消息队列 │
│ (缓存+pub/sub)│ │ (PostGIS) │ │ (Kafka/RabbitMQ)│
└─────────────────┘ └─────────────────┘ └─────────────────┘
│
┌─────────────────────┼───────────────────────┐
│ │ │
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 地理计算 │ │ 数据仓库 │ │ 地图渲染 │
│ (Geospatial) │ │ (ClickHouse) │ │ (Mapbox) │
└─────────────────┘ └─────────────────┘ └─────────────────┘
关键组件
1. 流量层
- API网关:请求路由、限流、认证
- 负载均衡:Nginx L7负载均衡
- CDN加速:静态资源缓存
2. 服务层
- 位置服务:位置更新、订阅管理
- 搜索服务:附近的人查询
- 计算服务:距离计算、路线规划
- 社交服务:好友关系管理
3. 存储层
- Redis集群:位置缓存、订阅频道
- PostgreSQL:空间数据存储
- ClickHouse:地理位置分析
- MongoDB:轨迹数据存储
4. 分析层
- 数据仓库:离线数据分析
- 实时计算:Flink/Kafka Streams
- 地图服务:地图渲染和展示
数据库设计
用户位置表
CREATE TABLE `user_location` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL COMMENT '用户ID',
`latitude` decimal(10,8) NOT NULL COMMENT '纬度',
`longitude` decimal(11,8) NOT NULL COMMENT '经度',
`accuracy` decimal(10,2) DEFAULT NULL COMMENT '定位精度(米)',
`device_type` varchar(20) DEFAULT NULL COMMENT '设备类型',
`app_version` varchar(50) DEFAULT NULL COMMENT '应用版本',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
`updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
`expire_at` timestamp NOT NULL COMMENT '过期时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_id` (`user_id`),
KEY `idx_location` (`latitude`, `longitude`),
KEY `idx_expire_at` (`expire_at`),
SPATIAL KEY `idx_spatial` (`latitude`, `longitude`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
位置历史表
CREATE TABLE `location_history` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL COMMENT '用户ID',
`latitude` decimal(10,8) NOT NULL COMMENT '纬度',
`longitude` decimal(11,8) NOT NULL COMMENT '经度',
`accuracy` decimal(10,2) DEFAULT NULL COMMENT '定位精度(米)',
`timestamp` datetime NOT NULL COMMENT '时间戳',
`device_type` varchar(20) DEFAULT NULL COMMENT '设备类型',
PRIMARY KEY (`id`),
KEY `idx_user_timestamp` (`user_id`, `timestamp`),
KEY `idx_location` (`latitude`, `longitude`),
SPATIAL KEY `idx_spatial` (`latitude`, `longitude`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
地理索引表
CREATE TABLE `geo_index` (
`id` bigint NOT NULL AUTO_INCREMENT,
`grid_id` varchar(20) NOT NULL COMMENT '网格ID',
`min_lat` decimal(10,8) NOT NULL COMMENT '最小纬度',
`max_lat` decimal(10,8) NOT NULL COMMENT '最大纬度',
`min_lng` decimal(11,8) NOT NULL COMMENT '最小经度',
`max_lng` decimal(11,8) NOT NULL COMMENT '最大经度',
`user_count` int NOT NULL DEFAULT 0 COMMENT '用户数量',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_grid_id` (`grid_id`),
KEY `idx_bounds` (`min_lat`, `max_lat`, `min_lng`, `max_lng`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
好友关系表
CREATE TABLE `user_friends` (
`id` bigint NOT NULL AUTO_INCREMENT,
`user_id` bigint NOT NULL COMMENT '用户ID',
`friend_id` bigint NOT NULL COMMENT '好友ID',
`distance` decimal(10,2) DEFAULT NULL COMMENT '距离(公里)',
`status` tinyint NOT NULL DEFAULT 1 COMMENT '状态',
`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_friend` (`user_id`, `friend_id`),
KEY `idx_user_id` (`user_id`),
KEY `idx_friend_id` (`friend_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
缓存策略
Redis缓存设计
// 用户位置缓存
const USER_LOCATION_PREFIX = 'location:';
const USER_LOCATION_TTL = 60; // 1分钟
// 地理网格缓存
const GRID_PREFIX = 'grid:';
const GRID_TTL = 300; // 5分钟
// 用户订阅缓存
const SUBSCRIPTION_PREFIX = 'subscription:';
const SUBSCRIPTION_TTL = 3600; // 1小时
// 距离计算缓存
const DISTANCE_PREFIX = 'distance:';
const DISTANCE_TTL = 60; // 1分钟
缓存策略
-
多级缓存:
- 本地缓存:Caffeine
- 分布式缓存:Redis Cluster
- CDN缓存:静态资源
-
缓存更新策略:
- Write Behind:异步更新
- Refresh Ahead:预加载热点数据
- Cache Invalidation:定时失效
-
空间索引缓存:
- 网格索引预加载
- 热点区域缓存
- 分层缓存策略
GeoHash实现
import math
def geohash_encode(latitude, longitude, precision=12):
# 地球半径(米)
earth_radius = 6371000
# GeoHash字符集
chars = "0123456789bcdefghjkmnpqrstuvwxyz"
# 计算精度对应的网格大小
grid_size = 5e6 / (2 ** (precision / 2))
# 计算经纬度范围
lat_min = -90
lat_max = 90
lng_min = -180
lng_max = 180
geo_hash = []
for i in range(precision):
# 纬度二进制
mid_lat = (lat_min + lat_max) / 2
if latitude >= mid_lat:
lat_min = mid_lat
lat_bit = 1
else:
lat_max = mid_lat
lat_bit = 0
# 经度二进制
mid_lng = (lng_min + lng_max) / 2
if longitude >= mid_lng:
lng_min = mid_lng
lng_bit = 1
else:
lng_max = mid_lng
lng_bit = 0
# 组合成字符索引
index = lat_bit * 2 + lng_bit
geo_hash.append(chars[index])
return ''.join(geo_hash)
def calculate_distance(lat1, lng1, lat2, lng2):
# Haversine公式计算距离
R = 6371000 # 地球半径(米)
dLat = math.radians(lat2 - lat1)
dLng = math.radians(lng2 - lng1)
a = (math.sin(dLat/2) * math.sin(dLat/2) +
math.cos(math.radians(lat1)) * math.cos(math.radians(lat2)) *
math.sin(dLng/2) * math.sin(dLng/2))
c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
distance = R * c
return distance
扩展性考虑
1. 水平扩展
- 无状态服务:位置服务和搜索服务无状态化
- 数据分片:按用户ID分片
- 读写分离:主库写入,从库读取
2. 垂直扩展
- 服务拆分:位置更新服务、查询服务、计算服务
- 数据分层:热数据、温数据、冷数据
- 多级缓存:本地、Redis、CDN
3. 全球化部署
- 地域化服务:按地域部署服务
- 数据同步:跨地域数据同步
- 灾备切换:多机房容灾
4. 性能优化
- 空间索引:R-Tree、Quadtree
- 批量处理:批量查询优化
- 异步处理:消息队列异步化
实际项目经验
1. 技术栈选择
- 前端:React Native + Flutter
- 后端:Spring Boot + Node.js
- 数据库:PostgreSQL + Redis
- 缓存:Redis Cluster
- 消息队列:Kafka
- 监控:Prometheus + Grafana
2. 性能优化
- 空间索引优化:GeoHash、R-Tree
- 缓存优化:多级缓存策略
- 数据库优化:分库分表、索引优化
- 网络优化:HTTP/2、Keep-Alive
3. 运维部署
- 容器化:Docker + Kubernetes
- CI/CD:Jenkins + GitLab
- 监控告警:ELK Stack + AlertManager
- 压测:JMeter + Locust
4. 安全设计
- 位置隐私:数据脱敏、权限控制
- 数据加密:传输加密、存储加密
- 访问控制:API鉴权、黑白名单
阿里P7加分项
1. 架构设计能力
- 高可用架构:99.99%可用性
- 高性能架构:支持亿级查询
- 全球化架构:全球多机房部署
2. 技术深度
- 空间算法:GeoHash、R-Tree算法
- 分布式系统:分布式缓存、分布式计算
- 数据库优化:PostGIS优化、空间索引
3. 业务理解
- 社交业务:理解社交应用场景
- 位置服务:掌握LBS业务模式
- 用户行为:分析用户移动模式
4. 团队管理
- 技术团队:带领20人+团队
- 项目管控:管理千万级用户项目
- 技术方案:主导技术架构设计
5. 前沿技术
- 边缘计算:边缘节点处理
- AI应用:轨迹预测、位置推荐
- Serverless:函数化服务
面试常见问题
1. 如何高效查询附近的人?
- 空间索引:GeoHash、R-Tree
- 网格划分:地理网格索引
- 缓存策略:多级缓存优化
2. 如何保证位置数据实时性?
- 推送机制:WebSocket实时推送
- 订阅模式:用户订阅位置变化
- 数据过期:定时清理过期数据
3. 如何处理海量位置数据?
- 分库分表:按用户ID分片
- 数据归档:冷热数据分离
- 压缩存储:轨迹数据压缩
4. 如何保护用户隐私?
- 数据脱敏:位置信息模糊化
- 权限控制:基于关系的访问控制
- 加密存储:敏感信息加密
5. 如何优化距离计算?
- 近似计算:GeoHash过滤
- 批量计算:向量运算优化
- 缓存机制:距离结果缓存