Phân tích sản phẩm 4 — Cohort & Retention
Retention: chỉ số sống còn
Một app ngân hàng số có thể tiêu hàng chục tỷ đồng marketing để kéo 100.000 khách mở tài khoản trong quý. Nhưng nếu ba tháng sau chỉ còn 20.000 người thực sự giao dịch, thì 80% ngân sách vừa chảy qua một cái thùng thủng. Retention (tỷ lệ giữ chân) là câu hỏi đo cái thùng có thủng không — và nó quan trọng hơn cả tốc độ đổ nước vào.
Có một sự thật kinh tế đứng sau: giữ một khách hiện hữu rẻ hơn nhiều lần so với thu hút một khách mới. Trong ngân hàng số, chi phí thu hút (CAC — customer acquisition cost) cho một tài khoản active thường gấp nhiều lần chi phí giữ chân, chưa kể khách ở lâu mới sinh lợi (cross-sell thẻ, vay, tiết kiệm — xem pa-06 — CLV & Churn). Growth bền vững không đến từ đổ tiền vào miệng phễu, mà đến từ bịt đáy phễu. Một sản phẩm có retention tốt tăng trưởng kép; một sản phẩm retention kém buộc phải chạy marketing mãi chỉ để đứng yên.
Bài này nối tiếp pa-03 — Phễu: phễu đo người dùng đi tới lần đầu tiên (activation), còn retention đo họ quay lại sau đó. Công cụ chính để đo retention đúng cách là cohort analysis.
Cohort analysis: vì sao cần
Cohort (nhóm đồng hình) là tập người dùng chia sẻ một đặc điểm chung — phổ biến nhất là cùng kỳ acquisition, tức cùng tháng (hoặc tuần) mở tài khoản. "Cohort tháng 3/2026" = tất cả khách mở tài khoản trong tháng 3/2026.
Vì sao không nhìn thẳng tổng số active user (metric tổng) mà phải chia cohort? Vì metric tổng trộn lẫn tân binh với khách cũ, che giấu sự thật. Ví dụ: tổng active user tháng này tăng 10% — nghe tốt. Nhưng nếu bạn đổ rất nhiều khách mới vào (tân binh luôn active ngay tháng đầu), con số tăng đó có thể đang che một thực tế là khách cũ đang rời bỏ hàng loạt. Cohort analysis tách hai ảnh hưởng này: nhìn riêng từng nhóm khách theo thời gian, ta thấy xu hướng thật của hành vi giữ chân, không bị "hiệu ứng tân binh" làm nhiễu.
Có hai kiểu cohort:
| Kiểu | Tiêu chí nhóm | Ví dụ ngân hàng |
|---|---|---|
| Acquisition cohort | Cùng thời điểm bắt đầu (mở tài khoản) | Khách mở tài khoản tháng 3/2026 |
| Behavioral cohort | Cùng một hành vi/đặc điểm | Khách có bật thông báo đẩy; khách dùng ≥3 tính năng; khách đến từ giới thiệu |
Bài này tập trung acquisition cohort theo tháng vì đó là nền để đo retention. Behavioral cohort thường dùng khi truy nguyên "hành vi nào sớm dự báo giữ chân lâu".
Retention curve: hình dạng nói lên tất cả
Với một cohort, ta vẽ đường cong retention (retention curve): trục hoành là số kỳ trôi qua kể từ khi vào (kỳ 0, 1, 2, ...), trục tung là tỷ lệ khách của cohort còn "hoạt động" ở kỳ đó. Đường này luôn bắt đầu 100% ở kỳ 0 và đi xuống. Điều quan trọng không phải nó xuống bao nhiêu, mà nó xuống rồi có phẳng lại (flatten) hay không:
- Đường phẳng lại (flattening curve — trên): sau vài kỳ đầu rơi mạnh, đường cong ổn định ở một mức dương (ví dụ ~36%). Đây là dấu hiệu vàng của product-market fit (PMF): có một lõi khách hàng tìm thấy giá trị thật và ở lại lâu dài. Mức phẳng đó chính là "sàn retention" — nền tảng để tăng trưởng kép.
- Đường dốc về 0 (không phẳng — dưới): đường cong cứ trượt dần tới 0. Nghĩa là không có lõi khách trung thành — mọi người rồi cũng rời. Sản phẩm kiểu này giống một cái xô thủng đáy: đổ marketing vào bao nhiêu cũng chảy hết. Đây là tín hiệu chưa đạt PMF, cần sửa sản phẩm trước khi tăng chi acquisition.
Quy tắc thực chiến: trước khi tăng tốc thu hút, hãy chắc retention curve đã phẳng. Scale một sản phẩm chưa giữ được chân khách chỉ làm cháy ngân sách nhanh hơn.
Định nghĩa "retention" cho đúng
"Retention" nghe đơn giản nhưng có nhiều định nghĩa, và chọn sai định nghĩa cho ra con số vô nghĩa. Ba trục cần quyết định:
1. "Hoạt động" nghĩa là gì — định nghĩa core event
Retention luôn được đo trên một sự kiện cốt lõi (core event) thể hiện khách nhận được giá trị. Với ngân hàng số, "hoạt động" hầu như luôn nên là "có phát sinh giao dịch" — không phải "có mở app". Mở app mà không làm gì không tạo giá trị và không giữ chân được. Định nghĩa core event sai (quá lỏng như "đăng nhập") thổi phồng retention một cách nguy hiểm.
2. N-day (bounded) vs unbounded/rolling
| Kiểu | Định nghĩa | Phù hợp sản phẩm |
|---|---|---|
| N-day retention (bounded) | Khách active đúng vào kỳ thứ N (ví dụ đúng tuần thứ 4) | Sản phẩm dùng đều đặn, tần suất cao (app nhắn tin, mạng xã hội) |
| Unbounded / rolling retention | Khách active bất kỳ lúc nào từ kỳ N trở đi (còn "sống" tại thời điểm N) | Sản phẩm tần suất thấp/không đều (ngân hàng, du lịch, bảo hiểm) |
Đây là lựa chọn tùy tần suất tự nhiên của sản phẩm. Một app tin nhắn kỳ vọng dùng hàng ngày → N-day chặt (dùng đúng ngày N) hợp lý. Nhưng ngân hàng số thì khách có thể không giao dịch suốt 3 tuần rồi mới trả tiền điện — dùng N-day chặt sẽ báo "đã churn" oan. Với ngân hàng, rolling/bounded-theo-tháng (có ≥1 giao dịch trong cả tháng thứ N) là hợp lý hơn nhiều so với đòi đúng ngày.
3. Đơn vị kỳ (granularity)
Ngày / tuần / tháng. Chọn theo tần suất kỳ vọng: sản phẩm dùng hàng ngày → theo tuần; ngân hàng số (giao dịch thưa hơn) → theo tháng là chuẩn phổ biến. Trong bài này ta dùng cohort tháng, retention tháng.
Churn: mặt kia của cùng đồng xu
Churn (rời bỏ) là phần bù của retention. Trong một kỳ:
retention_rate(kỳ) + churn_rate(kỳ) = 1
Nếu retention tháng thứ nhất là 55% thì churn tháng đó là 45%. Hai chỉ số nói cùng một sự thật từ hai góc: retention nhìn "ai còn ở lại", churn nhìn "ai đã rời đi". Chúng bổ sung nhau — dùng churn khi muốn nhấn mạnh tổn thất và dự báo suy giảm doanh thu, dùng retention khi muốn nhấn mạnh sức khỏe lõi khách hàng.
Một điểm tinh tế: churn trong sản phẩm tần suất thấp khó định nghĩa dứt khoát vì không có "hủy đăng ký" rõ ràng — khách chỉ đơn giản ngừng giao dịch. Vì thế người ta thường định nghĩa churn theo ngưỡng im lặng: "khách không có giao dịch nào trong ≥90 ngày coi như churn". Ngưỡng này chọn theo phân bố khoảng cách giữa các giao dịch của khách active. Chi tiết định nghĩa và dự báo churn ở pa-06 — CLV & Churn.
Ma trận cohort-retention: đọc heatmap tam giác
Cách trình bày kinh điển là ma trận cohort-retention — một bảng/heatmap hình tam giác:
- Mỗi hàng = một cohort (một tháng acquisition), xếp từ cũ tới mới.
- Mỗi cột = số kỳ trôi qua kể từ khi cohort vào (M0, M1, M2, ...).
- Ô (hàng, cột) = % khách của cohort đó còn active sau đúng ngần ấy kỳ.
Ví dụ minh hoạ (đọc theo % còn giao dịch):
| Cohort | Cỡ | M0 | M1 | M2 | M3 | M4 |
|---|---|---|---|---|---|---|
| 2026-01 | 4.200 | 100% | 52% | 40% | 36% | 35% |
| 2026-02 | 5.100 | 100% | 55% | 43% | 38% | — |
| 2026-03 | 6.800 | 100% | 58% | 47% | — | — |
| 2026-04 | 7.500 | 100% | 61% | — | — | — |
| 2026-05 | 8.100 | 100% | — | — | — | — |
Vì sao tam giác? Cohort tháng 5 mới vào một tháng — nó chưa thể có dữ liệu M2, M3 (tương lai chưa đến), nên góc dưới-phải trống. Cohort càng cũ càng có nhiều cột. Hai cách đọc:
- Đọc theo hàng (một cohort qua thời gian): đây là retention curve của cohort đó — rơi rồi có phẳng không. Hàng 2026-01 phẳng quanh 35% từ M3 → có lõi trung thành.
- Đọc theo cột (so cùng-tuổi giữa các cohort): so cột M1 dọc xuống: 52% → 55% → 58% → 61%. Cohort mới giữ chân tốt hơn cohort cũ ở cùng độ tuổi → sản phẩm đang cải thiện (có thể do một thay đổi onboarding). Đây chính là cách đo tác động của thay đổi sản phẩm: nếu bạn tung tính năng mới tháng 3, hãy so cột M1 của cohort trước và sau tháng 3.
Đọc theo cột là kỹ thuật mạnh nhất: nó so người cùng độ chín (apples-to-apples), loại được yếu tố "cohort mới chưa đủ thời gian". So nhầm cohort chưa chín với cohort đã chín là một bẫy kinh điển.
Resurrection: khách hồi sinh
Retention không phải câu chuyện một chiều đi xuống. Một khách đã im lặng (churned) vẫn có thể quay lại giao dịch — gọi là resurrection (hồi sinh) / reactivation (tái kích hoạt). Phân rã đầy đủ tập người dùng mỗi kỳ gồm: new (mới), retained (giữ được, active kỳ trước và kỳ này), resurrected (kỳ trước im, kỳ này quay lại), và churned (kỳ trước active, kỳ này im). Growth accounting chính là theo dõi bốn dòng này.
Resurrection quan trọng vì kéo một khách cũ quay lại thường rẻ hơn thu hút khách hoàn toàn mới (họ đã có tài khoản, đã định danh). Chiến dịch win-back (nhắc nợ nhẹ nhàng, ưu đãi hoàn tiền giao dịch đầu tiên sau khi ngủ đông) nhắm chính vào nhóm này.
SQL: dựng cohort & đo retention từ dữ liệu thật
Sandbox có customers, accounts, transactions. Ta định nghĩa:
- Cohort của một khách = tháng khách được tạo (
customers.created_at, kiểu DATE), cắt bằngdate_trunc('month', ...). - "Active" ở tháng M = khách có ≥1 giao dịch trong tháng đó (
transactions.created_at, kiểu TIMESTAMP). Để nối giao dịch về khách phải đitransactions → accounts → customers(không cócustomer_idtrêntransactions). - Retention M = số khách của cohort còn active tháng M / cỡ cohort (đếm distinct customer).
Query 1 — Cỡ mỗi cohort theo tháng mở tài khoản
Nhóm khách theo tháng tạo. Đây là mẫu số của mọi phép tính retention sau này:
-- ▶ Chạy được
SELECT
date_trunc('month', c.created_at)::date AS cohort_month,
COUNT(*) AS cohort_size
FROM customers c
GROUP BY date_trunc('month', c.created_at)
ORDER BY cohort_month;
date_trunc('month', created_at) gom mọi ngày trong cùng tháng về mốc đầu tháng; ép ::date cho gọn (created_at đã là DATE). Mỗi dòng là một cohort và số khách của nó.
Query 2 — Retention: khách mỗi cohort còn giao dịch ở các tháng sau
Đây là truy vấn cốt lõi. Với mỗi khách, ta biết cohort của họ (tháng mở) và tháng giao dịch; hiệu số tháng giữa hai mốc là "tuổi" (month_number = 0, 1, 2, ...). Đếm distinct khách theo (cohort, tuổi) rồi chia cho cỡ cohort ra tỷ lệ retention:
-- ▶ Chạy được
WITH cohort AS (
SELECT c.id AS customer_id,
date_trunc('month', c.created_at) AS cohort_month
FROM customers c
),
activity AS (
SELECT DISTINCT
co.customer_id,
co.cohort_month,
date_trunc('month', t.created_at) AS active_month
FROM cohort co
JOIN accounts a ON a.customer_id = co.customer_id
JOIN transactions t ON t.account_id = a.id
),
sized AS (
SELECT cohort_month, COUNT(*) AS cohort_size
FROM cohort GROUP BY cohort_month
)
SELECT
s.cohort_month::date AS cohort_month,
s.cohort_size,
(EXTRACT(YEAR FROM ac.active_month) - EXTRACT(YEAR FROM ac.cohort_month)) * 12
+ (EXTRACT(MONTH FROM ac.active_month) - EXTRACT(MONTH FROM ac.cohort_month)) AS month_number,
COUNT(DISTINCT ac.customer_id) AS active_customers,
ROUND(100.0 * COUNT(DISTINCT ac.customer_id)
/ NULLIF(s.cohort_size, 0)::numeric, 1) AS retention_pct
FROM activity ac
JOIN sized s ON s.cohort_month = ac.cohort_month
GROUP BY s.cohort_month, s.cohort_size, ac.cohort_month, ac.active_month
ORDER BY s.cohort_month, month_number;
month_number = chênh lệch tháng giữa tháng giao dịch và tháng mở (dùng EXTRACT năm×12 + tháng để tránh lệ thuộc kiểu interval). month_number = 0 là tháng đầu, giá trị dương là các tháng sau. NULLIF(..., 0)::numeric chống chia 0 và ép numeric trước ROUND. Mỗi dòng: một cohort, một độ tuổi, và % khách còn giao dịch — chính là các ô của ma trận ở dạng dài (long format).
Query 3 — Ma trận cohort × tháng (pivot thành heatmap tam giác)
Xoay kết quả trên thành bảng chữ nhật: mỗi hàng một cohort, mỗi cột là M0..M4 (retention %). Đây đúng là ma trận tam giác để vẽ heatmap — các tháng chưa đến sẽ là NULL:
-- ▶ Chạy được
WITH cohort AS (
SELECT c.id AS customer_id,
date_trunc('month', c.created_at) AS cohort_month
FROM customers c
),
activity AS (
SELECT DISTINCT
co.customer_id,
co.cohort_month,
(EXTRACT(YEAR FROM date_trunc('month', t.created_at)) - EXTRACT(YEAR FROM co.cohort_month)) * 12
+ (EXTRACT(MONTH FROM date_trunc('month', t.created_at)) - EXTRACT(MONTH FROM co.cohort_month)) AS month_number
FROM cohort co
JOIN accounts a ON a.customer_id = co.customer_id
JOIN transactions t ON t.account_id = a.id
),
sized AS (
SELECT cohort_month, COUNT(*) AS cohort_size
FROM cohort GROUP BY cohort_month
)
SELECT
s.cohort_month::date AS cohort_month,
s.cohort_size,
ROUND(100.0 * COUNT(DISTINCT ac.customer_id) FILTER (WHERE ac.month_number = 0)
/ NULLIF(s.cohort_size, 0)::numeric, 0) AS m0_pct,
ROUND(100.0 * COUNT(DISTINCT ac.customer_id) FILTER (WHERE ac.month_number = 1)
/ NULLIF(s.cohort_size, 0)::numeric, 0) AS m1_pct,
ROUND(100.0 * COUNT(DISTINCT ac.customer_id) FILTER (WHERE ac.month_number = 2)
/ NULLIF(s.cohort_size, 0)::numeric, 0) AS m2_pct,
ROUND(100.0 * COUNT(DISTINCT ac.customer_id) FILTER (WHERE ac.month_number = 3)
/ NULLIF(s.cohort_size, 0)::numeric, 0) AS m3_pct,
ROUND(100.0 * COUNT(DISTINCT ac.customer_id) FILTER (WHERE ac.month_number = 4)
/ NULLIF(s.cohort_size, 0)::numeric, 0) AS m4_pct
FROM sized s
LEFT JOIN activity ac ON ac.cohort_month = s.cohort_month
GROUP BY s.cohort_month, s.cohort_size
ORDER BY s.cohort_month;
COUNT(DISTINCT ...) FILTER (WHERE month_number = k) đếm khách còn active đúng ở tuổi k, chia cho cỡ cohort. LEFT JOIN giữ cả cohort chưa có tháng nào tiếp theo. Đọc bảng: nhìn theo hàng ra retention curve từng cohort; nhìn theo cột so cùng-tuổi giữa các cohort để thấy sản phẩm cải thiện hay xấu đi. Ô rỗng (NULL) ở góc dưới-phải chính là phần tam giác "tương lai chưa tới".
Bẫy thường gặp
| Bẫy | Hậu quả | Cách tránh |
|---|---|---|
| Đo retention trên "mở app" thay vì giao dịch | Retention ảo cao, che churn thật | Chọn core event tạo giá trị (giao dịch) |
| Dùng N-day chặt cho sản phẩm tần suất thấp | Báo churn oan hàng loạt | Dùng rolling/bounded-theo-tháng cho ngân hàng |
| Đếm event thay vì distinct customer | Retention >100%, sai lệch | COUNT(DISTINCT customer_id) |
| So cohort chưa chín với cohort đã chín | Kết luận sai xu hướng | Chỉ so cùng cột (cùng độ tuổi) |
| Nhìn metric tổng, bỏ cohort | Hiệu ứng tân binh che khách cũ rời | Luôn bổ cohort khi đánh giá giữ chân |
| Quên resurrection | Bỏ sót khách win-back rẻ | Theo dõi cả reactivation |
Use case thực tế
Bối cảnh: Quý 2/2026 NCB Digital chạy chiến dịch kéo khách mở tài khoản, đồng thời tháng 3 tung lại luồng onboarding có OCR + gợi ý giao dịch đầu tiên. Đội product analytics dựng ma trận cohort-retention theo tháng, "active" = có ≥1 giao dịch trong tháng (số minh hoạ):
| Cohort | Cỡ | M0 | M1 | M2 | M3 |
|---|---|---|---|---|---|
| 2026-01 | 4.200 | 100% | 52% | 40% | 36% |
| 2026-02 | 5.100 | 100% | 55% | 43% | 38% |
| 2026-03 | 6.800 | 100% | 61% | 47% | — |
| 2026-04 | 7.500 | 100% | 63% | — | — |
Đọc ma trận:
- Theo hàng: cohort 2026-01 rơi từ 100% → 52% → 40% rồi phẳng quanh 36–38% ở M3. Đường cong flatten → có lõi khách trung thành, dấu hiệu PMF ở mảng tài khoản thanh toán.
- Theo cột M1: 52% → 55% → 61% → 63%. Cohort sau tháng 3 giữ chân tháng đầu tốt hơn hẳn (+8 điểm so cohort 01). Vì đây là so cùng độ tuổi, cải thiện này quy được cho thay đổi onboarding tháng 3, không phải nhiễu tân binh.
Hành động: (1) Churn M0→M1 vẫn ~40% — đội soi nhóm rời sớm, thấy phần lớn chỉ giao dịch 1 lần rồi im; đặt trigger "gợi ý liên kết trả hóa đơn tự động" trong 30 ngày đầu để tạo thói quen định kỳ. (2) Với nhóm đã churn ≥90 ngày (từ query kiểu Query 2, lọc khách không xuất hiện ở các tháng gần đây), chạy win-back hoàn tiền giao dịch đầu → đo resurrection ở cohort tiếp theo. (3) Vì retention curve đã phẳng, đội tự tin tăng ngân sách acquisition — scale một cái thùng không thủng.
Phát hiện churn sớm: thay vì đợi hết 90 ngày, đội cảnh báo sớm khi một khách của cohort không giao dịch trong 30 ngày sau khi từng active đều — danh sách này đẩy sang CRM để chăm sóc trước khi khách rời hẳn. Cohort giúp biết đâu là "bình thường" (nhóm cùng tháng) để phát hiện lệch.
Ghi nhớ
- Retention là chỉ số sống còn: giữ khách rẻ hơn thu hút mới; growth bền đến từ bịt đáy phễu, không phải đổ đầy miệng phễu.
- Cohort analysis nhóm khách theo kỳ acquisition (tháng mở tài khoản) rồi theo dõi theo thời gian → tách hiệu ứng tân binh khỏi xu hướng thật. Phân biệt acquisition cohort vs behavioral cohort.
- Retention curve: đường phẳng lại ở mức dương = có product-market fit (lõi khách trung thành); đường dốc về 0 = xô thủng đáy, chưa nên scale.
- Định nghĩa retention cần chốt 3 trục: core event ("active" = giao dịch, không phải mở app), N-day (bounded) vs unbounded/rolling, và đơn vị kỳ — chọn theo tần suất sản phẩm (ngân hàng: theo tháng, rolling).
- retention + churn = 1 trong một kỳ; churn sản phẩm tần suất thấp định nghĩa theo ngưỡng im lặng (vd ≥90 ngày).
- Ma trận cohort-retention (heatmap tam giác): đọc theo hàng = retention curve một cohort; đọc theo cột = so cùng độ tuổi giữa các cohort để đo tác động thay đổi sản phẩm.
- Đừng quên resurrection/reactivation: kéo khách cũ quay lại thường rẻ hơn thu hút khách mới → chiến dịch win-back.
- Ngân hàng số: cohort theo tháng mở tài khoản, retention = còn giao dịch; ma trận giúp phát hiện churn sớm và biết mức "bình thường" của từng nhóm.
Bài viết liên quan
Phân biệt ước lượng điểm và ước lượng khoảng, cách xây khoảng tin cậy (CI) bằng margin of error z*·SE / t*·SE, đánh đổi mức 90/95/99%, và cách DIỄN GIẢI ĐÚNG khoảng tin cậy (95% CI không phải xác suất tham số nằm trong khoảng). Có CI cho trung bình và cho tỷ lệ, ảnh hưởng của cỡ mẫu, cùng ví dụ ngân hàng về tỷ lệ nợ xấu và số dư trung bình.
Kimball dimensional modeling: bảng fact/dimension, star vs snowflake, grain, và Slowly Changing Dimension.
Khối OLAP, các thao tác drill-down/roll-up/slice & dice/pivot, OLAP vs OLTP và ROLAP/MOLAP/HOLAP.
Từ nguồn dữ liệu qua ETL/ELT vào Data Warehouse, Data Mart đến dashboard; staging, ODS, batch vs streaming.