非常好的选择!让我们来优化数据收集和存储方式,并添加更多实时指标。这将使我们的分析系统能够处理更大规模的数据,并提供更有价值的实时洞察。
第三十四部分:优化数据收集和存储,添加更多实时指标
- 优化数据收集和存储
为了处理更大规模的用户行为数据,我们需要进行以下优化:
a. 使用批量插入 b. 实现数据压缩 c. 使用分区表 d. 添加索引以提高查询性能
首先,更新 src/db/schema.ts:
import { sqliteTable, integer, text, real } from "drizzle-orm/sqlite-core";
import { sql } from "drizzle-orm";
export const userBehaviors = sqliteTable("user_behaviors", {
id: integer("id").primaryKey(),
userId: integer("user_id"),
data: text("data"), // 存储压缩的JSON数据
timestamp: integer("timestamp", { mode: "timestamp" }),
date: text("date").default(sql`DATE('now')`), // 用于分区
});
// 添加索引
db.run(`CREATE INDEX IF NOT EXISTS idx_user_behaviors_user_id ON user_behaviors (user_id)`);
db.run(`CREATE INDEX IF NOT EXISTS idx_user_behaviors_date ON user_behaviors (date)`);更新 src/routes/analytics.ts 中的数据插入逻辑:
import { Elysia } from "elysia";
import { db } from "../db";
import { userBehaviors } from "../db/schema";
import { compress } from "lz-string";
let behaviorBuffer = [];
export const analyticsRoutes = new Elysia()
// ... 保留之前的路由
.post("/api/log-behavior", async ({ body }) => {
const compressedData = compress(JSON.stringify(body));
behaviorBuffer.push({
userId: body.userId,
data: compressedData,
timestamp: new Date(),
});
if (behaviorBuffer.length >= 100) {
await db.insert(userBehaviors).values(behaviorBuffer);
behaviorBuffer = [];
}
return { success: true };
});
// 定期刷新缓冲区
setInterval(async () => {
if (behaviorBuffer.length > 0) {
await db.insert(userBehaviors).values(behaviorBuffer);
behaviorBuffer = [];
}
}, 60000); // 每分钟刷新一次- 添加更多实时指标
我们将添加实时转化率和实时收入指标。首先,更新 src/db/schema.ts:
export const conversions = sqliteTable("conversions", {
id: integer("id").primaryKey(),
userId: integer("user_id"),
type: text("type"), // 例如:'purchase', 'signup', 'download'
value: real("value"), // 转化的价值(如购买金额)
timestamp: integer("timestamp", { mode: "timestamp" }),
});
// 添加索引
db.run(`CREATE INDEX IF NOT EXISTS idx_conversions_timestamp ON conversions (timestamp)`);更新 src/routes/analytics.ts 以提供实时指标:
import { Elysia } from "elysia";
import { db } from "../db";
import { userBehaviors, conversions, postViews } from "../db/schema";
export const analyticsRoutes = new Elysia()
// ... 保留之前的路由
.get("/api/real-time-stats", async () => {
const now = new Date();
const fiveMinutesAgo = new Date(now.getTime() - 5 * 60 * 1000);
const activeUsersCount = await db
.select({ count: sql`count(distinct ${userBehaviors.userId})` })
.from(userBehaviors)
.where(sql`${userBehaviors.timestamp} > ${fiveMinutesAgo}`)
.get();
const pageViewsCount = await db
.select({ count: sql`count(*)` })
.from(postViews)
.where(sql`${postViews.lastViewed} > ${fiveMinutesAgo}`)
.get();
const conversionsCount = await db
.select({ count: sql`count(*)` })
.from(conversions)
.where(sql`${conversions.timestamp} > ${fiveMinutesAgo}`)
.get();
const revenue = await db
.select({ total: sql`sum(${conversions.value})` })
.from(conversions)
.where(sql`${conversions.timestamp} > ${fiveMinutesAgo}`)
.get();
const conversionRate = pageViewsCount.count > 0
? (conversionsCount.count / pageViewsCount.count) * 100
: 0;
const activePagesResult = await db
.select({
path: postViews.path,
count: sql`count(*)`.as("count"),
})
.from(postViews)
.where(sql`${postViews.lastViewed} > ${fiveMinutesAgo}`)
.groupBy(postViews.path)
.orderBy(sql`count(*)`, "desc")
.limit(5)
.all();
return {
activeUsers: activeUsersCount.count,
pageViews: pageViewsCount.count,
conversions: conversionsCount.count,
conversionRate: conversionRate.toFixed(2),
revenue: revenue.total || 0,
activePages: activePagesResult,
};
})
.post("/api/log-conversion", async ({ body }) => {
await db.insert(conversions).values(body);
return { success: true };
});现在,更新 src/views/analytics.eta 以显示这些新的实时指标:
<% layout('./layouts/main.eta', { title: 'Analytics' }) %>
<h1>Real-time Analytics Dashboard</h1>
<div class="real-time-stats">
<div class="stat-box">
<h2>Active Users</h2>
<p id="active-users-count">0</p>
</div>
<div class="stat-box">
<h2>Page Views</h2>
<p id="page-views-count">0</p>
</div>
<div class="stat-box">
<h2>Conversions</h2>
<p id="conversions-count">0</p>
</div>
<div class="stat-box">
<h2>Conversion Rate</h2>
<p id="conversion-rate">0%</p>
</div>
<div class="stat-box">
<h2>Revenue</h2>
<p id="revenue">$0</p>
</div>
</div>
<div class="stat-box">
<h2>Top Active Pages</h2>
<ul id="active-pages-list"></ul>
</div>
<!-- 保留之前的图表和脚本 -->
<script>
// ... 保留之前的代码
// 更新实时统计数据的函数
async function updateRealTimeStats() {
const response = await fetch('/api/real-time-stats');
const stats = await response.json();
document.getElementById('active-users-count').textContent = stats.activeUsers;
document.getElementById('page-views-count').textContent = stats.pageViews;
document.getElementById('conversions-count').textContent = stats.conversions;
document.getElementById('conversion-rate').textContent = `${stats.conversionRate}%`;
document.getElementById('revenue').textContent = `$${stats.revenue.toFixed(2)}`;
const activePagesList = document.getElementById('active-pages-list');
activePagesList.innerHTML = '';
stats.activePages.forEach(page => {
const li = document.createElement('li');
li.textContent = `${page.path}: ${page.count} views`;
activePagesList.appendChild(li);
});
}
// 定期更新实时统计数据
setInterval(updateRealTimeStats, 5000); // 每5秒更新一次
// 初始加载
updateRealTimeStats();
</script>
<style>
.real-time-stats {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
margin-bottom: 2em;
}
.stat-box {
background-color: #f0f0f0;
border-radius: 8px;
padding: 1em;
text-align: center;
margin: 0.5em;
flex: 1 1 200px;
}
</style>这些改进实现了以下功能:
-
优化数据收集和存储:
- 使用批量插入减少数据库操作次数
- 实现数据压缩以减少存储空间
- 使用分区表(通过日期字段)以提高大规模数据的查询效率
- 添加索引以提高查询性能
-
添加更多实时指标:
- 实时活跃用户数
- 实时页面浏览量
- 实时转化次数
- 实时转化率
- 实时收入
- 实时最活跃页面列表
主要改进包括:
- 优化了数据存储和查询方式,使系统能够处理更大规模的用户行为数据
- 提供了更全面的实时指标,包括业务关键指标如转化率和收入
- 改进了实时数据的可视化展示,使用更直观的布局
接下来的步骤可以包括:
- 实现数据归档策略,定期将旧数据移动到历史表中
- 添加更多的数据可视化图表,如实时转化漏斗、用户行为流图等
- 实现自定义仪表板功能,允许用户选择他们最关心的指标
- 添加异常检测功能,当指标出现异常波动时发出警报
- 实现预测分析,如预测未来一小时的收入或转化率
你希望继续深入哪个方向,或者有其他想法吗?