在体育产业数字化升级的背景下,如何构建一个支撑千万级用户的高可用数据平台?今天我将分享一套经过生产环境验证的企业级体育赛事数据中台架构方案。
系统架构全景
整体架构设计
核心微服务设计
1. 赛事服务 (Match Service)
java
@RestController
@RequestMapping("/api/match")
public class MatchController {
@Autowired
private MatchService matchService;
/**
* 获取赛事列表 - 支持多种筛选条件
*/
@GetMapping("/list")
public Result<PageVO<MatchVO>> getMatchList(
@RequestParam(required = false) String league,
@RequestParam(required = false) MatchStatus status,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "20") Integer size) {
MatchQuery query = MatchQuery.builder()
.league(league)
.status(status)
.page(page)
.size(size)
.build();
return Result.success(matchService.getMatchList(query));
}
/**
* 获取赛事详情 - 包含实时数据
*/
@GetMapping("/detail/{matchId}")
public Result<MatchDetailVO> getMatchDetail(
@PathVariable String matchId,
@RequestParam(defaultValue = "false") Boolean includeRealtime) {
return Result.success(matchService.getMatchDetail(matchId, includeRealtime));
}
}
@Service
@Slf4j
public class MatchService {
/**
* 批量更新赛事状态
*/
@Async
public void batchUpdateMatchStatus(List<MatchStatusUpdate> updates) {
updates.forEach(update -> {
try {
// 更新数据库
matchMapper.updateStatus(update);
// 发布状态变更事件
applicationEventPublisher.publishEvent(
new MatchStatusEvent(this, update)
);
// 更新缓存
redisTemplate.opsForValue().set(
"match:status:" + update.getMatchId(),
update.getStatus()
);
} catch (Exception e) {
log.error("更新赛事状态失败: {}", update.getMatchId(), e);
}
});
}
}
2. 实时数据服务 (Realtime Service)
java
@Component
@EnableScheduling
public class RealtimeDataProcessor {
@Autowired
private WebSocketSessionManager sessionManager;
/**
* 处理实时数据推送
*/
public void processRealtimeData(RealtimeData data) {
// 数据校验
if (!validateData(data)) {
log.warn("无效的实时数据: {}", data);
return;
}
// 数据持久化
saveToDatabase(data);
// 实时推送
pushToClients(data);
}
/**
* 推送数据到订阅的客户端
*/
private void pushToClients(RealtimeData data) {
List<WebSocketSession> sessions =
sessionManager.getSessionsByMatch(data.getMatchId());
sessions.forEach(session -> {
try {
String message = JSON.toJSONString(
WebSocketMessage.success("realtime_update", data)
);
session.sendMessage(new TextMessage(message));
} catch (IOException e) {
log.error("推送实时数据失败: {}", session.getId(), e);
sessionManager.removeSession(session);
}
});
}
}
前端架构设计
1. 状态管理设计
typescript
// store/matchStore.ts
export const useMatchStore = defineStore('match', () => {
const matches = ref<Match[]>([])
const realtimeData = ref<Map<string, RealtimeData>>(new Map())
// 获取赛事列表
const fetchMatches = async (query: MatchQuery) => {
const { data } = await matchAPI.getList(query)
matches.value = data
}
// 订阅实时数据
const subscribeRealtime = (matchId: string) => {
const ws = new WebSocket(`ws://localhost/ws/match/${matchId}`)
ws.onmessage = (event) => {
const message = JSON.parse(event.data)
if (message.type === 'realtime_update') {
realtimeData.value.set(matchId, message.data)
}
}
return () => ws.close()
}
return {
matches,
realtimeData,
fetchMatches,
subscribeRealtime
}
})
2. 组件设计
vue
<template>
<div class="match-list">
<div class="filters">
<league-filter v-model="selectedLeague" />
<status-filter v-model="selectedStatus" />
<date-range-picker v-model="dateRange" />
</div>
<div class="matches">
<match-card
v-for="match in filteredMatches"
:key="match.id"
:match="match"
:realtime-data="realtimeData.get(match.id)"
@click="showDetail(match)"
/>
</div>
<div class="pagination">
<el-pagination
:current-page="currentPage"
:page-size="pageSize"
:total="total"
@current-change="handlePageChange"
/>
</div>
</div>
</template>
<script setup lang="ts">
// 组合式函数
const {
matches,
realtimeData,
fetchMatches
} = useMatchStore()
// 响应式数据
const selectedLeague = ref('')
const selectedStatus = ref('')
const currentPage = ref(1)
const pageSize = ref(20)
// 计算属性
const filteredMatches = computed(() => {
return matches.value.filter(match => {
return (!selectedLeague.value || match.league === selectedLeague.value) &&
(!selectedStatus.value || match.status === selectedStatus.value)
})
})
// 生命周期
onMounted(() => {
fetchMatches({
page: currentPage.value,
size: pageSize.value
})
})
</script>
性能优化策略
1. 数据库优化
sql
-- 赛事表索引优化
CREATE INDEX idx_match_league_status ON matches(league, status);
CREATE INDEX idx_match_start_time ON matches(start_time);
CREATE INDEX idx_match_league_time ON matches(league, start_time);
-- 分页查询优化
EXPLAIN SELECT * FROM matches
WHERE league = '英超' AND status = 'LIVE'
ORDER BY start_time DESC
LIMIT 20 OFFSET 0;
2. 缓存策略
java
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30))
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}
}
@Service
public class MatchServiceImpl implements MatchService {
@Cacheable(value = "matches", key = "#matchId")
public MatchDetailVO getMatchDetail(String matchId) {
return matchMapper.selectDetailById(matchId);
}
@CacheEvict(value = "matches", key = "#matchId")
public void updateMatch(MatchUpdate update) {
matchMapper.updateById(update);
}
}
监控与运维
1. 应用监控
yaml
# application-monitor.yml
management:
endpoints:
web:
exposure:
include: health,metrics,info,prometheus
endpoint:
health:
show-details: always
metrics:
export:
prometheus:
enabled: true
2. 日志收集
java
@Aspect
@Component
@Slf4j
public class ServiceMonitorAspect {
@Around("execution(* com.sports..*Service.*(..))")
public Object monitorService(ProceedingJoinPoint joinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
String methodName = joinPoint.getSignature().getName();
try {
Object result = joinPoint.proceed();
long cost = System.currentTimeMillis() - startTime;
log.info("Service method executed: {}.{}, cost: {}ms",
joinPoint.getTarget().getClass().getSimpleName(),
methodName, cost);
// 记录指标
Metrics.counter("service_execution")
.tag("method", methodName)
.tag("status", "success")
.increment();
return result;
} catch (Exception e) {
Metrics.counter("service_execution")
.tag("method", methodName)
.tag("status", "error")
.increment();
throw e;
}
}
}
部署方案
Docker Compose 部署
yaml
version: '3.8'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: ${DB_PASSWORD}
volumes:
- mysql_data:/var/lib/mysql
redis:
image: redis:6.2-alpine
command: redis-server --appendonly yes
backend:
build: ./backend
environment:
SPRING_PROFILES_ACTIVE: prod
depends_on:
- mysql
- redis
frontend:
build: ./frontend
ports:
- "80:80"
volumes:
mysql_data:
这套架构方案已经在多个体育数据平台中得到验证,支撑了日均百万级的访问量。通过微服务架构、实时数据推送、多级缓存等关键技术,确保了系统的高可用性和可扩展性。
完整源码:关注后私信获取 GitHub 仓库地址,包含详细部署文档和开发指南。
欢迎在评论区交流架构设计和技术实现细节!