ClickHouse是如何提高留存計(jì)算速度

這篇文章將為大家詳細(xì)講解有關(guān)ClickHouse是如何提高留存計(jì)算速度,文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。

創(chuàng)新互聯(lián)堅(jiān)持“要么做到,要么別承諾”的工作理念,服務(wù)領(lǐng)域包括:成都網(wǎng)站建設(shè)、網(wǎng)站建設(shè)、企業(yè)官網(wǎng)、英文網(wǎng)站、手機(jī)端網(wǎng)站、網(wǎng)站推廣等服務(wù),滿(mǎn)足客戶(hù)于互聯(lián)網(wǎng)時(shí)代的冷水灘網(wǎng)站設(shè)計(jì)、移動(dòng)媒體設(shè)計(jì)的需求,幫助企業(yè)找到有效的互聯(lián)網(wǎng)解決方案。努力成為您成熟可靠的網(wǎng)絡(luò)建設(shè)合作伙伴!

關(guān)于用戶(hù)留存是各大數(shù)據(jù)分析平臺(tái)必不可少的功能,企業(yè)一般用留存率衡量用戶(hù)的活躍情況,也是能直接反應(yīng)產(chǎn)品功能價(jià)值的直接指標(biāo),留存率是衡量用戶(hù)質(zhì)量的最重要指標(biāo)之一,因此計(jì)算各種留存率是數(shù)據(jù)分析取數(shù)的最底層的基本功。所以下面舉幾個(gè)用戶(hù)留存分析的實(shí)戰(zhàn)例子。

1. 準(zhǔn)備

了解目前留存率幾種常規(guī)計(jì)算方法、了解ClickHouse提供retention(cond1, cond2, …)函數(shù)計(jì)算留存率

建表:用戶(hù)基本信息表:login_event

CREATE TABLE login_event --用戶(hù)登錄事件
(
    `accountId` String COMMENT '賬號(hào)的ID', --用戶(hù)唯一ID
    `ds` Date COMMENT '日期' --用戶(hù)登錄日期
)
ENGINE = MergeTree
PARTITION BY accountId
ORDER BY accountId

導(dǎo)數(shù):插入8月份用戶(hù)登錄數(shù)據(jù)

--插入數(shù)據(jù)
insert into login_event values (10001,toDate('2020-08-01'), (10001,toDate('2020-08-08')), (10001,toDate('2020-08-09')), (10001,toDate('2020-08-10')), (10001,toDate('2020-08-12')),
(10001,toDate('2020-08-13')), (10001,toDate('2020-08-14')), (10001,toDate('2020-08-15')), (10001,toDate('2020-08-16')), (10001,toDate('2020-08-17')), (10001,toDate('2020-08-18')),
(10001,toDate('2020-08-20')), (10001,toDate('2020-08-22')), (10001,toDate('2020-08-23')), (10001,toDate('2020-08-24')), (10002,toDate('2020-08-20')), (10002,toDate('2020-08-22')), (10002,toDate('2020-08-23')), (10002,toDate('2020-08-01')), (10002,toDate('2020-08-11')), (10002,toDate('2020-08-12')), (10002,toDate('2020-08-13')), (10002,toDate('2020-08-20')),
(10002,toDate('2020-08-15')), (10002,toDate('2020-08-30')), (10002,toDate('2020-08-20')), (10002,toDate('2020-08-01')), (10002,toDate('2020-08-06')), (10002,toDate('2020-08-24')), (10003,toDate('2020-08-05')), (10003,toDate('2020-08-08')), (10003,toDate('2020-08-09')), (10003,toDate('2020-08-10')), (10003,toDate('2020-08-11')), (10003,toDate('2020-08-13')),
(10003,toDate('2020-08-15')), (10003,toDate('2020-08-16')), (10003,toDate('2020-08-18')), (10003,toDate('2020-08-20')), (10003,toDate('2020-08-01')), (10003,toDate('2020-08-21')),
(10003,toDate('2020-08-22')), (10003,toDate('2020-08-24')), (10003,toDate('2020-08-26')), (10003,toDate('2020-08-25')), (10003,toDate('2020-08-27')), (10003,toDate('2020-08-28')),
(10003,toDate('2020-08-29')), (10003,toDate('2020-08-30')), (10004,toDate('2020-08-01')), (10004,toDate('2020-08-02')), (10004,toDate('2020-08-03')), (10004,toDate('2020-08-04')),
(10004,toDate('2020-08-05')), (10004,toDate('2020-08-08')), (10004,toDate('2020-08-09')), (10004,toDate('2020-08-10')), (10004,toDate('2020-08-11')), (10004,toDate('2020-08-14')),
(10004,toDate('2020-08-15')), (10004,toDate('2020-08-16')), (10004,toDate('2020-08-17')), (10004,toDate('2020-08-19')), (10004,toDate('2020-08-20')), (10004,toDate('2020-08-21')),
(10004,toDate('2020-08-22')), (10004,toDate('2020-08-23')), (10004,toDate('2020-08-24')), (10004,toDate('2020-08-23')),(10004,toDate('2020-08-23')), (10004,toDate('2020-08-25')),
(10004,toDate('2020-08-27')), (10004,toDate('2020-08-30'));

2. 題目分析

計(jì)算某日活躍用戶(hù)的次留、3留、7留、14留、30留,我們將問(wèn)題解決分為三個(gè)步驟:

  • 找到某日活躍用戶(hù)

  • 找到某日活躍用戶(hù)在第2、3、6、13、29日的登錄情況

  • 計(jì)算某日活躍用戶(hù)在第2、3、6、13、29日登錄數(shù),計(jì)算N日留存率

解決方法一:

--計(jì)算出2020-08-01活躍用戶(hù)在第2、3、6、13、29日的留存數(shù),計(jì)算出留存率
SELECT
    ds,
    count(accountIdD0) AS activeAccountNum,
    count(accountIdD1) / count(accountIdD0) AS `次留`,
    count(accountIdD3) / count(accountIdD0) AS `3留`,
    count(accountIdD7) / count(accountIdD0) AS `7留`,
    count(accountIdD14) / count(accountIdD0) AS `14留`,
    count(accountIdD30) / count(accountIdD0) AS `30留`
FROM
( --使用LEFT JOIN 找到2020-08-01當(dāng)日活躍用戶(hù)在第2、3、6、13、29日的登錄用戶(hù)
    SELECT DISTINCT
        a.ds AS ds,
        a.accountIdD0 AS accountIdD0,
        IF(b.accountId = '', NULL, b.accountId) AS accountIdD1,
        IF(c.accountId = '', NULL, c.accountId) AS accountIdD3,
        IF(d.accountId = '', NULL, d.accountId) AS accountIdD7,
        IF(e.accountId = '', NULL, e.accountId) AS accountIdD14,
        IF(f.accountId = '', NULL, f.accountId) AS accountIdD30
    FROM
    (--找出2020-08-01當(dāng)日活躍用戶(hù)
        SELECT DISTINCT
            ds,
            accountId AS accountIdD0
        FROM login_event
        WHERE ds = '2020-08-01'
        ORDER BY ds ASC
    ) AS a
    LEFT JOIN test.login3_event AS b ON (b.ds = addDays(a.ds, 1)) AND (a.accountIdD0 = b.accountId)
    LEFT JOIN test.login3_event AS c ON (c.ds = addDays(a.ds, 2)) AND (a.accountIdD0 = c.accountId)
    LEFT JOIN test.login3_event AS d ON (d.ds = addDays(a.ds, 6)) AND (a.accountIdD0 = d.accountId)
    LEFT JOIN test.login3_event AS e ON (e.ds = addDays(a.ds, 13)) AND (a.accountIdD0 = e.accountId)
    LEFT JOIN test.login3_event AS f ON (f.ds = addDays(a.ds, 29)) AND (a.accountIdD0 = f.accountId)
) AS temp
GROUP BY ds

結(jié)果:
-----------------------------------------
┌─────────ds─┬─activeAccountNum─┬─次留─┬──3留─┬─7留─┬─14留─┬─30留─┐
│ 2020-08-01 │                4 │ 0.25 │ 0.25 │   0 │  0.5 │ 0.75 │
└────────────┴──────────────────┴──────┴──────┴─────┴──────┴──────┘

1 rows in set. Elapsed: 0.022 sec.

解決方法二:

--判斷2020-08-01活躍用戶(hù)在第2、3、6、13、29日的留存數(shù),計(jì)算出留存率,計(jì)算出留存率
SELECT DISTINCT
    b.ds AS ds,
    ifnull(countDistinct(if(a.ds = b.ds, a.accountId, NULL)), 0) AS activeAccountNum,
    ifnull(countDistinct(if(a.ds = addDays(b.ds, 1), b.accountId, NULL)) / activeAccountNum, 0) AS `次留`,
    ifnull(countDistinct(if(a.ds = addDays(b.ds, 2), b.accountId, NULL)) / activeAccountNum, 0) AS `3留`,
    ifnull(countDistinct(if(a.ds = addDays(b.ds, 6), b.accountId, NULL)) / activeAccountNum, 0) AS `7留`,
    ifnull(countDistinct(if(a.ds = addDays(b.ds, 13), b.accountId, NULL)) / activeAccountNum, 0) AS `14留`,
    ifnull(countDistinct(if(a.ds = addDays(b.ds, 29), b.accountId, NULL)) / activeAccountNum, 0) AS `30留`
FROM
  --使用INNER JOIN找出2020-08-01活躍用戶(hù)在后續(xù)1~30日登錄情況
(
    SELECT
        ds,
        accountId
    FROM login_event
    WHERE (ds <= addDays(toDate('2020-08-01'), 29)) AND (ds >= '2020-08-01')
) AS a
INNER JOIN
--找出2020-08-01當(dāng)日活躍用戶(hù)
(
    SELECT DISTINCT
        accountId,
        ds
    FROM test.login3_event
    WHERE ds = '2020-08-01'
) AS b ON a.accountId = b.accountId
GROUP BY ds

結(jié)果:
-----------------------------------------
┌─────────ds─┬─activeAccountNum─┬─次留─┬──3留─┬─7留─┬─14留─┬─30留─┐
│ 2020-08-01 │                4 │ 0.25 │ 0.25 │   0 │  0.5 │ 0.75 │
└────────────┴──────────────────┴──────┴──────┴─────┴──────┴──────┘

1 rows in set. Elapsed: 0.019 sec.

解決方法三:

--根據(jù)數(shù)組下標(biāo)SUM(r[index])獲取2020-08-01活躍用戶(hù)在第2、3、6、13、29日的留存數(shù),計(jì)算出留存率
SELECT
    toDate('2020-08-01') AS ds,
    SUM(r[1]) AS activeAccountNum,
    SUM(r[2]) / SUM(r[1]) AS `次留`,
    SUM(r[3]) / SUM(r[1]) AS `3留`,
    SUM(r[4]) / SUM(r[1]) AS `7留`,
    SUM(r[5]) / SUM(r[1]) AS `14留`,
    SUM(r[6]) / SUM(r[1]) AS `30留`   
FROM
--找到2020-08-01活躍用戶(hù)在第2、3、6、13、29日的登錄情況,1/0 => 登錄/未登錄
(
    WITH toDate('2020-08-01') AS tt   
SELECT
    accountId,
    retention(
      toDate(ds) = tt, 
      toDate(subtractDays(ds, 1)) = tt, 
      toDate(subtractDays(ds, 2)) = tt, 
      toDate(subtractDays(ds, 6)) = tt,
      toDate(subtractDays(ds, 13)) = tt,
      toDate(subtractDays(ds, 29)) = tt
    ) AS r
  --找出2020-08-01活躍用戶(hù)在后續(xù)1~30日登錄數(shù)據(jù)
FROM login_event
WHERE (ds >= '2020-08-01') AND (ds <= addDays(toDate('2020-08-01'), 29))
GROUP BY accountId
)
GROUP BY ds


結(jié)果:
-----------------------------------------
┌─────────ds─┬─activeAccountNum─┬─次留─┬──3留─┬─7留─┬─14留─┬─30留─┐
│ 2020-08-01 │                4 │ 0.25 │ 0.25 │   0 │  0.5 │ 0.75 │
└────────────┴──────────────────┴──────┴──────┴─────┴──────┴──────┘

1 rows in set. Elapsed: 0.009 sec.

3. 總結(jié)

  • 方法一,使用傳統(tǒng)做法多表關(guān)聯(lián),了解ClickHouse的程序猿都清楚,多表關(guān)聯(lián)是ClickHouse天敵,運(yùn)行速度相對(duì)很慢。

  • 方法二,使用一個(gè)表關(guān)聯(lián),通過(guò)IF函數(shù)判斷日期差值,找到所需日期用戶(hù)數(shù)據(jù),相對(duì)方法一減少了多表關(guān)聯(lián),提高了運(yùn)行速度。

  • 方法三,使用ClickHouse自帶retention函數(shù),retention function是ClickHouse中高級(jí)聚合函數(shù),該函數(shù)可以接受多個(gè)條件,以第一個(gè)條件結(jié)果為基準(zhǔn),后面各條件滿(mǎn)足為1,不滿(mǎn)足則為0,最后返回一個(gè)1和0組成的數(shù)組。通過(guò)統(tǒng)計(jì)數(shù)組中對(duì)應(yīng)1的數(shù)量,既可計(jì)算出留存率。

三種計(jì)算方法比較而言,在海量的數(shù)據(jù)集下使用ClickHouse自帶retention留存函數(shù)運(yùn)行速度更快、更高效。提升了現(xiàn)有技術(shù)中用戶(hù)留存率的計(jì)算方式速度慢效率低的問(wèn)題,進(jìn)而達(dá)到了提高計(jì)算速度和計(jì)算效率的效果。

關(guān)于ClickHouse是如何提高留存計(jì)算速度就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

新聞標(biāo)題:ClickHouse是如何提高留存計(jì)算速度
標(biāo)題路徑:http://muchs.cn/article12/geppgc.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供微信小程序、面包屑導(dǎo)航、外貿(mào)建站、云服務(wù)器、用戶(hù)體驗(yàn)自適應(yīng)網(wǎng)站

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)

小程序開(kāi)發(fā)