01Khi gặp đề bài system design (vd. thiết kế hệ thống rút gọn link), bạn tiếp cận theo các bước nào?Junior
Gợi ý trả lời
Người phỏng vấn đánh giá cách bạn suy nghĩ và trao đổi nhiều hơn một đáp án "chuẩn". Hãy đi theo trình tự rõ ràng:
- Làm rõ yêu cầu: chức năng chính, ngoài phạm vi; yêu cầu phi chức năng (độ trễ, sẵn sàng, nhất quán).
- Ước lượng quy mô: số người dùng, tỉ lệ đọc/ghi, QPS, dung lượng lưu trong vài năm.
- API và mô hình dữ liệu chính.
- Thiết kế tổng quan: client, load balancer, service, DB, cache, queue.
- Đào sâu vào phần khó nhất: với link rút gọn là cách sinh mã ngắn không trùng và cache cho luồng redirect đọc rất nhiều.
- Điểm nghẽn và trade-off: điểm lỗi đơn lẻ, cách scale, giám sát.
Hay sai: vẽ ngay Kafka, Kubernetes, microservices khi chưa hỏi quy mô. Một thiết kế đơn giản có lý do rõ ràng được đánh giá cao hơn.
02Scale dọc (vertical) và scale ngang (horizontal) khác nhau thế nào? Load balancer đóng vai trò gì?Junior
Gợi ý trả lời
Scale dọc là nâng cấp một máy (thêm CPU, RAM); đơn giản nhưng có giới hạn phần cứng và vẫn là điểm lỗi đơn lẻ. Scale ngang là thêm nhiều máy chạy song song; gần như không giới hạn nhưng đòi hỏi ứng dụng stateless.
- Muốn scale ngang, session và file upload phải nằm ở nơi dùng chung (Redis, object storage), không nằm trên đĩa hay RAM của từng máy.
- Load balancer phân phối request (round robin, least connections), kiểm tra health để bỏ máy lỗi, có thể kết thúc TLS.
- L4 cân bằng theo kết nối TCP/UDP; L7 hiểu HTTP nên định tuyến được theo path, header.
Người phỏng vấn muốn nghe: database thường là phần khó scale ngang nhất, nên cần thêm cache, read replica, rồi mới đến partition hay sharding.
03Monolith và microservices: khi nào nên tách? Cái giá phải trả là gì?
Gợi ý trả lời
Microservices giải quyết vấn đề tổ chức: nhiều team cần deploy độc lập và scale riêng từng phần. Nó không tự làm hệ thống nhanh hơn.
Nên cân nhắc tách khi:
- Nhiều team đụng nhau trong cùng codebase, deploy phải chờ nhau.
- Một phần có tải hoặc yêu cầu công nghệ rất khác phần còn lại.
- Ranh giới nghiệp vụ (bounded context) đã rõ ràng và ổn định.
Cái giá: gọi mạng thay vì gọi hàm (độ trễ, lỗi một phần), nhất quán dữ liệu phân tán, cần tracing, service discovery, CI/CD và vận hành phức tạp hơn.
Người phỏng vấn muốn nghe: "modular monolith" là bước khởi đầu hợp lý: chia module rõ ràng trong một ứng dụng, sau này tách theo đúng ranh giới đó. Tách sai ranh giới tạo ra "distributed monolith", tệ hơn cả monolith.
04Thiết kế cache: các chiến lược cache-aside, write-through và cách xử lý cache invalidation.
Gợi ý trả lời
Cache giảm độ trễ và tải cho DB, đổi lại phải chấp nhận dữ liệu có thể cũ trong một khoảng thời gian.
- Cache-aside (phổ biến nhất): đọc cache, miss thì đọc DB rồi ghi vào cache kèm TTL. Khi cập nhật thì ghi DB rồi xoá key cache.
- Write-through: ghi qua cache rồi cache ghi xuống DB, dữ liệu cache luôn mới nhưng ghi chậm hơn.
- Write-behind: ghi cache trước, đẩy xuống DB sau; nhanh nhưng có thể mất dữ liệu.
Các vấn đề cần xử lý:
- Cache stampede: key nóng hết hạn, hàng nghìn request cùng đánh vào DB. Giải pháp: lock hoặc single-flight, làm mới sớm, thêm độ lệch ngẫu nhiên cho TTL.
- Cache penetration: truy vấn key không tồn tại liên tục. Giải pháp: cache cả kết quả rỗng, dùng bloom filter.
Hay sai: cập nhật cache thay vì xoá, dễ ghi đè giá trị cũ khi hai request chạy song song.
05Khi nào dùng message queue như Kafka hay RabbitMQ? Hai công cụ này khác nhau thế nào?
Gợi ý trả lời
Message queue tách bên gửi và bên nhận: xử lý bất đồng bộ, hấp thụ tải đột biến, cho nhiều hệ thống cùng phản ứng với một sự kiện.
- RabbitMQ: message broker truyền thống, định tuyến linh hoạt qua exchange (direct, topic, fanout), message bị xoá sau khi consumer ack. Hợp với hàng đợi tác vụ như gửi email, xử lý ảnh.
- Kafka: log phân tán chỉ ghi nối, message được giữ theo thời gian cấu hình, consumer tự quản lý offset nên đọc lại được. Thứ tự chỉ đảm bảo trong từng partition. Hợp với event streaming, thông lượng lớn, nhiều consumer group.
Lưu ý khi dùng:
- Mặc định nên coi là at-least-once, nên consumer phải idempotent.
- Có dead letter queue cho message lỗi lặp lại.
- Theo dõi consumer lag.
Người phỏng vấn muốn nghe: lý do chọn gắn với nhu cầu cụ thể, không chọn Kafka chỉ vì nghe "xịn".
06Định lý CAP là gì? Strong consistency và eventual consistency áp dụng thế nào trong thực tế?
Gợi ý trả lời
CAP nói rằng khi xảy ra phân mảnh mạng (Partition), hệ phân tán phải chọn giữa Consistency (mọi node trả dữ liệu mới nhất hoặc báo lỗi) và Availability (luôn trả lời, có thể bằng dữ liệu cũ). Khi mạng bình thường, trade-off thực tế là giữa độ trễ và tính nhất quán (mô hình PACELC).
- Strong consistency: cần cho số dư tài khoản, tồn kho, đặt chỗ, nơi đọc dữ liệu cũ gây thiệt hại thật.
- Eventual consistency: chấp nhận được cho lượt thích, bảng tin, số người xem, kết quả tìm kiếm.
Một hệ thống thường kết hợp cả hai theo từng loại dữ liệu. Có những mức ở giữa như read-your-writes: người vừa sửa thì phải thấy thay đổi của mình, người khác thấy chậm vài giây cũng không sao.
Hay sai: nói "chọn 2 trong 3" như thể có thể bỏ P. Với hệ phân tán, phân mảnh mạng là chuyện chắc chắn xảy ra.
07Thiết kế rate limiter cho API. Bạn chọn thuật toán nào và đặt ở đâu?
Gợi ý trả lời
Rate limiter bảo vệ hệ thống khỏi lạm dụng và quá tải, thường đặt ở API gateway hoặc middleware, đếm theo user, API key hoặc IP.
Thuật toán:
- Fixed window: đơn giản nhưng bị dồn request ở ranh giới hai cửa sổ.
- Sliding window (log hoặc counter): mượt hơn, tốn bộ nhớ hơn.
- Token bucket: cho phép burst ngắn trong giới hạn tốc độ trung bình, được dùng phổ biến.
Khi chạy nhiều instance, bộ đếm phải dùng chung, thường là Redis. Thao tác kiểm tra và tăng phải atomic (
INCRkèmEXPIRE, hoặc Lua script).Trả về HTTP
429kèm headerRetry-After.Người phỏng vấn muốn nghe: bạn quyết định được khi Redis lỗi thì cho qua (fail-open) hay chặn (fail-closed) và giải thích lý do, đồng thời biết giới hạn riêng cho endpoint đắt như đăng nhập, gửi OTP.
08Một service phụ thuộc bị chậm hoặc chết. Làm sao để không kéo sập cả hệ thống?Senior
Gợi ý trả lời
Cô lập lỗi để sự cố của một phụ thuộc không lan ra toàn hệ thống (cascading failure).
- Timeout cho mọi lời gọi mạng. Thiếu timeout là nguyên nhân hàng đầu làm cạn thread hoặc connection.
- Retry có giới hạn, exponential backoff kèm jitter, và chỉ retry thao tác idempotent.
- Circuit breaker: lỗi vượt ngưỡng thì "ngắt mạch", trả lỗi nhanh trong một thời gian, sau đó thử lại dần (half-open).
- Bulkhead: tách pool thread hoặc connection theo từng phụ thuộc.
- Fallback / graceful degradation: trả dữ liệu cache, ẩn tính năng phụ thay vì lỗi cả trang.
- Load shedding: từ chối bớt request khi quá tải để bảo vệ phần còn lại.
Hay sai: retry ở nhiều tầng cùng lúc, số request nhân lên theo cấp số và làm phụ thuộc đang yếu chết hẳn (retry storm).
09Làm sao đảm bảo nhất quán dữ liệu giữa nhiều microservice khi không có transaction chung?Senior
Gợi ý trả lời
Tránh two-phase commit giữa các service; thay vào đó dùng Saga kết hợp Outbox pattern.
- Saga: chuỗi transaction cục bộ, mỗi bước có hành động bù trừ nếu bước sau thất bại (vd. đặt hàng → giữ tồn kho → thanh toán; thanh toán lỗi thì hoàn tồn kho, huỷ đơn). Có hai kiểu: choreography (các service phản ứng theo event) và orchestration (một service điều phối, dễ theo dõi hơn khi luồng dài).
- Transactional outbox: ghi thay đổi nghiệp vụ và event vào bảng outbox trong cùng một transaction DB, sau đó tiến trình riêng hoặc CDC đẩy event lên broker. Cách này tránh tình huống DB đã lưu mà event bị mất.
- Consumer phải idempotent (lưu id message đã xử lý) vì event có thể đến nhiều lần.
Người phỏng vấn muốn nghe: bạn chấp nhận eventual consistency và thiết kế trạng thái trung gian rõ ràng cho người dùng, vd. đơn ở trạng thái "đang xử lý".
10Thiết kế hệ thống thông báo (notification) gửi push, email, SMS cho hàng triệu người dùng.Senior
Gợi ý trả lời
Tách phần nhận yêu cầu khỏi phần gửi qua hàng đợi, và tách riêng từng kênh để kênh này lỗi không ảnh hưởng kênh khác.
- Notification service nhận yêu cầu, kiểm tra cài đặt và tuỳ chọn tắt thông báo của người dùng, render template, rồi đẩy vào queue theo kênh.
- Worker theo kênh gọi nhà cung cấp (FCM/APNs, email, SMS), retry có backoff, chuyển message lỗi vĩnh viễn sang dead letter queue.
- Ưu tiên: OTP, giao dịch dùng queue riêng, không bị kẹt sau chiến dịch marketing gửi hàng loạt.
- Chống trùng: idempotency key cho mỗi thông báo.
- Giới hạn: rate limit theo nhà cung cấp và theo người dùng; tôn trọng khung giờ yên lặng.
- Lưu trạng thái gửi, mở, lỗi để theo dõi và xử lý token thiết bị hết hạn.
Người phỏng vấn muốn nghe: bạn ước lượng được lưu lượng lúc cao điểm và nêu cách scale worker theo độ dài hàng đợi.
11Hệ thống microservices gặp sự cố, bạn cần những gì để quan sát (observability) và tìm nguyên nhân?Senior
Gợi ý trả lời
Cần đủ ba nguồn tín hiệu được liên kết với nhau: metrics, logs, traces.
- Metrics: theo phương pháp RED cho service (Rate, Errors, Duration với p95, p99) và USE cho tài nguyên. Cảnh báo dựa trên SLO (vd. tỉ lệ lỗi, độ trễ) thay vì CPU đơn thuần.
- Logs có cấu trúc (JSON), kèm
trace_idvàrequest_idđể lọc toàn bộ log của một request qua nhiều service. - Distributed tracing với OpenTelemetry: thấy request đi qua những service nào, chậm ở đoạn nào.
- Dashboard theo từng service, ghi nhận các lần deploy để đối chiếu với thời điểm lỗi.
Khi có sự cố: ưu tiên khôi phục (rollback, tắt feature flag) trước, tìm nguyên nhân gốc sau, rồi viết postmortem không đổ lỗi cá nhân.
Người phỏng vấn muốn nghe: một sự cố thật bạn từng tham gia và điều đã cải thiện sau đó.
12Là kiến trúc sư, bạn đưa ra và ghi lại một quyết định kiến trúc quan trọng như thế nào?Senior
Gợi ý trả lời
Quyết định kiến trúc là chọn trade-off dựa trên ràng buộc thực tế (nghiệp vụ, đội ngũ, ngân sách, thời gian), không phải chọn công nghệ "tốt nhất".
- Xác định các thuộc tính chất lượng quan trọng nhất (hiệu năng, sẵn sàng, bảo mật, chi phí, khả năng thay đổi) và sắp xếp ưu tiên cùng stakeholder.
- Đưa ra 2–3 phương án, so sánh rủi ro; làm POC hoặc spike cho điểm chưa chắc chắn.
- Ghi lại bằng ADR (Architecture Decision Record): bối cảnh, quyết định, phương án đã loại, hệ quả. Lưu cùng repo để người sau hiểu "vì sao".
- Phân biệt quyết định khó đảo ngược (chọn DB, ranh giới service) với quyết định dễ thay đổi; quyết định dễ đảo ngược thì nên làm nhanh.
- Xem lại quyết định khi bối cảnh thay đổi.
Người phỏng vấn muốn nghe: bạn thuyết phục đội ngũ bằng dữ liệu, lắng nghe phản biện, và kể được một quyết định từng sai cùng cách bạn sửa.
Gặp câu hỏi khác khi phỏng vấn? Kể lại ẩn danh để người sau ôn đúng trọng tâm, hoặc xem câu hỏi thật hay gặp ở nhiều công ty.