2. 核心概念:事件、用户、会话、属性、时间戳的定义与关系

好,我们直接进入正题。用户行为流产品化设计,说白了就是围绕五个核心概念搭起来的:事件、用户、会话、属性、时间戳。这五个东西你搞清楚了,后面所有模型、管道、分析逻辑都是它们的排列组合。

我记得刚带团队做用户行为平台时,有个同事把「事件」和「属性」混在一起存,结果查询时维度拆不开,整张表重做。嗯,这种坑踩过一次你就记住了。

2.1 事件(Event)—— 行为的最小原子

事件就是用户在系统里做的一件事。一次点击、一次滑动、一次支付提交,都是事件。它不可再分,是行为流的最小单元。

我个人习惯把事件分为三类:

类型 举例 特点
自动采集事件 页面浏览、App 启动/退出 SDK 自动触发,无需业务埋点
业务事件 加入购物车、提交订单、支付成功 需手动埋点,携带业务上下文
虚拟事件 「高价值用户注册」= 注册事件 + 首充事件 由多个原始事件组合而成,不在原始流中存储
我的经验:事件命名一定要统一规范。我曾经接手过一个项目,同一个「支付成功」事件,iOS 叫 pay_success,Android 叫 payment_ok,后端叫 order_paid。后面做漏斗分析时,三个事件对不上,白白浪费两天做映射。建议团队从一开始就定好 {对象}_{动作} 的命名规则,比如 product_add_to_cart。

2.2 用户(User)—— 行为的载体

用户是事件的发起者。没有用户,事件就是孤立的噪声。

这里有个关键问题:用户怎么标识?

  • 登录用户:用 user_id,稳定可靠。
  • 未登录用户:用设备 ID(device_id)或 Cookie ID。
  • 跨设备用户:需要 ID 映射表(ID-Mapping),把手机、电脑、小程序的同一用户关联起来。

你想想看,如果用户没登录就加购了商品,登录后又提交了订单,这两个事件如果分属两个不同的「用户」,那转化率就永远算不对。这就是为什么用户身份合并是行为流系统的第一道坎。

避坑指南:我曾经在 ID-Mapping 上犯过一个低级错误——用 user_id 覆盖了 device_id 的历史事件。结果用户换设备后,之前的行为全丢了。正确的做法是保留原始标识,用「用户图谱」做实时关联,而不是直接覆盖。

2.3 会话(Session)—— 行为的自然分段

会话把连续的事件切分成有意义的「段」。一次会话代表用户一次连续的、有目的的交互过程。

怎么切?常见策略:

  • 超时切分:事件间隔超过 30 分钟(或自定义时长),自动开启新会话。
  • 午夜切分:跨天时强制新会话。
  • 场景切分:从首页到详情页到支付页,算一次会话;切到后台再切回来,算新会话。

我个人习惯用「超时 + 场景」组合策略。纯超时切分会把用户中午用和晚上用算成一次会话,这显然不合理。

核心要点:会话 ID 必须在客户端生成,随第一个事件一起上报。不要在服务端事后拼接,否则网络延迟会导致会话归属错乱。

2.4 属性(Properties)—— 事件的上下文

属性是事件的「形容词」。事件本身只告诉你「做了什么」,属性告诉你「在什么条件下做的」。

属性分两类:

属性类型 示例 说明
事件属性 商品 ID、价格、页面来源 随事件一起上报,只对该事件有效
用户属性 会员等级、注册渠道、性别 绑定在用户上,所有事件共享

举个例子:用户「点击购买」这个事件,事件属性里带着 product_id: 1001、price: 299,用户属性里带着 vip_level: gold。这样你就能分析「黄金会员是否更倾向于购买 200-300 元价位的商品」。

我的建议:属性值尽量用枚举或标准化格式。比如价格统一用「分」为单位,避免前端传 299、后端传 299.00、服务端传 299.0 这种混乱。我曾经因为价格单位不统一,导致一个「客单价分布」报表跑出来全是错的,排查了整整一个下午。

2.5 时间戳(Timestamp)—— 行为的坐标轴

时间戳是事件在时间轴上的坐标。没有时间戳,事件就是一盘散沙,你没法排序、没法算间隔、没法做漏斗。

这里有两个时间戳必须区分:

  • 客户端时间戳:事件发生时的本地时间。问题在于用户可能改了系统时间,或者时区不对。
  • 服务端时间戳:事件到达服务器的时间。问题在于网络延迟会导致时间偏差。

我个人习惯的做法是:双时间戳都存。客户端时间戳用于行为序列分析(用户真实操作顺序),服务端时间戳用于数据完整性校验(防止客户端篡改)。分析时以客户端时间戳为主,但做异常检测时会比对两者差值。

注意:千万不要只用服务端时间戳做用户行为排序。网络不好时,事件 B 可能比事件 A 先到服务器,排序就全乱了。我见过一个团队因此把「用户先加购后支付」分析成了「先支付后加购」,转化漏斗直接反了。

2.6 五者关系 —— 一张图说清楚

这五个概念不是孤立的。它们的关系可以用一句话概括:一个用户在一次会话中,在某个时间点触发了一个事件,并携带了若干属性。

下面这张图展示了它们之间的逻辑关系:

用户行为流核心概念关系图 用户行为流核心概念关系图 用户 (User) 会话 (Session) 事件 (Event) 时间戳 (Timestamp) 属性 (Properties) 1 个用户有 N 个会话 1 个会话有 N 个事件 标记发生时刻 携带上下文 一句话总结 一个用户在一次会话中,在某个时间点触发了一个事件,并携带了若干属性。

2.7 数据模型示例

最后,我们看看这五个概念在数据层面怎么落地。下面是一个典型的事件表结构:

-- 用户行为事件表(简化版)
CREATE TABLE user_behavior_events (
    event_id        BIGINT PRIMARY KEY,        -- 事件唯一 ID
    user_id         VARCHAR(64) NOT NULL,       -- 用户标识
    session_id      VARCHAR(64) NOT NULL,       -- 会话 ID
    event_name      VARCHAR(128) NOT NULL,      -- 事件名称
    event_time      DATETIME NOT NULL,          -- 客户端时间戳
    server_time     DATETIME NOT NULL,          -- 服务端时间戳
    event_props     JSON,                       -- 事件属性(JSON 格式)
    user_props      JSON                        -- 用户属性(快照)
);

你看,五个概念在这张表里全齐了。每一行记录就是一个完整的行为原子。

关键设计决策:用户属性为什么要存快照?因为用户属性会变。今天他是普通会员,明天升级成黄金会员。如果不存快照,你回溯分析昨天的行为时,看到的却是今天的会员等级,分析结果就失真了。我个人习惯在事件入库时,把当前用户属性一并写入,这样每个事件都自带「当时的用户画像」。