1. 概述

除了基於已經埋點的屬性來直接建立虛擬屬性之外,我們還可以結合第三方維度表來建立更復雜的虛擬屬性應用。

假設我們在神策分析中有一個 pay_order 事件,同時該事件有 product_id、product_name 等屬性。現在我們希望在分析的時候使用 product 的更多其它維度來進行分析(例如 product_manufacturer),但是這些維度並沒有在埋點的時候打入神策系統中,這個時候就可以引入維度表來滿足這個需求。

目前你可在「元數據」-「維度表」中查看你已經在後台自定義的維度表和 items 表中已有的屬性資訊。如需使用 items 表的屬性建立的虛擬屬性,那麼需要先在後台建立 items 和 events 表的關聯。

2. 使用 items 表作為維度表

如果希望啟用 items 表,首先需要透過 SDK 提供的 itemSet 介面進行 Item 資訊的上報。以 Java SDK 為例:

Map<String, Object> properties = new LinkedHashMap<>();
properties.put("product_name", "iPhone 8");
properties.put("product_manufacturer", "Apple");
properties.put("price", 998.88);
// 參數分別為 item_type、item_id、自定義屬性
sensorsAnalytics.itemSet("product", "T12345", properties);
JAVA

注意:item_type 可以用於區分不同的 item 類型,比如 movie、muisic 等,item_id 區分同一個 item 類型下面的不同的 Item。

然後,我們就可以在自定義查詢功能中使用這個表:

SELECT * FROM items LIMIT 10
SQL

接下來我們建立 items 和 events 表的關聯:

spadmin external_view external_dimension_table add \
-p default \
-t items \
-e "events.product_id = items.item_id AND items.item_type = 'product'"
BASH

注意:這裡是使用 product_id 欄位進行關聯,同時限定了 item_type。如果同時存在多種 item_type,那麼需要建立多次不同的關聯,具體請參考 2.3 中的例子。

最後,使用 items 表中的 product_manufacturer 來建立虛擬屬性即可:

spadmin external_view external_property add \
-p default \
-n product_manufacturer \
-c '產品製造商' \
-e items.product_manufacturer \
-t STRING
BASH

至此,我們已經可以在神策系統的所有分析功能中使用 pay_order 進行分析的時候,看到 product_manufacturer 屬性,並使用這個屬性進行任意的分析工作。

如果需要對維度表和虛擬屬性進行刪除、更新等管理操作,可以直接執行不帶參數的命令查看相關的幫助:

spadmin external_view external_property 
BASH

3. 自定義維度表

除了引用 items 表之外,我們也可以手動建立維度表。在這個例子中,我們使用一張 product_info 的維度表來作為例子。首先我們需要在 impala 中建立這樣一張表:

注意:這裡的維度表必須使用 Kudu 或者 HDFS 的 Parquet 文件格式來儲存,否則無法支援全部特性。

CREATE DATABASE dimensions;

CREATE TABLE dimensions.product_info (
	product_id STRING NOT NULL,
	product_manufacturer STRING NULL,
	PRIMARY KEY (product_id)
)
PARTITION BY HASH (product_id) PARTITIONS 3
STORED AS KUDU TBLPROPERTIES ('kudu.master_addresses'='${kudu_master_host}:7051');
SQL

可以透過此命令取得 kudu master 的地址。

#選擇 master_address 的 value
spadmin config get client -m kudu
BASH

然後,我們需要準備好這張維度表的數據,通常應該是從其它業務數據庫或者數據倉庫中匯入進來。具體可以使用 impala-shell 匯入 SQL 文件,或者 JDBC 等多種方式來進行,例如我們可以直接插入幾條數據:

INSERT INTO dimensions.product_info VALUES ('124', 'Xiaomi'), ('123', 'Apple');
SQL

如果需要插入的數據量比較大,建議使用批量文件匯入的方式。首先需要建立一個文字格式的表,並指定分隔符號:

CREATE TABLE dimensions.raw_csv_product_info (
	product_id STRING,
	product_manufacturer STRING
) ROW FORMAT DELIMITED FIELDS TERMINATED by ',' LOCATION '/tmp/raw_csv_product_info/';
SQL

然後上傳已經準備好的逗號分隔的文字數據文件:

hdfs dfs -put data.csv /tmp/raw_csv_product_info/
BASH

更新 CSV 表,並執行 INSERT 把數據匯入 Kudu 即可。

REFRESH dimensions.raw_csv_product_info;
INSERT INTO dimensions.product_info SELECT * FROM dimensions.raw_csv_product_info;
SQL

在準備好維度表數據之後,我們用 sa_view_tools 工具來把該維度表加入神策系統中:

spadmin external_view external_dimension_table add \
-p default \
-t dimensions.product_info \
-e 'events.product_id = dimensions.product_info.product_id'
BASH

其中,-p 是神策系統的專案名稱,-t 參數是維度表的完整名稱, -e 參數表示該維度表和事件表(events)的關聯關係,即 SQL 中進行 JOIN 的條件。

在定義了維度表之後,我們就可以把該維度表(即 product_info)中的具體欄位作為一個虛擬屬性加入神策系統中:

spadmin external_view external_property add \
-p default \
-n product_manufacturer \
-c '產品製造商' \
-e dimensions.product_info.product_manufacturer \
-t STRING
BASH

至此,我們已經可以在神策系統的所有分析功能中使用 pay_order 進行分析的時候,看到 product_manufacturer 屬性,並使用這個屬性進行任意的分析工作。

4. 一張維度表使用不同的關聯條件

如果同一張維度表需要使用不同的關聯條件,那麼需要在新增維度表的時候使用別名。具體的方式為在原有的表面後面加上 #1 或者其它標識符號。例如:

spadmin external_view external_dimension_table add \
-p default \
-t dimensions.product_info#1 \
-e 'events.item_id = dimensions.product_info#1.item_id'
BASH

增加完成之後,後面在其它命令引用這張表時也需要使用 dimensions.product_info#1

5. 限制與約束

5.1. 查詢性能

由於關聯維度表需要使用 JOIN,雖然神策的查詢引擎已經對這個類型的 JOIN 做了一定程度的優化,但是相比直接使用原始的事件屬性依然會有比較顯著的性能降低,具體的性能和維度表的大小、JOIN 的條件等都有關係。因此,我們建議在直接使用事件屬性可以滿足需求的情況下,不要使用維度表;同時,應當保證維度表的行數在百萬以內,以盡量降低 JOIN 帶來的額外性能損耗。

5.2. 快取一致性

目前為止,神策系統的快取機制依然是基於事件數據的變更來實現的,這個機制中暫時沒有考慮到維度表的數據變化帶來的影響。因此,如果維度表的數據發生了變更(例如進行 Update 或者 Insert),查詢結果可能還會使用舊的快取數據,這個時候需要強制重整才能得到正確的結果。

6. 測試維度表的關聯條件

對於比較複雜的關聯條件,建議先使用 impala-shell 執行 SQL 來進行測試,以保證結果的正確性。可以直接使用 JOIN 語法進行,例如:

/*sa(test_project)*/ 
SELECT dimensions.product_info.product_manufacturer FROM
events 
LEFT JOIN dimensions.product_info
ON events.product_id = dimensions.product_info.product_id
WHERE date = CURRENT_DATE()
LIMIT 100
SQL