Menu
ESC

Nhập từ khóa để tìm kiếm

↑↓ Di chuyển
Enter Mở
ESC Đóng

Đang tải...

Bài 12 — User Profiles + People Properties

Product Analytics Mixpanel and Amplitude Bài 12/60

Mở đầu — vì sao bài này quan trọng

Ở những bài trước, chúng ta đã dành rất nhiều thời gian để nói về event — tức là những "hành động" mà người dùng thực hiện: mở app, nhấn nút thanh toán, xem một sản phẩm, hoàn tất một đơn hàng. Event trả lời cho câu hỏi "chuyện gì đã xảy ra?". Nhưng có một câu hỏi khác cũng quan trọng không kém mà event một mình không trả lời nổi: "Ai đã làm việc đó?"

Đó chính là lý do bài này tồn tại. User Profile (hồ sơ người dùng) và People Properties (thuộc tính con người) là lớp dữ liệu mô tả bản thân người dùng — chứ không phải hành động của họ. Một event nói cho bạn biết "có người vừa mua gói Premium". Một user profile nói cho bạn biết "người đó tên Minh, 28 tuổi, ở TP.HCM, đăng ký từ tháng 3, đã chi tổng cộng 4,2 triệu đồng, và đang dùng gói Business".

Nếu bạn từng thắc mắc tại sao trong Mixpanel hay Amplitude bạn có thể lọc funnel theo "chỉ những người dùng gói Premium" hay "chỉ khách hàng ở Hà Nội", thì chính lớp People Properties này làm điều đó khả thi. Không có nó, mọi phân tích của bạn chỉ dừng ở mức hành vi trần trụi mà thiếu ngữ cảnh về con người. Với vai trò một Product Manager, hiểu rõ cơ chế này giúp bạn thiết kế dữ liệu đúng ngay từ đầu, tránh cảnh sau ba tháng mới phát hiện ra mình không thể phân khúc người dùng theo cách mình cần.

Khái niệm cốt lõi

User Profile là gì

Hãy hình dung mỗi người dùng trong hệ thống analytics của bạn có một "thẻ hồ sơ" riêng. Trên thẻ đó là tập hợp các thuộc tính mô tả người đó tại thời điểm hiện tại. Đây là điểm khác biệt cốt lõi so với event:

  • Event là dữ liệu dạng dòng thời gian (time-series): mỗi lần xảy ra là một bản ghi mới, bất biến, gắn với một mốc thời gian cụ thể. Bạn không sửa một event đã xảy ra.
  • User Profile là dữ liệu dạng trạng thái (state): nó chỉ giữ giá trị mới nhất. Khi người dùng đổi gói từ Free sang Premium, thuộc tính plan của họ bị ghi đè, chứ không tạo ra hai dòng.
Nói cách khác: event là cuốn nhật ký, còn user profile là tấm căn cước. Nhật ký ghi lại mọi thứ đã xảy ra; căn cước chỉ phản ánh con người bây giờ.

People Properties — các thuộc tính con người

People Properties là các trường dữ liệu gắn trên profile. Chúng thường được chia thành vài nhóm:

  • Thuộc tính định danh & nhân khẩu: tên, email, số điện thoại, thành phố, độ tuổi, giới tính.
  • Thuộc tính trạng thái sản phẩm: gói đăng ký (plan), vai trò (role), trạng thái tài khoản (active/churned), ngày đăng ký.
  • Thuộc tính tích lũy: tổng số tiền đã chi (total_spent), số đơn hàng, số lần đăng nhập, số bài học đã hoàn thành.
  • Thuộc tính hành vi rút gọn: ngày hoạt động gần nhất (last_active), sản phẩm hay xem nhất.
Điểm quan trọng: các thuộc tính này bền vững qua các event (persist). Bạn chỉ cần set plan = "Premium" một lần, và từ đó về sau mọi event của người dùng đều có thể được lọc/nhóm theo giá trị này — kể cả các event xảy ra trước khi bạn set (tùy công cụ), vì việc lọc diễn ra tại thời điểm truy vấn dựa trên trạng thái profile hiện tại.

Ba thao tác cập nhật: set, set_once, increment

Đây là phần "linh hồn" của bài học, vì chọn sai thao tác là nguyên nhân số một khiến dữ liệu profile bị sai lệch. Cả Mixpanel lẫn Amplitude đều hỗ trợ ba cơ chế cập nhật tương đương nhau, chỉ khác tên gọi:

set — ghi đè giá trị. Dùng khi thuộc tính có thể thay đổi và bạn luôn muốn giữ giá trị mới nhất. Ví dụ: plan, city, last_login_date. Mỗi lần gọi set, giá trị cũ bị thay thế.

// Mixpanel — người dùng nâng cấp gói
mixpanel.people.set({
  "plan": "Premium",
  "city": "Hồ Chí Minh",
  "last_upgrade": "2026-06-27"
});

set_once — chỉ ghi lần đầu tiên. Dùng cho các thuộc tính "bất biến", chỉ nên có một giá trị duy nhất suốt đời và không được ghi đè về sau. Ví dụ kinh điển: signup_date (ngày đăng ký), first_referral_source (nguồn giới thiệu ban đầu), acquisition_channel. Nếu bạn lỡ dùng set cho signup_date, mỗi lần code chạy lại nó sẽ ghi đè bằng ngày hôm nay — và bạn mất sạch dữ liệu ngày đăng ký thật.

// Mixpanel — chỉ set nếu thuộc tính chưa tồn tại
mixpanel.people.set_once({
  "signup_date": "2026-03-15",
  "first_channel": "Facebook Ads"
});

increment — cộng dồn vào giá trị số hiện tại. Dùng cho các bộ đếm tích lũy mà bạn không muốn tự tính toán ở client. Ví dụ: mỗi lần mua hàng thì increment tổng chi tiêu, mỗi lần hoàn thành bài học thì increment số bài học.

// Mixpanel — cộng dồn sau mỗi giao dịch
mixpanel.people.increment({
  "total_spent": 299000,
  "purchase_count": 1
});

Trong Amplitude, ba thao tác tương ứng là identify.set(), identify.setOnce(), và identify.add(). Amplitude còn có thêm append/prepend để thêm phần tử vào một thuộc tính dạng mảng (ví dụ danh sách sản phẩm đã mua), và unset để xóa hẳn một thuộc tính. Về mặt tư duy, chúng hoàn toàn song song với Mixpanel.

Identity: sợi dây nối profile với event

Để một event "dính" đúng vào profile của một người, công cụ cần một distinct ID (Mixpanel) hoặc user ID / device ID (Amplitude). Khi người dùng ẩn danh mới vào app, họ có một ID tạm theo thiết bị. Khi họ đăng nhập, bạn gọi identify() để nối ID ẩn danh đó với user ID thật. Đây là điểm nối tối quan trọng — nếu làm sai, một người dùng có thể bị đếm thành hai, hoặc dữ liệu trước và sau đăng nhập không gộp được. Bài này chỉ nhấn mạnh vai trò của identity ở mức khái niệm; phần thực hành sâu về setup ID đã nằm ở bài Mixpanel/Amplitude Deep Dive.

Tình huống thực tế

Ví dụ 1 — Tiki và bài toán phân khúc khách hàng theo giá trị

Giả sử đội Growth của Tiki muốn chạy một chiến dịch retention nhắm riêng vào nhóm khách hàng "giá trị cao": những người đã chi trên 5 triệu đồng trong 90 ngày. Vấn đề là: dữ liệu chi tiêu nằm rải rác trong hàng nghìn event Order Completed. Nếu mỗi lần cần phân khúc lại phải quét toàn bộ event và cộng dồn, truy vấn sẽ rất chậm và tốn kém.

Giải pháp là dùng People Properties. Mỗi lần một đơn hàng hoàn tất, ngoài việc gửi event, hệ thống gọi increment để cộng giá trị đơn vào thuộc tính total_spent_90dset thuộc tính last_order_date. Giờ đây, để tìm nhóm khách hàng giá trị cao, đội chỉ cần tạo một filter đơn giản: total_spent_90d > 5000000. Truy vấn chạy tức thì vì nó đọc trực tiếp từ profile.

Bài học rút ra: khi một chỉ số cần được dùng đi dùng lại để phân khúc, hãy "vật chất hóa" nó thành một People Property thay vì tính lại từ event mỗi lần. Nó giống như việc bạn ghi sẵn tổng số dư vào sổ tiết kiệm thay vì cộng lại từng giao dịch mỗi khi cần biết.

Ví dụ 2 — Startup SaaS gọi món "GoFnB" và cái bẫy set_once

Một startup SaaS phục vụ quản lý nhà hàng (tạm gọi GoFnB) muốn phân tích retention theo nguồn thu hút khách: khách đến từ Facebook Ads có ở lại lâu hơn khách đến từ Google không? Họ tạo thuộc tính acquisition_source và ghi nó bằng set mỗi khi người dùng mở app, lấy từ tham số UTM của phiên hiện tại.

Sau ba tháng, số liệu trở nên vô nghĩa: gần như tất cả người dùng đều mang nguồn "direct" hoặc "organic". Lý do? Khách được thu hút lần đầu từ Facebook, nhưng những lần sau họ mở app trực tiếp — và mỗi lần mở, set lại ghi đè acquisition_source bằng nguồn của phiên gần nhất. Nguồn ban đầu — thứ họ thực sự cần — đã bị xóa sạch.

Cách sửa: thuộc tính nguồn thu hút phải dùng set_once. Nó chỉ được ghi đúng một lần, ở lần chạm đầu tiên, và không bao giờ bị ghi đè. Nếu muốn cả nguồn gần nhất, họ tạo thêm một thuộc tính riêng last_source dùng set — hai thuộc tính, hai mục đích, không lẫn lộn.

Bài học rút ra: hãy tự hỏi với mỗi thuộc tính — "giá trị này nên bất biến hay thay đổi theo thời gian?". Câu trả lời quyết định giữa set_onceset, và chọn sai thì dữ liệu hỏng âm thầm cho tới khi quá muộn.

Ví dụ 3 — MoMo và người dùng bị đếm hai lần

Đội analytics của một ví điện tử (tình huống mô phỏng theo MoMo) phát hiện số "người dùng hoạt động" báo cáo cao hơn thực tế khoảng 18%. Điều tra ra, gốc rễ nằm ở identity. Khi một người dùng cài lại app trên điện thoại mới, họ nhận một device ID mới và được coi là người ẩn danh. Nhưng vì lời gọi identify() để nối device ID mới với user ID thật (số điện thoại đã mã hóa) bị đặt sau một vài event onboarding, các event đầu tiên đã tạo ra một profile "ma" tách biệt.

Sau khi chuyển lời gọi identify() lên ngay khi xác thực đăng nhập thành công — trước mọi event nghiệp vụ — hai profile được gộp đúng, và số liệu active user trở về đúng thực tế.

Bài học rút ra: People Properties chỉ đáng tin khi identity đúng. Một profile "sạch" bắt đầu từ việc nối ID đúng thời điểm. Hãy coi identify() là việc đầu tiên làm ngay sau khi người dùng đăng nhập, không phải việc làm sau cùng.

Hướng dẫn từng bước

Đây là quy trình thực tế để thiết kế và triển khai People Properties cho một sản phẩm:

  • Liệt kê câu hỏi phân khúc bạn muốn trả lời. Bắt đầu từ nhu cầu, không phải từ dữ liệu. Ví dụ: "Retention của khách trả phí so với miễn phí?", "Người dùng ở tỉnh khác thành phố lớn có hành vi khác không?". Mỗi câu hỏi gợi ý một thuộc tính cần có.
  • Lập bảng thuộc tính, gán loại thao tác. Với mỗi thuộc tính, ghi rõ ba cột: tên (snake_case nhất quán), ý nghĩa, và thao tác (set / set_once / increment). Đây là "tracking plan" thu nhỏ cho phần profile.
  • Xác định điểm kích hoạt (trigger). Thuộc tính được cập nhật khi nào? signup_date set khi đăng ký; plan set khi thanh toán/hủy gói; total_spent increment khi đơn hàng hoàn tất; last_active set mỗi phiên.
  • Đặt identify() đúng chỗ. Gọi ngay sau đăng nhập/đăng ký thành công, trước các event nghiệp vụ, để mọi cập nhật profile gắn vào đúng người.
  • Triển khai lời gọi tương ứng. Với Mixpanel dùng mixpanel.people.set/set_once/increment; với Amplitude dùng đối tượng Identify với set/setOnce/add.
  • Kiểm chứng trên giao diện. Vào tab People/Users, tìm một profile thử, xác nhận các thuộc tính xuất hiện đúng giá trị và đúng kiểu dữ liệu (số ra số, ngày ra ngày, không phải chuỗi).
  • Tạo một cohort thử để nghiệm thu. Ví dụ tạo cohort plan = Premium AND total_spent > 1000000 và xem số lượng có hợp lý không. Nếu ra 0 hoặc ra toàn bộ user, gần như chắc chắn có lỗi thao tác hoặc kiểu dữ liệu.

Lỗi thường gặp & mẹo

  • Dùng set cho thuộc tính lẽ ra phải set_once. Đây là lỗi phổ biến nhất, phá hỏng dữ liệu nguồn thu hút và ngày đăng ký. Nguyên tắc: bất cứ thứ gì mô tả "lần đầu tiên" đều nên là set_once.
  • Tự cộng dồn ở client rồi set, thay vì increment. Nếu bạn đọc total_spent cũ, cộng ở app rồi set lại, bạn sẽ gặp race condition khi có hai giao dịch gần nhau — một giá trị bị đè mất. Hãy để công cụ tự cộng bằng increment.
  • Sai kiểu dữ liệu. Gửi age là chuỗi "28" thay vì số 28 khiến bạn không lọc được age > 25. Gửi ngày dưới dạng chuỗi tùy tiện khiến không so sánh được theo thời gian. Luôn dùng đúng kiểu số, boolean, và định dạng ngày ISO 8601.
  • Nhồi thông tin nhạy cảm vào profile. Đừng đẩy số CMND, mật khẩu, số thẻ đầy đủ lên profile analytics. Vấn đề tuân thủ GDPR/PDPL sẽ được bàn kỹ ở bài riêng, nhưng nguyên tắc tối thiểu: chỉ đưa vào profile những gì bạn thực sự cần để phân tích.
  • Đặt tên thuộc tính lộn xộn. Plan, plan, user_plan, subscription cùng tồn tại sẽ khiến đội không biết dùng cái nào. Thống nhất quy ước đặt tên ngay từ đầu.
  • Mẹo — profile update là hàng đợi bất đồng bộ. Sau khi gọi set, giá trị có thể mất vài giây mới hiện trên dashboard. Đừng kết luận "code sai" chỉ vì chưa thấy ngay; hãy chờ và refresh.
  • Mẹo — dùng thuộc tính "phái sinh" tính sẵn. Với các chỉ số phân khúc dùng thường xuyên (ví dụ is_high_value), tính sẵn thành boolean trên profile để filter cực nhanh, thay vì viết điều kiện phức tạp mỗi lần.

Bài tập thực hành

Hãy tưởng tượng bạn là PM của một app học tiếng Anh tên "EngGo". Thực hiện các bước sau:

  • Liệt kê ít nhất 6 People Properties bạn cần cho app này. Với mỗi thuộc tính, ghi rõ nên dùng set, set_once, hay increment và giải thích ngắn tại sao. (Gợi ý: ngày đăng ký, gói học, số bài đã hoàn thành, cấp độ hiện tại, ngày học gần nhất, nguồn cài đặt app.)
  • Viết đoạn mã giả (pseudo-code) cho ba tình huống: người dùng vừa đăng ký; người dùng hoàn thành một bài học; người dùng nâng cấp lên gói Premium. Mỗi tình huống gọi đúng thao tác profile tương ứng.
  • Thiết kế một cohort dùng ít nhất hai People Property để trả lời câu hỏi: "Ai là học viên chăm chỉ nhưng đang có nguy cơ rời bỏ?". (Gợi ý: kết hợp số bài đã hoàn thành cao với ngày học gần nhất đã cũ.)
  • Chỉ ra một thuộc tính trong danh sách của bạn mà nếu chọn sai thao tác sẽ làm hỏng dữ liệu, và mô tả hậu quả cụ thể.
Nếu có tài khoản Mixpanel hoặc Amplitude miễn phí, hãy triển khai thật ba lời gọi ở bước 2 và kiểm chứng trên tab People/Users.

Tóm tắt

User Profile là lớp dữ liệu mô tả con người thay vì hành động, giữ trạng thái mới nhất của mỗi người dùng và bền vững qua các event. Sức mạnh của nó nằm ở khả năng phân khúc: nhờ People Properties, bạn có thể lọc và nhóm mọi phân tích theo đặc điểm người dùng.

Ba thao tác cập nhật là trái tim của kỹ thuật này: set ghi đè cho thuộc tính thay đổi, set_once chỉ ghi một lần cho thuộc tính bất biến như ngày đăng ký hay nguồn thu hút, và increment cộng dồn cho bộ đếm tích lũy như tổng chi tiêu. Chọn đúng thao tác là ranh giới giữa dữ liệu đáng tin và dữ liệu hỏng âm thầm.

Cuối cùng, mọi thứ chỉ đúng khi identity đúng: hãy gọi identify() ngay sau khi đăng nhập, trước mọi event nghiệp vụ, để profile gắn vào đúng người. Nắm vững ba nguyên tắc này — đúng thao tác, đúng kiểu dữ liệu, đúng identity — bạn đã có nền tảng vững để xây dựng mọi phân khúc và phân tích sâu hơn ở các bài tiếp theo.