选取合适的用户标识对于提高用户行为分析的准确性有非常大的影响,尤其是漏斗、留存、Session 等用户相关的分析功能。因此,我们在进行任何数据接入之前,都应当先确定如何来标识用户。下面会介绍神策分析用户标识的原理,以及几种典型情况下的用户标识方案。

注意:不要在线上页面直接切换不同项目的数据接收地址,会导致首日首次,ID 异常。建议在线下测试时数据发到测试项目,没问题的话,线上采集的数据直接发往正式项目。

1. 基本概念

神策分析使用 神策 ID (即 events 表里的 user_id 和 users 表里的 id )来对每个产品的用户进行唯一的标识,而 神策 ID 是基于 distinct_id 按照一定规则生成的。一般来说,典型的 distinct_id 有以下两种:

1.1. 设备 ID

需要注意的是,设备 ID 并不一定是设备的唯一标识。例如 Web 端的 Cookies 有可能被清空(例如各种安全卫士),而 iOS 端的 IDFV 在不同厂商的 App 间是不同的。不过神策的客户端 SDK 已经做了各种处理。

SDK 类型规则
Android1.10.5 之前版本,默认使用 UUID(例如:550e8400-e29b-41d4-a716-446655440000),App 卸载重装 UUID 会变,为了保证设备 ID 不变,可以通过配置使用 AndroidId(例如:774d56d682e549c3);1.10.5 及之后的版本 SDK 默认使用 AndroidId 作为设备 ID,如果 AndroidId 获取不到则获取随机的 UUID。
iOS1.10.18 及之后版本,如果 App 引入了 AdSupport 库,SDK 默认使用 IDFA 作为匿名 ID。1.10.18 之前版本,神策 SDK 会优先使用 IDFV(例如:1E2DFA10-236A-47UD-6641-AB1FC4E6483F),如果 IDFV 获取失败,则使用随机的 UUID(例如:550e8400-e29b-41d4-a716-446655440000),一般情况下都能够获取到 IDFV。如果使用 IDFV 或 UUID ,当用户卸载重装 App 时设备 ID 会变。也可以通过配置使用 IDFA(例如:1E2DFA89-496A-47FD-9941-DF1FC4E6484A),如果开启 IDFA ,神策 SDK 会优先获取 IDFA,如果获取失败再尝试获取 IDFV。使用 IDFA 能避免用户在重装 App 后设备 ID 发生变化的情况。
JavaScript默认情况下使用 cookie_id(例如:15ffdb0a3f898-02045d1cb7be78-31126a5d-250125-15ffdb0a3fa40a),cookie_id 为神策 JavaScript SDK 默认生成的,存贮在浏览器的 cookie 中,规则为五段不同含义的字段拼接而成来保证唯一性,其中包括两段时间戳,一段屏幕宽高,一段随机数,一段 UA 值。
微信小程序默认情况下使用 UUID(例如:1558509239724-9278730-00c1875d5f63f8-41373096),但是删除小程序,UUID 会变。为了保证设备 ID 不变,建议获取并使用 openid(例如:oWDMZ0WHqfsjIz7A9B2XNQOWmN3E)。 如果选择使用 openid 的话,请注意【操作暂存】,由于获取 openid 是一个异步的操作,但是冷启动事件等会先发生,这时候这个冷启动事件的 distinct_id 就不对了。所以我们会把先发生的操作,暂存起来,等获取到 openid 等后调用 sa.init() 后才会发送数据。 openid 的获取和操作暂存的方法,请参考此文档微信小程序 SDK

1.2. 登录 ID

登录 ID 通常是业务数据库里的主键或其它唯一标识。所以 登录 ID,相对来说更精确更持久。但是,用户在使用时不一定注册或者登录,而这个时候是没有登录 ID 的。
登录 ID 会存在 users 表里的 second_id 字段
登录 ID  一旦确定尽量不要修改,如果需要修改请联系神策值班同学。

需要特别说明的是,神策分析里的用户是发生事件的主体,不一定是终端用户,也可以是一个企业、商家甚至是一辆汽车,需要根据具体的分析需求灵活决定。

1.3. 方案详解

请注意,以下各方案之间互不兼容,请务必在正式接入之前选择一个最合适的方案

1.3.1. 方案一:只使用设备 ID

1.3.1.1. 适用场景

适合没有用户注册体系,或者极少数用户会进行多设备登录的产品,如工具类产品、搜索引擎、部分电商等。这也是绝大多数其它数据分析产品唯一提供的方案。

1.3.1.2. 局限性

  • 同一用户在不同设备使用会被认为不同的用户(神策 ID 不同,跟设备 ID 关联),对后续的分析统计有影响。
  • 不同用户在相同设备使用会被认为是一个用户(神策 ID 相同,因为设备 ID 相同),也对后续的分析统计有影响。
  • 但如果用户跨设备使用或者多用户共用设备不是产品的常见场景的话,可以忽略上述问题。

1.3.1.3. 实施方法

  • 直接使用客户端 SDK 产生的 设备 ID 作为 distinct_id 即可,不需要进行特殊处理。如果不希望使用神策 SDK 默认的 设备 ID 生成规则,可以直接调用 identify 接口来传入自定义的 设备 ID ,该方法建议在 SDK 初始化完成之后立即调用。
  • 如果是有用户注册体系的产品,可以额外在所有 Event 里加入 login_user_id 属性标识用户的正式 ID,这样也可以筛选出某个用户的具体行为,或者单独看登录前/登录后的用户行为。

1.3.1.4. 案例

注意:X,Y 均有用户属性,所以即使 second_id 没值,也可以以 first_id 的形式写进用户表。

详细步骤说明如下:

1. 某用户在小米手机上新安装了 App,并进行了一系列操作,SDK 生成的设备 ID 为 X,发送的 distinct_id 为 X,对应分配的神策 ID 为 1,若客户端 SDK 调用接口 profile_set ,则将神策 ID 1 、设备 ID X 存入 users 表的 id, first_id 字段。
2. 该用户进行了注册并登录,设备未变,发送的 distinct_id 仍为 X,所以神策 ID 仍为 1。
3. 该用户登录之后继续进行一系列操作,发送的 distinct_id 仍 为 X,所以神策 ID 仍为 1。
4. 该用户退出登录并进行了一系列操作,发送的 distinct_id 仍为 X,所以神策 ID 仍为 1。
5. 该用户把手机送给朋友了,朋友用自己的账号登录(已注册但未接入神策系统)设备 X,但发送的 distinct_id 仍为 X,所以神策 ID 仍为 1。
6. 之后,该用户的朋友一直使用账号 B 在设备 X 上进行了一系列操作,由于设备未变,所以神策 ID 仍为 1。
7. 该用户更换了一个新的苹果手机,并进行一系列操作,由于设备 ID 变为 Y,发送的 distinct_id 为 Y,分配的神策 ID 为 2,若客户端 SDK 调用接口 profile_set ,则将神策 ID 2 、设备 ID Y 存入 users 表的 id, first_id 字段。
8. 该用户在苹果手机上使用账号 A 进行登录,发送的 distinct_id 为 Y,所以神策 ID 仍为 2。
9. 该用户登录之后的后续操作,都以神策 ID 2 标识,只要不更换设备。

在上述案例中,仅使用设备 ID 识别用户的好处就是逻辑很简单,当然局限性也很明显:

  • 当用户换手机后,用户换手机前后的行为无法关联上。
  • 当用户把手机送给朋友后,朋友的行为却仍记在该用户下。

1.3.2. 方案二:关联设备 ID 和登录 ID(一对一)

仅使用 设备 ID 标识用户虽然简单,但是对于某些应用场景这种方式不够准确,因此神策分析提供了另一种关联 设备 ID 和 登录 ID 的方案,在一定程度上融合 设备 ID 和 登录 ID,从而实现更准确的用户追踪。

1.3.2.1. 适用场景

成功关联设备 ID 和登录 ID 之后,用户在该设备 ID 上或该登录 ID 下的行为就会贯通,被认为是一个神策 ID 发生的。在进行事件、漏斗、留存等用户相关分析时也会算作一个用户。

关联设备 ID 和登录 ID 的方法虽然实现了更准确的用户追踪,但是也会增加埋点接入的复杂度。所以一般来说,我们建议只有当同时满足以下条件时,才考虑进行 ID 关联:

  1. 需要贯通一个用户在一个设备上注册前后的行为。
  2. 需要贯通一个注册用户在不同设备上登录之后的行为。

1.3.2.2. 局限性

  • 一个设备 ID 只能和一个登录 ID 关联,而事实上一台设备可能有多个用户使用。
  •  一个登录 ID 只能和一个设备 ID 关联,而事实上一个用户可能用一个登录 ID 在多台设备上登录。
  • 如果不遵循神策接口调用顺序,可能会导致部分用户标识异常(例如历史数据导入时),影响数据统计的准确性。

1.3.2.3. 客户端接入实施方法

客户端接入是指使用 iOS / Android / JavaScript 等 SDK 进行埋点,具体调用流程如下:

  1. 在 SDK 初始化完成之后,神策的 SDK 会自动生成一个设备 ID 作为用户标识。
  2. 在用户注册成功、登录成功 、初始化 SDK(如果能获取到登录 ID)的时候,客户端主动调用 login(登录 ID) 接口。
  3. 在用户注销的时候,有几种选择:
    1. 不做任何操作,这样的话相当于神策会继续使用之前的用户标识来进行追踪。如果没有特殊情况,一般建议选择该方式。
    2. 主动调用 logout() 方法,这样会清空登录 ID,重新使用设备 ID 作为用户标识,一般情况没有必要选择此方式。
    3. 对于 JavaScript SDK,还可以调用 logout(true) 方法,该方法除了清空 登录 ID 之外,还会重新初始化设备 ID。

备注一:

SDK 类型 前端获取前端缓存中 ID 的方法
安卓通过 getAnonymousId 方法 获取神策分析 SDK 分配的 匿名 ID,String AnonymousId=SensorsDataAPI.sharedInstance().getAnonymousId();
iOS通过 anonymousId 方法可获取神策分析 iOS SDK 分配的 匿名 ID,获取当前用户的匿名id NSString  anonymousId = [[SensorsAnalyticsSDK sharedInstance] anonymousId];(swift 代码示例:let anonymousId:String = SensorsAnalyticsSDK.sharedInstance().anonymousId())。
JavaScript获取匿名 ID 的方法 sensors.quick('getAnonymousID'); 返回匿名 id (SDK 版本 1.13.4 及以上支持)
微信小程序sensors.getAnonymousID();

1.3.2.4. 服务端接入实施方法

服务端接入包括使用 Java / Python / PHP 等 SDK,以及直接使用 BatchImporter / LogAgent / FormatImporter 等工具进行导入的情况,具体流程如下:

  • 在进行服务端埋点或者历史数据导入时,如果当前在 track 或者 profile_set 等接口里传入的 distinct_id 是一个 登录 ID,那么 is_login_id 的参数值必须为 true,来告诉神策这是一个 登录 ID 产生的行为,以 Java SDK 为例:
  • 如果是 登录 ID 产生的行为:sa.track(registerId, true, "SubmitOrderDetail", properties);
  • 如果是 匿名 ID 产生的行为:sa.track(deviceId, false, "SubmitOrderDetail", properties);
  • 对于任意一个 登录 ID ,只要导入过任意数据,那么该 登录 ID 后面将不能和任何 设备 ID 进行关联。因此在进行历史数据(即接入神策之前产生的数据)的导入时,建议按照下面的方式操作:
    • 先进行正常的 SDK 接入,并且保证所有用户都正常的通过了 login/track_signup 接口进行用户关联,在运行一段时间之后再导入历史数据,因为这个时候大部分活跃用户都应该已经成功进行了关联。
    • 如果历史数据中存在 登录 ID  和其对应 设备 ID 的对应关系,那么也可以先把这批数据构造 track_signup 请求进行导入,然后再导入具体的用户行为或者用户属性数据即可。
    • 由于客户端埋点存在一定的数据丢失概率,我们建议开发者也在服务端的注册接口里调用 track_signup 方法,将新用户的 设备 ID 和 登录 ID 进行关联,以实现更准确的用户识别。

1.3.2.5. 案例

注意:Y 有用户属性,所以即使 second_id 没值,也可以以 first_id 的形式写进用户表。

详细步骤说明如下:

1. 某用户在小米手机上新安装了 App,并进行了一系列操作,SDK 生成的设备 ID 为 X,发送的 distinct_id 为 X,对应分配的神策 ID 为 1,若客户端 SDK 调用接口 profile_set ,则将神策 ID 1 、设备 ID X 存入 users 表的 id, first_id 字段。
2. 该用户进行了注册并登录,其登录 ID 为 A,这里会调用 SDK 的 login(客户端)或 track_signup 接口(服务端),设备 ID X 和登录 ID A 关联成功,并将登录 ID A 存入 users 表的 second_id 字段,神策 ID 仍为 1。
3. 该用户登录之后继续进行一系列操作,发送的 distinct_id 为 A,神策 ID 仍为 1。
4. 该用户退出登录并进行了一系列操作,发送的 distinct_id 为 A,SDK 不调用任何方法,仍使用神策 ID 1 来标识当前用户(因为登录 ID A 已与神策 ID 1 绑定)。
5. 该用户把手机送给朋友了,朋友用自己的账号(已注册但未接入神策系统)登录设备 X,登录 ID 为 B,此时神策 SDK 尝试将设备 ID X 与登录 ID B 进行关联,由于 X 已与 A 关联,所以此次关联失败,同时会分配一个新的神策 ID 2 来标识此用户,并将登录 ID B 同时存入 users 表的 first_id 和 second_id 字段(用户的朋友账号上之前未关联过别的设备,且首次登录设备关联失败,则将登录 ID 同时记录到 first_id 上)。
6. 之后,该用户的朋友一直使用账号 B 在设备 X 上进行了一系列操作,发送的 distinct_id 为 B,神策都使用神策 ID 2 来标识此用户(因为登录 ID B 已与神策 ID 2 绑定)。
7. 该用户更换了一个新的苹果手机,并进行一系列操作,由于尚未登录,此时神策使用全新的设备 ID Y 来标识此设备,发送的 distinct_id 为 Y,对应分配的神策 ID 为 3,若客户端 SDK 调用接口 profile_set ,则将神策 ID 3 、设备 ID Y 存入 users 表的 id, first_id 字段。
8. 该用户在苹果手机上使用账号 A 进行登录,此时神策将尝试将设备 ID Y 与登录 ID A 进行关联,由于 A 已经与 X 关联,因此会关联失败,但是依然会切换到以 A 为登录 ID 的用户,其对应的神策 ID 依然为 1。
9. 该用户登录之后的后续操作,发送的 distinct_id 为 A,所以仍以神策 ID 1 标识。

在上述案例中,很大程度上已经实现了跨设备的用户贯通,但仍存在局限性:

  • 当用户换手机后,虽然登录账号之后的行为与换手机之前的行为贯通了,但是在新设备上首次登录之前的行为仍没法贯通,仍被识别为新的用户的行为。
  • 当用户把旧手机送给朋友之后,由于旧手机已被关联到自己的登录 ID 了,无法再与朋友的登录 ID 关联。后续使用这台旧手机的用户们,若不登录就操作,则都会被识别为同一个用户(旧手机成功关联的登录 ID )。

1.3.3. 方案三:关联设备 ID 和登录 ID(多对一)

关联设备 ID 和登录 ID(一对一)虽然已经实现了跨设备的用户贯通,但是对于某些应用场景还是不够准确,因此神策分析提供了新的关联方案,支持一个登录 ID 绑定多个设备 ID 的方案,从而实现更准确的用户追踪。

1.3.3.1. 适用场景

一个用户在多个设备上进行登录是一种比较常见的场景,比如 Web 端和 App 端可能都需要进行登录。支持一个登录 ID 下关联多设备 ID 之后,用户在多设备下的行为就会贯通,被认为是一个神策 ID 发生的。

1.3.3.2. 局限性

  • 一个设备 ID 只能和一个登录 ID 关联,而事实上一台设备可能有多个用户使用。
  • 一个设备 ID 一旦跟某个登录 ID 关联或者一个登录 ID 和一个设备 ID 关联,就不能解除(自动解除)。而事实上,设备 ID 和登录 ID 的动态关联才应该是更合理的。

1.3.3.3. 实施方法

客户端和服务端的实施方法与方案二完全相同,神策服务端的处理行为会不一样:

  • 对于已经关联过设备的登录 ID,仍然可以和新的设备 ID 进行关联,并存入users 表新增字段 $device_id_list (关联用户 id 列表) 中。
  • 例⾏任务每天读取 users 表中需要修复的 id 列表,即 $device_id_list。读取过去 2 天的所有 events 数据,找到需要修复的数据。修改其中的 user_id 字段与 profile 表中对应的 id 字段保持一致。

1.3.3.4. 案例

详细步骤说明如下:

1. 某用户在小米手机上新安装了 App,并进行了一系列操作,SDK 生成的设备 ID 为 X,发送的 distinct_id 为 X,对应分配的神策 ID 为 1,若客户端 SDK 调用接口 profile_set ,则将神策 ID 1 、设备 ID X 存入 users 表的 id, first_id 字段。
2. 该用户进行了注册并登录,其登录 ID 为 A,这里会调用 SDK 的 login(客户端)或 track_signup 接口(服务端),设备 ID X 和登录 ID A 关联成功,并将登录 ID A 存入 users 表的 second_id 字段,神策 ID 仍为 1。
3. 该用户登录之后继续进行一系列操作,发送的 distinct_id 为 A,神策 ID 仍为 1。
4. 该用户退出登录并进行了一系列操作,发送的 distinct_id 为 A,SDK 不调用任何方法,仍使用神策 ID 1 来标识当前用户(因为登录 ID A 已与神策 ID 1 绑定)。
5. 该用户把手机送给朋友了,朋友用自己的账号(已注册但未接入神策系统)登录设备 X,登录 ID 为 B,此时神策 SDK 尝试将设备 ID X 与登录 ID B 进行关联,由于 X 已与 A 关联,所以此次关联失败,同时会分配一个新的神策 ID 2 来标识此用户,并将登录 ID B 同时存入 users 表的 first_id 和 second_id 字段(用户的朋友账号上之前未关联过别的设备,且首次登录设备关联失败,则将登录 ID 同时记录到 first_id 上)。
6. 之后,该用户的朋友一直使用账号 B 在设备 X 上进行了一系列操作,发送的 distinct_id 为 B,神策都使用神策 ID 2 来标识此用户(因为登录 ID B 已与神策 ID 2 绑定)。
7. 该用户更换了一个新的苹果手机,并进行一系列操作,由于尚未登录,此时神策使用全新的设备 ID Y 来标识此设备,发送的 distinct_id 为 Y,对应分配的神策 ID 为 3,若客户端 SDK 调用接口 profile_set ,则将神策 ID 3 、设备 ID Y 存入 users 表的 id, first_id 字段。
8. 该用户在苹果手机上使用账号 A 进行登录,此时神策将设备 ID Y 与登录 ID A 进行关联,关联成功,对应的神策 ID 依然为 1,同时将设备 ID Y 添加到 users 表中神策 ID 1 的 $device_id_list 字段。
9. 该用户登录之后的后续操作,发送的 distinct_id 为 A,所以仍以神策 ID 1 标识。

后续修复如下:

  • 由于设备 Y 被关联到登录 ID A 下,修复设备 Y 上登录之前的数据:神策 ID 3 ->神策 ID 1。需要注意的是,对于要修复的数据,使⽤新的 user_id 生成新的 parquet 文件。对于被修复的文件暂时不进行修改,只在索引中进行标记哪些数据已经在源文件中失效。
  • 同时将 users 表中神策 ID 3 的用户属性合并到神策 ID 1 上,并删除users 表中神策 ID 3 这条数据。进行属性合并时,如果神策 ID 1 的用户该属性有值,则不会修改该属性的值;如果神策 ID 1 的用户该属性没值,且神策 ID 3 的用户该属性有值,则将对应的值合并到神策 ID 1 的用户上,并删除 users 表中神策 ID 3 这条数据。

在上述案例中,真正实现了跨设备的用户贯通,通过修复解决了方案二中换手机登录之前的行为贯通问题,但仍存在局限性:

  • 一个设备只能关联到一个登录 ID 下,当用户把旧手机送给朋友之后,由于旧手机已被关联到自己的登录 ID 了,无法再与朋友的登录 ID 关联。后续使用这台旧手机的用户们,若不登录就操作,则都会被识别为同一个用户(旧手机成功关联的登录 ID)。
  • 而事实上,旧手机上后续的匿名登录很难识别到底是谁,可能归为匿名登录之前最近一次登录的用户会更合理一些。

1.4. 方案对比

将上述三个方案放到一起,可以明显看到三种方案的区别,如下表所示:

事件序号 事件神策 ID_方案一神策 ID_方案二神策 ID_方案三
1安装 App111
2登录 App111
3使用 App111
4使用 App111
5登录 App122
6使用 App122
7使用 App233->1
8使用 App211
9使用 App211
  • 方案一:仅使用设备 ID,不管用户是谁,只要设备未变(设备 ID 未变),神策 ID 不变。
  • 方案二:关联设备 ID 和登录 ID(一对一),
    • 当用户换手机后,登录账号之后的行为与换手机之前的行为贯通了,但是在新设备上首次登录之前的行为仍没法贯通,仍被识别为新的用户的行为。
    • 当用户把旧手机送给朋友之后,由于旧手机已被关联到自己的登录 ID 了,无法再与朋友的登录 ID 关联。后续使用这台旧手机的用户们,若不登录就操作,则都会被识别为同一个用户。
  • 方案三:关联设备 ID 和登录 ID(多对一)
    • 当用户把旧手机送给朋友之后,由于旧手机已被关联到自己的登录 ID 了,无法再与朋友的登录 ID 关联。后续使用这台旧手机的用户们,若不登录就操作,则都会被识别为同一个用户。
    • 而事实上,旧手机上后续的匿名登录很难识别到底是谁,可能归为匿名登录之前最近一次登录的用户会更合理一些。

其实,三种方案没有对与错,建议客户结合产品的应用场景以及埋点复杂度来选择合适的方案。

准确的标识用户其实是一个很复杂的问题,神策一直致力于寻求更合理、更准确的方法,满足各种应用场景。


1.5. 常见问题

1.5.1. $is_login_id 参数说明

常见使用场景:历史数据导入的 events 或者 users 数据,服务端 SDK 的 track 或 profile 接口。

属性值含义:true ,表示这条数据中的 distinct_id 字段的值为一个真实的 ID(例如客户的业务 ID),如果在这条数据进入数据库之前,此真实 ID 未和设备 ID 绑定,(绑定操作,前端可参考 login 方法,以 JavaScript SDK 为例,在登录和注册成功后,调用 sensor.login(userid); 来标识真实用户。服务端可参考 tracksignup 方法,以 Java SDK 为例,历史数据导入可参考本文档的 tracksignup 接口。),那么这个真实 ID 会自关联。两种情况:

  1. 如果开启了多对一的话,新的设备 ID 还是可以和这个自关联的真实 ID 关联。
  2. 在未开启多对一的情况下导致这个真实 ID 之后不能再和设备 ID 绑定。

属性值含义:false,表示这条数据中的 distinct_id 字段的值为一个设备 ID(即客户注册登录之前的标识用户的 ID),之后此设备 ID 还可以和一个真实 ID 绑定。