# DNS 查询日志加载性能优化方案 ## 问题分析 当前日志详情页面加载时间约 10 秒,主要性能瓶颈分析如下: ### 1. **主要性能瓶颈** #### 1.1 同步 API 调用(最严重) - **位置**: `updateLogsTable()` 函数第 1221 行 - **问题**: 对每条日志都调用 `await isDomainInTrackerDatabase(log.domain)` - **影响**: 假设每页 10 条日志,每条日志检查跟踪器数据库需要 100-500ms,总计 1-5 秒 - **代码**: ```javascript const trackerInfo = await isDomainInTrackerDatabase(log.domain); // ❌ 每条都 await ``` #### 1.2 跟踪器数据库加载 - **位置**: `isDomainInTrackerDatabase()` 函数 - **问题**: 每次检查都要等待数据库加载完成 - **影响**: 首次加载或数据库未加载完成时,每条日志都要等待 #### 1.3 域名信息 API 调用 - **位置**: `getDomainInfoFromAPI()` 函数 - **问题**: 如果实现中调用 API,会产生网络延迟 - **影响**: 每个域名都可能产生一次 HTTP 请求 #### 1.4 IP 地理位置查询 - **位置**: `getIpGeolocation()` 函数 - **问题**: 虽然有缓存,但首次查询仍需调用外部 API - **影响**: 每个新 IP 地址需要 200-500ms ### 2. **性能测试数据(估算)** | 操作 | 单次耗时 | 每页 10 条总耗时 | 优化后耗时 | |------|---------|----------------|-----------| | 跟踪器检查 | 100-500ms | 1-5 秒 | <100ms | | IP 地理位置 | 200-500ms | 2-5 秒 | <500ms(缓存) | | 后端 API 查询 | 100-300ms | 100-300ms | 不变 | | DOM 渲染 | 50-100ms | 50-100ms | 不变 | | **总计** | **10 秒** | **3-10 秒** | **<1 秒** | --- ## 优化方案 ### 方案一:异步并行处理(推荐优先实施)⭐⭐⭐⭐⭐ #### 1.1 批量并行检查跟踪器 **问题**: 当前是串行等待每个域名的检查结果 **解决**: 使用 `Promise.all()` 并行处理所有域名的检查 ```javascript // 优化前(串行) for (const log of logs) { const trackerInfo = await isDomainInTrackerDatabase(log.domain); // ... 处理逻辑 } // 优化后(并行) const trackerChecks = logs.map(log => isDomainInTrackerDatabase(log.domain)); const trackerResults = await Promise.all(trackerChecks); // 然后渲染表格 logs.forEach((log, index) => { const trackerInfo = trackerResults[index]; // ... 渲染逻辑 }); ``` **预期效果**: 从 1-5 秒降低到 100-500ms #### 1.2 预加载跟踪器数据库 **问题**: 每次检查都要等待数据库加载 **解决**: 页面初始化时立即加载,而不是延迟加载 ```javascript // 在页面加载时立即执行 document.addEventListener('DOMContentLoaded', () => { loadTrackersDatabase(); // 提前加载,不等待日志显示 }); ``` **预期效果**: 消除数据库加载等待时间 --- ### 方案二:缓存优化 ⭐⭐⭐⭐ #### 2.1 增强跟踪器检查缓存 **问题**: 相同的域名被重复检查 **解决**: 添加内存缓存,避免重复检查 ```javascript // 添加缓存 const trackerCache = new Map(); const trackerCacheTimestamp = new Map(); const TRACKER_CACHE_EXPIRY = 24 * 60 * 60 * 1000; // 24 小时 async function isDomainInTrackerDatabase(domain) { // 检查缓存 const cached = trackerCache.get(domain); const timestamp = trackerCacheTimestamp.get(domain); if (cached && timestamp && (Date.now() - timestamp) < TRACKER_CACHE_EXPIRY) { return cached; } // 执行实际检查... const result = // ... 检查逻辑 // 缓存结果 trackerCache.set(domain, result); trackerCacheTimestamp.set(domain, Date.now()); return result; } ``` **预期效果**: 重复域名检查从 100ms 降低到 <1ms #### 2.2 IP 地理位置缓存优化 **问题**: 相同 IP 被重复查询 **解决**: 已有 LRU 缓存,确保缓存命中率高 ```javascript // 确保缓存大小足够(当前为 1000,建议保持) const GEOLOCATION_CACHE_MAX_SIZE = 1000; ``` --- ### 方案三:延迟渲染优化 ⭐⭐⭐ #### 3.1 先渲染后更新 **问题**: 等待所有数据获取完成才渲染表格 **解决**: 先渲染基本内容,再异步更新增强信息 ```javascript async function updateLogsTable(logs) { // 第一步:立即渲染基本表格(不等待跟踪器检查) renderBasicTable(logs); // 第二步:异步获取跟踪器信息并更新 const trackerChecks = logs.map(log => isDomainInTrackerDatabase(log.domain)); Promise.all(trackerChecks).then(results => { updateTrackerIcons(results); // 只更新图标部分 }); // 第三步:异步获取 IP 地理位置 logs.forEach(log => { getIpGeolocation(log.clientIP).then(location => { updateLocationElement(log.clientIP, location); }); }); } ``` **预期效果**: 用户感知加载时间从 10 秒降低到 500ms 以内 #### 3.2 虚拟滚动(针对大量数据) **问题**: 如果用户选择每页 100 条,DOM 节点过多 **解决**: 只渲染可见区域的日志 ```javascript // 使用虚拟滚动库或自定义实现 // 只渲染可视区域的 20-30 条,滚动时动态加载 ``` **预期效果**: 渲染时间从 100ms 降低到 20ms --- ### 方案四:后端优化 ⭐⭐⭐⭐ #### 4.1 后端批量检查跟踪器 **问题**: 前端检查每条日志的跟踪器状态 **解决**: 后端在返回日志时直接包含跟踪器信息 ```go // 后端代码修改 type QueryLog struct { // ... 现有字段 IsTracker bool `json:"isTracker"` TrackerName string `json:"trackerName,omitempty"` } // 在返回日志前批量检查 func (s *Server) enrichLogsWithTrackerInfo(logs []QueryLog) { for i := range logs { logs[i].IsTracker, logs[i].TrackerName = s.checkTracker(logs[i].Domain) } } ``` **预期效果**: 完全消除前端跟踪器检查时间 #### 4.2 后端缓存优化 **问题**: 后端查询数据库慢 **解决**: 增加 Redis 或内存缓存 ```go // 使用内存缓存查询结果 var queryCache = lru.New(100) ``` **预期效果**: 后端查询时间从 300ms 降低到 50ms --- ### 方案五:数据库索引优化 ⭐⭐⭐ #### 5.1 添加数据库索引 **问题**: 日志查询慢 **解决**: 为常用查询字段添加索引 ```sql -- 为时间戳添加索引(加速时间范围查询) CREATE INDEX idx_timestamp ON query_logs(timestamp DESC); -- 为域名添加索引(加速域名搜索) CREATE INDEX idx_domain ON query_logs(domain); -- 为客户端 IP 添加索引 CREATE INDEX idx_client_ip ON query_logs(client_ip); -- 组合索引(加速组合查询) CREATE INDEX idx_timestamp_result ON query_logs(timestamp DESC, result); ``` **预期效果**: 后端查询时间从 500ms 降低到 50ms --- ## 实施优先级 ### 第一阶段(立即实施,效果最明显) 1. ✅ **方案 1.1**: 并行处理跟踪器检查 2. ✅ **方案 2.1**: 添加跟踪器检查缓存 3. ✅ **方案 1.2**: 预加载跟踪器数据库 **预期效果**: 10 秒 → 2-3 秒 ### 第二阶段(本周内实施) 4. ✅ **方案 3.1**: 先渲染后更新 5. ✅ **方案 4.1**: 后端批量检查跟踪器 **预期效果**: 2-3 秒 → 1 秒以内 ### 第三阶段(长期优化) 6. ✅ **方案 5.1**: 数据库索引优化 7. ✅ **方案 4.2**: 后端缓存 8. ✅ **方案 3.2**: 虚拟滚动 **预期效果**: 1 秒 → 500ms 以内 --- ## 具体代码修改 ### 修改 1: `logs.js` - 并行处理跟踪器检查 ```javascript // 在 updateLogsTable() 函数中,替换第 1155-1353 行 async function updateLogsTable(logs) { const tableBody = document.getElementById('logs-table-body'); if (!tableBody) return; // 清空表格 tableBody.innerHTML = ''; if (logs.length === 0) { // 显示空状态 tableBody.innerHTML = `