Chấm điểm gian lận thời gian thực ở quy mô lớn
Cách tracio.ai xử lý 50K sự kiện/giây với chấm điểm dưới 50ms nhờ stream processing, vector tín hiệu tính sẵn và edge caching.
Chấm điểm gian lận ở quy mô lớn đòi hỏi một kiến trúc khác về bản chất so với xử lý theo lô (batch). Khi một khoản thanh toán đang được cấp phép hoặc một tài khoản đang được tạo, bạn chỉ có vài mili-giây — không phải vài phút — để trả về điểm rủi ro. Tại tracio.ai, chúng tôi xử lý hơn 50.000 sự kiện mỗi giây với độ trễ chấm điểm trung vị 22ms. Bài viết này giải thích kiến trúc giúp điều đó trở nên khả thi.
Pipeline chấm điểm
Mỗi sự kiện đến đều đi vào một pipeline ba giai đoạn: làm giàu tín hiệu, tính vector và chấm điểm rủi ro. Làm giàu tín hiệu gắn thêm dữ liệu device intelligence — fingerprint của khách truy cập, kết quả phát hiện bot, IP intelligence và hành vi trong quá khứ — vào sự kiện thô. Tính vector biến đổi các tín hiệu đã làm giàu này thành một vector đặc trưng có độ dài cố định, được tối ưu cho mô hình chấm điểm của chúng tôi. Chấm điểm rủi ro đưa vector qua mô hình đã huấn luyện và trả về một điểm số nằm giữa 0.0 và 1.0.
Quyết định thiết kế then chốt là tách phần làm giàu và tính vector ra khỏi phần chấm điểm. Dữ liệu làm giàu được tính sẵn và đưa vào cache. Khi một khách truy cập tải một trang, chúng tôi tính hồ sơ thiết bị của họ và lưu trong Redis với TTL 60 phút. Khi một yêu cầu chấm điểm đến — thường được kích hoạt bởi một khoản thanh toán hoặc một lần đăng nhập — chúng tôi lấy lại hồ sơ đã tính sẵn thay vì tính lại. Điều này giảm độ trễ chấm điểm từ hơn 200ms xuống dưới 30ms.
Stream processing với Go
Lớp thu nhận (ingestion) của chúng tôi được viết bằng Go và sử dụng kiến trúc fan-out. Các sự kiện đến qua HTTP POST và được đặt ngay vào một channel nội bộ. Một nhóm worker goroutine đọc từ channel này, thực hiện việc làm giàu và ghi các sự kiện đã làm giàu vào ClickHouse cho phân tích cũng như vào một hàng đợi chấm điểm để xử lý thời gian thực. Nhóm fan-out mở rộng động dựa trên độ sâu của hàng đợi.
Chúng tôi chọn Go cho lớp thu nhận nhờ các nguyên thủy đồng thời (concurrency) tuyệt vời và khả năng cấp phát bộ nhớ có thể dự đoán được. Mỗi worker goroutine tiêu tốn khoảng 4KB không gian stack, cho phép chúng tôi chạy hàng nghìn worker đồng thời trên một node duy nhất. Thời gian dừng dưới một mili-giây của bộ thu gom rác (garbage collector) là yếu tố then chốt để duy trì độ trễ ổn định ở thông lượng cao.
Edge caching và vector tín hiệu
Đối với những khách hàng có lưu lượng cao nhất, chúng tôi triển khai các mô hình chấm điểm tại edge bằng một cache vector tín hiệu được tính sẵn. Khi một thiết bị được thấy lần đầu, chúng tôi tính toàn bộ vector tín hiệu của nó và lưu trong edge cache của chúng tôi (triển khai trên Cloudflare Workers KV). Các yêu cầu chấm điểm tiếp theo cho cùng thiết bị đó sẽ lấy vector đã cache và chạy chấm điểm cục bộ tại edge, đạt độ trễ dưới 10ms.
Mô hình chấm điểm tại edge là một phiên bản được chưng cất (distilled) của mô hình đầy đủ — nhỏ hơn và nhanh hơn, nhưng được tối ưu cho cùng mục tiêu về độ chính xác. Chúng tôi huấn luyện lại mô hình edge hằng tuần và triển khai cập nhật qua rolling deployment để tránh các đợt vô hiệu hóa cache ồ ạt. Mô hình đầy đủ chạy phía máy chủ cho những trường hợp mà độ tin cậy của mô hình edge thấp hơn một ngưỡng có thể cấu hình.
ClickHouse cho phân tích
Tất cả các sự kiện đã làm giàu đều được lưu trong ClickHouse, cơ sở dữ liệu phân tích dạng cột của chúng tôi. Khả năng nén và hiệu năng truy vấn của ClickHouse cho phép chúng tôi lưu hàng tỷ sự kiện trong khi vẫn hỗ trợ các truy vấn phân tích thời gian thực. Khách hàng của chúng tôi dùng các phân tích này để hiểu các mẫu hình gian lận, tinh chỉnh ngưỡng chấm điểm và điều tra từng sự kiện riêng lẻ.
Chúng tôi dùng materialized view trong ClickHouse để duy trì các chỉ số đã tổng hợp sẵn: tỷ lệ gian lận theo quốc gia, phân bố điểm số theo loại thiết bị và tỷ lệ dương tính giả theo ngưỡng. Các materialized view này cập nhật theo thời gian thực khi sự kiện đến, cung cấp các chỉ số sẵn sàng cho dashboard mà không cần các truy vấn tổng hợp tốn kém.
Bài học rút ra
Việc xây dựng một hệ thống chấm điểm thời gian thực đã dạy cho chúng tôi vài bài học. Thứ nhất, tính sẵn là tối ưu hóa quan trọng nhất — bất kỳ công việc nào bạn có thể làm trước khi yêu cầu chấm điểm đến đều là công việc không tính vào ngân sách độ trễ của bạn. Thứ hai, mô hình đồng thời của Go rất phù hợp cho việc xử lý sự kiện thông lượng cao, nhưng bạn phải kỷ luật trong việc cấp phát bộ nhớ để tránh áp lực lên GC. Thứ ba, triển khai tại edge tạo ra thay đổi lớn về độ trễ nhưng đòi hỏi quản lý mô hình cẩn thận để tránh các dự đoán bị lỗi thời.