AnalyticDB PostgreSQL助力彩數(shù)實現(xiàn)全文檢索加工及分析
本文以彩數(shù)業(yè)務(wù)場景展示云原生數(shù)據(jù)倉庫AnalyticDB PostgreSQL版如何實現(xiàn)一站式全文檢索實時分析業(yè)務(wù)。
背景信息
彩數(shù)(上海)商務(wù)咨詢有限公司是韓國三星集團旗下第一企劃公司全資控股中國子公司,上海市專精特新企業(yè),主要業(yè)務(wù)是從社交媒體、新聞和電子商務(wù)網(wǎng)站采集分析數(shù)據(jù),包括社交數(shù)據(jù)分析,電商數(shù)據(jù)分析,問卷分析,埋點數(shù)據(jù)分析等,基于此為國際大型企業(yè)用戶提供實時性營銷咨詢報告。典型用戶有韓國現(xiàn)代,寶馬,三星,葛蘭素史克,拜耳等。
本文案例的業(yè)務(wù)背景為:某產(chǎn)品銷售平臺經(jīng)過長時間經(jīng)營,存在大量產(chǎn)品A的使用評價歷史數(shù)據(jù),同時每日還不斷收到該產(chǎn)品新的評價數(shù)據(jù),也稱為每日增量數(shù)據(jù)。現(xiàn)在該平臺希望將每日新評價與歷史評價信息寫入AnalyticDB PostgreSQL版,進行數(shù)據(jù)加工并從多維度分析客戶對產(chǎn)品的評價。
數(shù)據(jù)寫入或同步
案例中定義產(chǎn)品A的用戶評論信息表為product_customer_reply
,表結(jié)構(gòu)設(shè)計如下:
CREATE TABLE product_customer_reply (
customer_id INTEGER, -- 用戶ID
gender INTEGER, -- 性別
age INTEGER, -- 年齡
---
--- 可包含用戶的相關(guān)信息。
---
reply_time TIMESTAMP, -- 評論時間
reply TEXT -- 評論內(nèi)容
) DISTRIBUTED BY(customer_id);
如果業(yè)務(wù)數(shù)據(jù)是已經(jīng)處理好的格式化數(shù)據(jù)文件,可以通過COPY命令批量加載數(shù)據(jù)。例如,使用如下命令指定分隔符加載數(shù)據(jù)文件至AnalyticDB PostgreSQL版:
\COPY product_customer_reply FROM '</path/localfile>' DELIMITER as '|';
增量數(shù)據(jù)部分同樣可以使用COPY命令批量攢批數(shù)據(jù)加載,也可以結(jié)合應(yīng)用程序使用AnalyticDB PostgreSQL版 Client SDK攢批寫入。詳情請參見基于Client SDK數(shù)據(jù)寫入。
此外,如果業(yè)務(wù)數(shù)據(jù)已使用TP數(shù)據(jù)庫,那么可以通過DTS服務(wù)進行表結(jié)構(gòu)或全量數(shù)據(jù)同步,也可以配置增量同步實時更新數(shù)據(jù)。
全文檢索
使用全文檢索功能前,首先要對中文分詞進行配置。AnalyticDB PostgreSQL版默認(rèn)對中文分詞進行了基本配置,一般情況下可以直接使用中文分詞功能即可,當(dāng)然也應(yīng)結(jié)合業(yè)務(wù)對中文分詞進行定制化配置。例如,本案例中期望中文分詞能將產(chǎn)品名,品牌名這些非默認(rèn)分詞加入自定義詞庫,示例如下。
-- 添加自定義分詞
INSERT INTO zhparser.zhprs_custom_word VALUES('產(chǎn)品A');
INSERT INTO zhparser.zhprs_custom_word VALUES('品牌A');
隨著業(yè)務(wù)的增長,業(yè)務(wù)數(shù)據(jù)量增加、分詞數(shù)量增加都有可能使得全文檢索查詢執(zhí)行速度變慢。例如,以下是一個查詢篩選所有評論中,對產(chǎn)品A好評并有再次購買的潛在客戶。
SELECT count(*) FROM product_customer_reply WHERE to_tsvector('zh_cn', reply) @@ to_tsquery('zh_cn','產(chǎn)品A<1,10>購買') AND to_tsvector('zh_cn', reply) @@ to_tsquery('zh_cn','產(chǎn)品A<1,10>好');
當(dāng)數(shù)據(jù)量增長后,該查詢耗時為:
SELECT count(*) FROM product_customer_reply WHERE reply_ts @@ to_tsquery('zh_cn','產(chǎn)品A<1,10>購買') AND reply_ts @@ to_tsquery('zh_cn','產(chǎn)品A<1,10>好');
count
--------
428571
(1 row)
Time: 7625.684 ms (00:07.626)
您可以通過以下方式,加速查詢。
方式一:對文本列
reply
創(chuàng)建GIN索引,加快全文檢索對reply
列查詢的速度:CREATE INDEX on product_customer_reply USING GIN (to_tsvector('zh_cn',reply));
再次查詢,可以看到查詢時間下降:
SELECT count(*) FROM product_customer_reply WHERE to_tsvector('zh_cn', reply) @@ to_tsquery('zh_cn','產(chǎn)品A<1,10>購買') AND to_tsvector('zh_cn', reply) @@ to_tsquery('zh_cn','產(chǎn)品A<1,10>好'); count -------- 428571 (1 row) Time: 4539.930 ms (00:04.540)
方式二:對文本列
reply
創(chuàng)建tsvector,減少全文檢索的查詢計算工作量。例如創(chuàng)建類型為tsvector的reply_ts
列,存放reply
列的分詞數(shù)據(jù):ALTER TABLE product_customer_reply ADD COLUMN reply_ts tsvector;
同樣對于
reply_ts
創(chuàng)建GIN索引:CREATE INDEX ON product_customer_reply USING GIN (reply_ts);
查詢時間顯著下降:
SELECT count(*) FROM product_customer_reply WHERE reply_ts @@ to_tsquery('zh_cn','產(chǎn)品A’<1,10>‘購買') AND reply_ts @@ to_tsquery('zh_cn','產(chǎn)品A’<1,10>‘好'); count -------- 428571 (1 row) Time: 465.849 ms
通過合理地配置全文檢索、設(shè)計表結(jié)構(gòu)、使用索引,顯著地提升了案例中全文檢索的查詢性能。
數(shù)據(jù)加工
完成全文檢索設(shè)計后,可以批量加工產(chǎn)品的所有評論數(shù)據(jù),將文本數(shù)據(jù)的特征、分組特性提取出來進行分析查詢。數(shù)據(jù)加工任務(wù)可能涉及到全量數(shù)據(jù)的大量SQL處理,因此可以使用存儲過程來控制加工任務(wù)。例如,創(chuàng)建ts_search_detail
表,存放一些列的全文檢索查詢條件:
CREATE TABLE ts_search_detail (
search_id INTEGER,
ts_search_text TEXT
) DISTRIBUTED BY(id);
此外創(chuàng)建proc_results
表用于存放加工后的結(jié)果。本文創(chuàng)建一張經(jīng)過全文檢索分析后的,用戶ID、性別、年齡信息的明細(xì)表:
CREATE TABLE proc_results (
id INTEGER,
gender INTEGER,
age INTEGER,
search_id INTEGER
) DISTRIBUTED BY(id);
創(chuàng)建存儲過程ts_proc_jobs
,逐條加工ts_search_text
中不同的全文檢索條件,并將結(jié)果存放至proc_results
:
CREATE OR REPLACE PROCEDURE ts_proc_jobs()
AS $$
DECLARE
ts_search record;
proc_query text;
BEGIN
FOR ts_search IN (SELECT ts_search_text, search_id FROM ts_search_detail) LOOP
proc_query := '';
proc_query := 'INSERT INTO proc_results (id, gender, age, search_id)
SELECT customer_id, gender, age, '
|| ts_search.search_id
|| 'FROM product_customer_reply WHERE '
|| ts_search.ts_search_text;
execute(proc_query);
commit;
raise notice 'search id % finish', ts_search.search_id;
END LOOP;
END;
$$
LANGUAGE 'plpgsql';
完成加工后的數(shù)據(jù),可以根據(jù)業(yè)務(wù)需求進行復(fù)雜關(guān)聯(lián)查詢分析、全文檢索分析等業(yè)務(wù)。