埋点这件事,别等系统跑起来才想
很多系统上线时都说“先加点日志后面再分析”。过几个月再看日志确实不少问题还是回答不了。用户从哪里来不知道。注册页打开了多少次不知道。免费用户为什么没有付费不知道。某个接口调用量突然涨了是新用户增长、爬虫、重试风暴还是某个客户接入脚本写错了也不知道。日志不是埋点。日志通常是给工程师排障看的讲的是“程序发生了什么”。埋点是给产品、运营、增长、风控、计费和工程一起看的讲的是“用户和业务发生了什么”。这篇文章不讲某个具体项目而是把服务埋点这件事拆开什么服务需要埋点不同流量阶段怎么做数据库怎么选成本怎么算性能怎么评估最后用几个真实业务场景把“从用户源头到分析结果”的链路串起来。埋点到底解决什么问题埋点最常见的误区是把它当成“访问量统计”。访问量当然重要但只看 PV/UV最多知道门口来了多少人。真正有价值的问题通常更尖锐哪个渠道来的用户更容易注册注册后多久会完成第一次关键行为免费用户在什么地方掉队付费前通常调用了哪些功能某个版本上线后错误率上升是集中在一个入口还是所有入口都变差高价值用户和普通用户的行为差异是什么一个接口调用量涨了收入有没有跟着涨成本有没有失控这些问题都有一个共同点它们不只需要一条日志而是需要把用户、会话、来源、页面、接口、订单、功能、错误、成本串起来。所以服务埋点的核心不是“多记一点”而是建立一条可追踪的事实链用户来源 - 访问页面 - 注册/登录 - 激活行为 - 功能使用 - 成本消耗 - 付费/留存如果这条链断了再多日志也只能做局部猜测。哪些服务需要埋点不是所有服务一上来都需要复杂埋点。埋点设计应该和服务类型、业务阶段、流量规模一起看。服务类型最重要的问题优先埋点内容站、官网、文档站流量从哪来哪些内容带来转化page_view、referrer、utm、入口按钮、注册点击SaaS 后台用户是否真正激活哪些功能被使用login、workspace_created、feature_used、invite_sent电商/订阅服务漏斗哪里流失支付失败原因是什么product_view、checkout_started、payment_succeeded、payment_failedAPI 平台调用量、成功率、耗时、成本、客户价值api_call、first_api_call、quota_exceeded、billing_usageB2B 内部系统关键流程是否完成审批/任务卡在哪里workflow_started、step_completed、approval_rejected高流量网关峰值、异常、区域分布、计费和风控request_sample、error_bucket、rate_limited、billable_event小服务最怕过度设计。刚上线的官网不需要立刻建 ClickHouse 集群但至少要有稳定的anonymous_id、页面访问、关键按钮点击和注册成功事件。反过来一个每天几百万 API 调用的平台如果还只靠应用日志和后台 SQL 查账很快会把业务库拖慢。先定事件不要先定数据库数据库选型很重要但埋点系统最容易失败的地方不是数据库而是事件口径。一个合格的事件至少要回答四个问题{ event: api_call, time: 2026-06-29T10:23:45.123Z, user_id: u_123, anonymous_id: anon_abc, session_id: sess_789, source: web, page: /pricing, properties: { tool_id: stock_quote, success: true, latency_ms: 183, credits: 2, plan: pro } }这四个问题是谁user_id、anonymous_id、account_id、api_key_id什么时候事件时间和服务端接收时间最好都保留在哪里页面、入口、客户端、地区、设备、服务名做了什么事件名和业务属性这里有两个细节很关键。第一匿名用户不能丢。很多转化发生在注册前如果没有anonymous_id注册前的广告来源、落地页、定价页访问就没法和注册后的用户连起来。第二事件名要稳定。checkout_start、start_checkout、click_pay_button混着用后面看板一定会乱。事件名应该有 allowlist字段应该有 schema version。可选的埋点方式埋点入口大致分四类。前端埋点适合采页面浏览、按钮点击、表单展示、页面停留、客户端错误。它离用户最近能拿到 referrer、utm、屏幕、浏览器、入口位置。但它不可靠用户可能关页面浏览器可能拦截网络可能失败前端也容易被伪造。后端埋点适合采注册成功、登录成功、API Key 创建、订单支付、接口调用、扣费、权限失败。这类事件必须以后端为准特别是支付和计费不能相信前端说“我付费成功了”。网关埋点适合采 API 平台和微服务入口请求路径、状态码、耗时、租户、key、限流、熔断、上游错误。它的优点是统一缺点是业务语义有限。网关知道某个接口被调用了但不一定知道这次调用对业务意味着什么。异步埋点适合高流量场景。主流程只把事件放进内存队列、本地 buffer、Kafka、Pulsar 或云消息队列后面的消费者批量写库。它的好处是不会让数据库写入反压用户请求坏处是系统复杂度上来了需要处理丢失、重复、乱序和重放。一套常见的分工是前端页面和交互 后端业务事实 网关请求事实 队列削峰和批量写入 任务聚合、修复、回填采集时要避开的坑服务埋点不是写一条 insert 就完事。下面几个坑很常见。不要把敏感信息写进埋点。邮箱、手机号、token、API Key、支付卡号、原始 prompt、完整查询文本都要非常谨慎。需要分析时可以写 hash、长度、分类、是否为空不要把隐私和密钥带进分析库。不要让埋点阻塞主流程。注册、支付、API 调用这些路径上埋点失败不能导致业务失败。常见做法是 best effort、异步队列、超时丢弃、批量写入。不要采太细的无意义事件。滚动、鼠标移动、hover 这类事件很容易把量打爆。除非明确做行为热力图否则先别碰。不要只存原始事件不做聚合。Dashboard 如果每次都扫 180 天明细表迟早会变成数据库压力测试。常用指标应该做日聚合、小时聚合明细只用于 drilldown。不要让 properties 变成垃圾桶。JSON 字段可以灵活但每个事件应该有字段字典。否则三个月后你会发现同一个含义有plan、plan_type、tier、package四种写法。分析方法从单点事件到用户旅程埋点分析通常分几层。最简单的是计数和趋势select date_trunc(day, time) as day, count(*) as pv from event_logs where event page_view group by 1 order by 1;第二层是分组对比select utm_source, count(distinct anonymous_id) as visitors from event_logs where event page_view and time now() - interval 7 days group by utm_source order by visitors desc;第三层是漏斗landing_view - signup_click - signup_completed - first_api_call - payment_succeeded漏斗不要只看总转化率。要按渠道、设备、国家、套餐、入口页面拆开看。总转化率下降 20%可能只是某个渠道买量变差也可能是移动端注册页坏了也可能是支付失败率上升。第四层是 cohort 留存。比如看“完成首次 API 调用的用户7 天后是否还有调用”。这比 DAU 更接近产品是否真的有用。第五层是单位经济账。API 平台尤其需要这层某个客户调用量很高但如果每次调用都带来第三方成本收入没覆盖成本就不是好事。数据库怎么选服务埋点常见的存储选项有 PostgreSQL、MongoDB、ClickHouse也可以直接用产品分析工具比如 PostHog、Amplitude、Mixpanel。自己做的好处是口径可控能和业务数据打通坏处是要承担数据质量、性能和运维。PostgreSQL中小规模最稳的起点如果你的服务本来就用 PostgreSQL日事件量在几十万以内查询主要是近 7 天、近 30 天的 dashboardPostgreSQL 是很好的起点。它的优势是业务 join 方便。用户表、订单表、积分表、团队表都在同一个数据库或同类数据库里分析时不用同步一堆维表。JSONB 也能承接不同事件的扩展属性。PostgreSQL 官方文档里GIN 索引用于处理 JSONB、全文检索这类复合值查询这让“结构化字段 JSONB 扩展”成为一个现实方案。但 PostgreSQL 不是无限事件仓库。写事件表时要注意按时间分区比如月分区或日分区常用查询字段单独列出来不要全部塞进 JSONBJSONB 的 GIN 索引少建确实需要按 properties 查询再建Dashboard 查聚合表不要扫明细表明细保留周期要明确比如 90 天或 180 天一个比较稳的表结构是create table event_logs ( id uuid primary key, event text not null, event_time timestamptz not null, received_at timestamptz not null default now(), user_id text, anonymous_id text, session_id text, source text, page text, referrer text, utm_source text, utm_campaign text, properties jsonb not null default {}, schema_version int not null default 1 ) partition by range (event_time);MongoDB能存但不适合作为主分析库MongoDB 存事件很自然。事件本来就是 JSON字段变化频繁写入也简单。MongoDB 也有 time series collections官方文档说明它会把相似时间和相同来源的数据组织到一起提高时间序列数据的存储和查询效率。所以 MongoDB 不是不能做埋点。它适合这些场景事件结构非常不稳定早期还在探索主要用于调试和审计不做复杂漏斗已经有成熟 MongoDB 运维体系单用户或单对象维度查询多跨全量聚合少但如果你要长期做 DAU、渠道转化、留存、付费漏斗、用户分层、API 成本趋势MongoDB 会越来越吃力。不是完全做不了而是复杂 aggregation pipeline、索引数量、数据扫描成本、BI 接入成本都会上来。一句话MongoDB 可以当灵活事件仓库不建议当核心埋点分析主库。ClickHouse高流量分析的正路ClickHouse 是列式 OLAP 数据库适合 append-only 的大规模事件、日志、时间序列和多维聚合。ClickHouse 文档里 MergeTree、分区和 TTL 是核心能力TTL 文档也明确建议按同一个时间字段做分区这样过期数据可以整块 drop而不是逐行删除。它的优势很适合埋点列式存储只读查询需要的列压缩率高重复字段多的事件数据很省空间按时间、事件、用户、渠道、区域聚合很快批量写入吞吐高TTL、分区、冷热数据管理适合长期明细保留Cloudflare 的公开案例很有代表性。Cloudflare 在一篇博客里介绍过用 ClickHouse 支撑 HTTP Analytics处理每秒数百万请求级别的分析流水线后来也多次分享过 ClickHouse 在日志分析、客户 dashboard、Bot 管理等场景中的使用。这类案例说明 ClickHouse 不是“更快的 PostgreSQL”而是完全不同定位的分析型基础设施。但 ClickHouse 也不是所有阶段都该上。它不适合频繁单行更新不适合强事务不适合替代业务主库。它最好作为分析库由 PostgreSQL、日志、队列、业务服务异步同步过来。一个简单的选型表阶段日事件量推荐方案说明起步1 千 - 10 万PostgreSQL 单表或月分区先把事件口径做对成长10 万 - 50 万PostgreSQL 分区 聚合表控制索引聚合先行高速增长50 万 - 100 万PostgreSQL ClickHouse 旁路同步开始验证列式分析库高流量100 万 - 500 万ClickHouse 做明细分析PostgreSQL 做业务事实Dashboard 不扫业务库超高流量500 万以上ClickHouse 集群 队列 分层存储需要专门的数据平台治理这里的日事件量只是经验线不是绝对阈值。如果事件很窄、查询简单PostgreSQL 能撑得更久如果每个事件 properties 很大、看板又天天扫 180 天几十万日事件也可能很痛苦。成本怎么估算埋点成本主要由五个变量决定日事件数 × 单条事件大小 × 保留周期 × 副本数 × 查询复杂度PostgreSQL 里一条带 JSONB 的事件连同行开销和索引粗略可以按 1KB 到 3KB 估。ClickHouse 压缩后可能只有 200B 到 800B具体取决于字段设计和重复度。按 50 万事件/日估算存储单条估算月明细保留 12 个月PostgreSQL2KB约 30GB约 360GBPostgreSQL 加索引膨胀4KB约 60GB约 720GBClickHouse 压缩后500B约 7.5GB约 90GB这只是明细不含备份、副本、聚合表、WAL/binlog、对象存储归档。比较稳的预算方式是再乘一个 1.5 到 2 的预留系数。如果自建 ClickHouse一个 4-8 vCPU、16-32GB 内存、几百 GB 到 1TB SSD 的单节点通常足够做早期验证和中等规模分析。到了每天几百万事件或者保留一年以上明细就要认真规划副本、备份、冷热分层和查询隔离。性能怎么评估不要只压测写入。埋点系统真正容易出问题的是“写入 查询 聚合 清理”同时发生。我会看这些指标写入延迟主业务请求是否被埋点拖慢写入吞吐每秒事件数、批量大小、失败重试查询延迟常用 dashboard 的 P50/P95聚合耗时每日任务是否能在窗口内跑完存储增长每天新增多少数据索引占比多少数据延迟事件产生后多久能在看板看到数据质量重复率、丢失率、无效 user_id、非法 event一个简单的验收标准可以这样定指标小中型服务目标高流量服务目标主流程埋点开销P95 小于 10ms失败不影响业务主流程只入队P95 小于 3ms前端事件可见延迟1-5 分钟1 分钟内或准实时日聚合任务30 分钟内完成分钟级增量聚合常用看板查询P95 小于 2 秒P95 小于 1 秒明细查询范围默认限制 7-30 天通过 ClickHouse 支撑长窗口性能评估还有一个容易被忽略的点事件增长不是线性的。业务正常增长是一种某个 SDK 出 bug、客户端疯狂重试、debug 事件忘关、爬虫打爆接口是另一种。埋点系统要能降级比如采样 DEBUG 事件、丢弃低优先级事件、限制单用户事件频率。端到端案例一内容站如何分析注册转化假设你有一个技术内容站目标不是单纯看文章阅读量而是希望读者注册产品。事件设计阶段事件关键字段来源page_viewanonymous_id、utm_source、referrer、slug阅读article_readscroll_depth、read_seconds、slug点击signup_clicklocation、slug、button_type注册signup_completeduser_id、anonymous_id、method激活first_key_actionuser_id、action、hours_since_signup分析链路广告/搜索/社交 - 文章页 - 注册按钮 - 注册完成 - 第一次关键行为分析结果可能是搜索流量 PV 最大但注册率一般某篇深度教程 PV 不高但注册转化率很高移动端 signup_click 多signup_completed 少说明注册页移动端有问题某个渠道带来的用户注册快但激活率低可能是低质量流量这时优化动作就很明确不是“多写文章”而是把高转化文章前置、修移动端注册页、调整投放渠道。端到端案例二API 平台如何分析成本和收入API 平台只看调用量很危险。调用量越高成本可能越高如果没有收入或留存增长反而会变成亏损。事件设计阶段事件关键字段注册signup_completeduser_id、plan、utm_sourceAPI Keyapi_key_createduser_id、key_type首次调用first_api_calluser_id、tool_id、signup_to_first_call_hours调用api_calluser_id、api_key_id、tool_id、success、latency_ms、cost、credits限额quota_exceededuser_id、plan、requested_action付费payment_succeededuser_id、amount、plan、calls_before_pay核心分析select tool_id, count(*) as calls, sum((properties-cost)::numeric) as cost, sum((properties-credits)::numeric) as credits, avg((properties-latency_ms)::numeric) as avg_latency from event_logs where event api_call and event_time now() - interval 1 day group by tool_id order by calls desc;这类分析能回答哪些工具调用最多但转化不付费哪些工具毛成本高需要改定价免费用户到首次调用的时间是否太长首次调用成功率低是参数难填、文档不清楚还是第三方 provider 不稳定API 平台建议把“调用事实”和“业务行为”分开。调用事实可以来自网关或服务端必须准确业务行为可以来自产品埋点负责解释用户为什么走到这里。端到端案例三订阅服务如何看付费漏斗订阅服务最怕只看支付成功。支付成功当然重要但付费前的路径更重要。事件链路pricing_view - plan_select - checkout_started - payment_succeeded/payment_failed - subscription_renewed/subscription_cancelled关键字段current_planselected_planpricepayment_methodfailure_reasoncountrydevice_typeentry_point分析时不要只算一个总漏斗要拆移动端和桌面端不同国家/地区不同入口比如首页、文档、用量不足提醒新用户和老用户月付和年付一个常见发现是plan_select - checkout_started转化很好但checkout_started - payment_succeeded很差。这个问题一般不在定价页而在支付链路支付方式不适配、3DS 验证失败、账单地址要求过多、错误提示不清楚。演进策略别一步到位也别没有退路我比较喜欢的演进方式是四步。第一步只做核心事件。页面访问、注册成功、登录成功、关键功能使用、支付成功、API 调用。先保证口径准确不要追求事件数量。第二步补身份链路。匿名用户、登录用户、账号、团队、API Key 要能串起来。没有身份链路漏斗和留存都不稳。第三步做聚合表和看板。DAU、注册转化、首次关键行为、付费转化、功能使用、调用成功率、成本趋势。不要让业务方天天找工程师跑 SQL。第四步引入分析库。PostgreSQL 扛不住长窗口明细扫描时再同步到 ClickHouse。这个时候事件口径已经稳定表设计也更容易做对。一个健康的埋点系统应该允许你从简单开始但未来能平滑长大PostgreSQL 原始事件 - PostgreSQL 分区 聚合表 - PostgreSQL ClickHouse 旁路分析 - ClickHouse 承担明细分析PostgreSQL 保留业务事实最后埋点是产品和工程之间的协议服务埋点不是产品经理写一张事件表也不是工程师随手加几行日志。它更像一份协议我们如何定义用户行为如何定义成功如何定义成本如何定义一次真正有价值的使用。一开始不用做得很重。小服务先把十几个核心事件采准比一口气采一百个混乱事件有用得多。等流量上来再考虑队列、分区、ClickHouse、冷热分层。真正重要的是系统上线后团队能持续回答这些问题用户从哪里来他们做了什么哪里卡住了哪些行为带来收入哪些调用消耗成本改了一版之后结果到底变好了还是变坏了能回答这些问题埋点才算开始有价值。参考资料CloudflareHTTP Analytics for 6M requests per second using ClickHouseCloudflareLog analytics using ClickHouseClickHouse DocsManage data with TTLClickHouse DocsTable partitionsPostgreSQL DocsGIN IndexesMongoDB DocsTime Series CollectionsPostHog DocsSelf-host PostHog