从 Busuanzi 到自建统计:EdgeOne KV 站点 PV/UV 实践指南(本网站统计数据清零原因说明)

前言:为什么要自建统计接口

在个人站点建设中,PV(访问量)和 UV(独立访客数)统计虽然看似简单,却对数据分析和运营决策至关重要。

最开始,我使用 Busuanzi 官方接口,只需嵌入几行代码即可显示 PV/UV:

  • 简单易用,无需服务器维护
  • 即刻可显示访问量和访客数

然而,随着访问量增加,Busuanzi 的问题逐渐显现:

  • 接口偶尔挂掉,导致统计数据无法显示
  • 加载速度不稳定,影响页面响应体验
  • 数据不可控,无法长期保存和分析历史访问情况

为了保证数据的可用性、稳定性和自主可控,我尝试自建统计接口:

  • PV:每次访问页面自增
  • UV:按 IP 去重,仅在新访客时增加
  • JSONP 输出,保持与 Busuanzi 接口兼容

通过自建接口,我可以完全掌控统计数据,同时避免依赖第三方服务的不稳定性。


自建统计接口的尝试与教训

最初,我在自己的服务器上搭建统计接口:

  • PV:每次访问页面自增
  • UV:按 IP 去重,仅在新访客时增加

短期内效果不错,但 服务器到期未续费,接口停止运行,之前累积的统计数据全部丢失

这就是本网站统计数据清零的主要原因:由于自建服务器停止运行,所有原有数据无法迁移到新系统,只能从零重新累积。

这个经历也提醒我:

统计系统不仅要能计算 PV/UV,更需要 可靠存储和高可用,否则数据随时可能丢失。


迁移到 EdgeOne KV

为了提升可用性,同时避免依赖单台服务器,我将统计逻辑迁移到了 EdgeOne KV

KV 优势

  1. 分布式高可用,不再依赖单台服务器
  2. 低延迟访问,适合实时 PV/UV 统计
  3. 按 key 分组管理,便于全站和单页面统计
  4. 数据可长期保存,解决了自建服务器丢失数据的问题

统计逻辑

  • PV(访问量)
    • 全站总访问量
    • 当前页面总访问量
  • UV(独立访客数)
    • 全站总访客数(去重 IP)
    • 当前页面总访客数(去重 IP)
  • 输出 JSONP,保持与 Busuanzi 接口兼容

性能优化策略

  1. 并行读取 KV
    • 使用 Promise.all 同时获取 PV/UV
  2. 异步写入 PV/UV
    • PV 写入不阻塞响应
    • UV 仅在新 IP 时写入 KV
  3. 去重处理
    • 全站 UV:记录访问过 IP
    • 页面 UV:记录访问过页面的 IP

经过优化,响应时间从 约 898ms 降至 约 220ms


从零开始重建统计

由于之前自建接口服务器停止,原有统计数据丢失,本网站数据全部 从零开始

  • 全站 PV/UV 从零累积
  • 每个页面 PV/UV 也从零累积
  • 可扩展按天统计,定期清理超过 7 天的历史数据

清零原因已经明确:服务器停止导致原数据无法迁移到 KV,所以新的统计系统从零开始累积。


EdgeOne 免费版配额概览(30 天)

功能 配额上限
安全加速流量 不限量
安全加速请求数 不限量
Edge Functions 请求数 300 万
Edge Functions CPU 时间 300 万秒
Cloud Functions 请求数 100 万
Cloud Functions GBs 50 万
KV 存储 1 GB
构建次数 500

个人站点使用完全够用,KV 存储可保存长期 UV/IP 数据。


Edge KV 核心实现代码

下面是实际使用的 EdgeOne KV PV/UV 统计接口代码,保持与 Busuanzi JSONP 接口兼容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
export async function onRequestGet({ request }) {
const KV = stats_kv; // KV 命名空间绑定

// ---- JSONP 回调 ----
const url = new URL(request.url);
const callbackRaw =
url.searchParams.get("jsonpCallback") || "BusuanziCallback";
const callback = callbackRaw.replace(/[^a-zA-Z0-9_]/g, "");

// ---- 站点 ID 和页面路径 ----
const referer = request.headers.get("Referer") || "https://unknown_site/";
const refererUrl = new URL(referer, "https://unknown_site");
const siteId = refererUrl.hostname.replace(/[^a-zA-Z0-9_\-]/g, "_");
const rawPath = refererUrl.pathname || "/";
const pagePath = rawPath.replace(/[^a-zA-Z0-9_\-./]/g, "_") || "_index";

const clientIP =
request.eo?.clientIp ||
request.headers.get("EO-Client-IP")?.split(",")[0].trim() ||
`test_${Math.floor(Math.random() * 100000)}`;

// ---- 并行读取 KV ----
const [
sitePVVal,
pagePVVal,
siteUVVal,
siteIPVisited,
pageUVCountVal,
pageIPVisited,
] = await Promise.all([
KV.get(`site_pv_total:${siteId}`),
KV.get(`page_pv_total:${siteId}:${pagePath}`),
KV.get(`site_uv_total:${siteId}`),
KV.get(`site_uv_alltime:${siteId}:${clientIP}`),
KV.get(`page_uv_count:${siteId}:${pagePath}`),
KV.get(`page_uv_total:${siteId}:${pagePath}:${clientIP}`),
]);

// ---- PV 处理 ----
let sitePV = Number(sitePVVal) || 0;
let pagePV = Number(pagePVVal) || 0;
sitePV += 1;
pagePV += 1;

// 异步写入 PV,不阻塞响应
KV.put(`site_pv_total:${siteId}`, String(sitePV));
KV.put(`page_pv_total:${siteId}:${pagePath}`, String(pagePV));

// ---- UV 处理 ----
let totalUV = Number(siteUVVal) || 0;
if (!siteIPVisited) {
totalUV += 1;
KV.put(`site_uv_alltime:${siteId}:${clientIP}`, "1");
KV.put(`site_uv_total:${siteId}`, String(totalUV));
}

let pageUV = Number(pageUVCountVal) || 0;
if (!pageIPVisited) {
pageUV += 1;
KV.put(`page_uv_total:${siteId}:${pagePath}:${clientIP}`, "1");
KV.put(`page_uv_count:${siteId}:${pagePath}`, String(pageUV));
}

// ---- 返回 JSONP ----
const data = {
site_pv: sitePV,
page_pv: pagePV,
site_uv: totalUV,
page_uv: pageUV,
};

return new Response(`${callback}(${JSON.stringify(data)});`, {
headers: {
"Content-Type": "text/javascript; charset=UTF-8",
"Access-Control-Allow-Origin": "*",
},
});
}

总结

通过 EdgeOne KV 的 Serverless 架构,我实现了:

  • 高可用、低延迟的全站和页面 PV/UV 统计
  • 异步处理和并行 KV 请求,提高响应速度
  • 从零开始重建统计系统,不依赖第三方接口

使用 KV 存储,你的统计数据掌握在自己手里,稳定可靠,适合个人或中小型网站。