Chào các bạn, tôi có gom được một số câu hỏi khá hay trên diễn đàn cùng một số giả định từ những người anh em mảng dev, ops về các câu hỏi phỏng vấn vị trí Senior Backend. Tôi muốn forward lại những gì tôi đã được chia sẻ các câu hỏi hóc búa đến cách tư duy giải quyết vấn đề của một System Design, API, Security, Database, Messaging, DevOps và Code Quality. Bài viết này sẽ giúp bạn chuẩn bị tốt với các ví dụ thực tế!
Dựa trên các câu hỏi từng gặp đề cập đến cuộc phỏng vấn Senior Backend, tôi đã xây dựng cách tiếp cận, giải pháp và áp dụng vào thực tế. Mỗi phần đều có ví dụ minh họa và bài học để áp dụng hiệu quả hơn.
✏️ Mảng kiến thức: System Design và Scalability
1. Thiết kế 1 hệ thống cho phép gửi email hàng loạt đến hàng triệu user, yêu cầu độ trễ thấp, đảm bảo delivery success rate. Giải pháp kiến trúc là gì?
Dựa trên câu hỏi này, tôi đưa ra bài toán: Cần một hệ thống gửi email hàng loạt với độ trễ thấp và đảm bảo tỷ lệ gửi thành công cao.
Giải pháp: Sử dụng kiến trúc microservices kết hợp với hệ thống hàng đợi (message queue):
- Một service nhận yêu cầu gửi email và đẩy vào hàng đợi (Kafka/RabbitMQ).
- Nhiều worker lấy email từ hàng đợi và gửi qua dịch vụ email (AWS SES, SendGrid).
- Dùng load balancer để phân phối tải và retry mechanism để đảm bảo email gửi thành công.
- Lưu trạng thái gửi vào database (PostgreSQL) để theo dõi delivery success rate.
Ví dụ: Hệ thống gửi email đến hàng triệu user
- Service chính nhận yêu cầu → đẩy vào Kafka topic
email-queue
. - 50 worker đọc từ
email-queue
, mỗi worker gửi 20 email/giây qua SendGrid. - Nếu gửi thất bại, worker retry 3 lần trước khi ghi log lỗi vào database.
Bài học:
Khi trả lời, tôi tập trung giải thích lý do chọn Kafka (scale tốt) và cách retry mechanism đảm bảo delivery success rate. Người phỏng vấn đánh giá cao việc đề cập đến monitoring (lưu trạng thái vào DB).
2. Làm thế nào để scale 1 service xử lý request tăng đột biến từ 1K lên 100K request/s trong vòng vài phút?
Dựa trên câu hỏi, bài toán là xử lý tình huống tăng đột biến request từ 1K lên 100K request/s trong thời gian ngắn.
Giải pháp:
- Dùng horizontal scaling: Thêm nhiều instance của service (Kubernetes/AWS ECS).
- Sử dụng load balancer (AWS ALB) để phân phối request.
- Tăng hiệu suất với caching (Redis) và autoscaling để tự động thêm instance khi tải tăng.
Ví dụ: Service ban đầu xử lý 1K request/s
- Cấu hình Kubernetes autoscaling: Nếu CPU usage > 70%, thêm 10 pod mới.
- Dùng Redis để cache kết quả của 80% request lặp lại → giảm tải cho server.
- AWS ALB phân phối đều 100K request/s cho 50 pod, mỗi pod xử lý 2K request/s.
Bài học:
Người phỏng vấn hỏi thêm về trường hợp Redis bị quá tải. Tôi trả lời rằng có thể dùng Redis Cluster để scale hoặc thêm fallback mechanism (như DB). Câu hỏi này kiểm tra tư duy xử lý bottleneck.
3. Nếu service A gọi service B và B bị quá tải, A nên xử lý như thế nào để không gây chain-failure?
Câu hỏi đặt ra bài toán: Làm sao để tránh chain-failure khi service A gọi service B nhưng B bị quá tải.
Giải pháp:
- Áp dụng circuit breaker pattern: Nếu B quá tải, A tạm dừng gọi và trả về lỗi mặc định.
- Dùng timeout và retry: Nếu B không phản hồi trong 2 giây, A thử lại 1-2 lần.
- Lưu yêu cầu vào hàng đợi để xử lý sau khi B ổn định.
Ví dụ: Service A gọi API của B
- Dùng thư viện Resilience4j (Java) để cấu hình circuit breaker.
- Nếu B không phản hồi trong 2 giây, circuit breaker mở → A trả về lỗi “Service unavailable”.
- Yêu cầu bị lỗi được đẩy vào hàng đợi RabbitMQ để xử lý lại sau.
Bài học:
Câu hỏi kiểm tra khả năng xử lý lỗi trong hệ thống phân tán. Tôi đề cập đến circuit breaker và retry, nhưng người phỏng vấn hỏi thêm về monitoring. Tôi bổ sung rằng cần log trạng thái circuit breaker để dễ debug.
4. Làm sao để triển khai zero downtime deployment cho hệ thống đang chạy production real-time?
Bài toán từ câu hỏi: Triển khai hệ thống production real-time mà không gây downtime.
Giải pháp:
- Sử dụng blue-green deployment hoặc canary deployment:
- Blue-green: Chạy 2 môi trường, chuyển traffic sang môi trường mới khi sẵn sàng.
- Canary: Triển khai bản mới cho một phần user trước, mở rộng nếu ổn.
- Dùng Kubernetes để quản lý deployment và rollback nhanh.
Ví dụ: Triển khai blue-green trên Kubernetes
- Môi trường Blue (phiên bản cũ) đang chạy, phục vụ 100% traffic.
- Triển khai Green (phiên bản mới) trên 10 pod mới.
- Chuyển traffic sang Green qua Kubernetes Ingress → không downtime.
- Nếu có lỗi, quay lại Blue ngay lập tức.
Bài học:
Người phỏng vấn hỏi về chi phí của blue-green deployment (cần gấp đôi tài nguyên). Tôi trả lời rằng có thể dùng canary để tiết kiệm tài nguyên, vẫn đảm bảo zero downtime.
5. Làm sao để trace 1 request đi qua 5 service khác nhau trong hệ thống microservice?
Câu hỏi đặt ra bài toán: Làm sao để theo dõi một request đi qua nhiều service trong hệ thống microservice.
Giải pháp:
- Sử dụng distributed tracing:
- Gắn trace ID và span ID vào mỗi request (Jaeger/Zipkin).
- Mỗi service ghi log với trace ID để theo dõi lộ trình.
- Dùng dashboard của tracing tool để xem toàn bộ hành trình.
Ví dụ: Request đi qua 5 service (A → B → C → D → E)
- A tạo trace ID
12345
và span ID1
. - B nhận trace ID
12345
, tạo span ID2
, ghi log. - Tương tự cho C, D, E.
- Dùng Jaeger dashboard để xem timeline: A (0ms) → B (10ms) → C (20ms) → D (30ms) → E (40ms).
Bài học:
Tôi từng trả lời sai khi không đề cập đến log correlation (gắn trace ID vào log). Sau khi bổ sung, người phỏng vấn đánh giá cao tư duy toàn diện về observability.
✏️ Mảng kiến thức: API Design và Real-World Practices
1. Khi nào nên dùng GRPC, GraphQL, REST?
Câu hỏi đặt ra bài toán: Lựa chọn công nghệ phù hợp để thiết kế API.
Giải pháp:
- gRPC: Dùng khi cần hiệu suất cao (giao tiếp giữa các service).
- GraphQL: Dùng khi client cần dữ liệu linh hoạt (ứng dụng mobile).
- REST: Dùng khi hệ thống đơn giản, dễ tích hợp (API public).
Ví dụ:
- gRPC: Service A gọi B để lấy dữ liệu real-time (dùng Protobuf, truyền nhanh).
- GraphQL: Ứng dụng mobile chỉ lấy
name
vàemail
của user, không lấy toàn bộ dữ liệu. - REST: API
/users
trả về danh sách user cho đối tác.
Bài học:
Câu hỏi kiểm tra hiểu biết về trade-off. Tôi giải thích thêm rằng gRPC khó debug hơn REST, và người phỏng vấn hài lòng với việc cân nhắc thực tế.
2. Làm sao để versioning 1 API mà không break client cũ? Có những chiến lược nào?
Câu hỏi đặt ra bài toán: Làm sao để quản lý phiên bản API mà không ảnh hưởng client cũ.
Giải pháp:
- Chiến lược versioning:
- URL versioning:
/api/v1/resource
. - Header versioning:
Accept: application/vnd.api.v1+json
. - Query parameter:
/resource?version=1
.
- URL versioning:
- Giữ version cũ hoạt động một thời gian và thông báo trước khi bỏ.
Ví dụ:
- API
/api/v1/users
(version 1) trả vềname
,email
. - Version 2
/api/v2/users
thêmphone
. - Client cũ vẫn gọi
/api/v1/users
mà không bị lỗi.
Bài học:
Tôi từng quên đề cập đến thông báo trước khi bỏ version cũ. Người phỏng vấn nhắc nhở, và tôi học được rằng communication với client cũng quan trọng không kém.
3. Triển khai rate-limit trên API public, những cách phổ biến và ưu nhược điểm?
Câu hỏi đặt ra bài toán: Triển khai rate-limit cho API public một cách hiệu quả.
Giải pháp:
- Fixed Window: Giới hạn số request trong khoảng thời gian (100 request/phút).
- Ưu: Dễ triển khai.
- Nhược: Có thể bị “burst” vào đầu window.
- Token Bucket: Client có số token, mỗi request tiêu tốn 1 token.
- Ưu: Kiểm soát đều hơn.
- Nhược: Cần lưu trữ trạng thái token.
- Dùng Redis để lưu trạng thái rate-limit.
Ví dụ:
- Fixed Window: User A được 100 request/phút → nếu gửi 101 request, bị chặn.
- Token Bucket: User B có 50 token, gửi 40 request → còn 10 token, nạp lại 5 token/phút.
Bài học:
Người phỏng vấn hỏi về cách xử lý nếu Redis thất bại. Tôi trả lời rằng cần có fallback (như in-memory rate-limit) và log để debug.
4. Thiết kế 1 API cho phép user upload file nặng >500MB nhưng không timeout?
Câu hỏi đặt ra bài toán: Thiết kế API upload file lớn mà không bị timeout.
Giải pháp:
- Dùng multipart upload: Chia file thành nhiều phần nhỏ.
- Sử dụng presigned URL (AWS S3) để upload trực tiếp lên storage.
- Dùng asynchronous processing: Xử lý file nền, thông báo qua WebSocket.
Ví dụ:
- API
/upload/init
tạo presigned URL cho S3. - Client upload file 1GB (chia thành 10 phần 100MB) trực tiếp lên S3.
- Sau khi upload xong, server nhận thông báo qua SQS → xử lý file và gửi email thông báo.
Bài học:
Câu hỏi kiểm tra tư duy về hiệu suất. Tôi từng quên đề cập đến thông báo kết quả, nhưng sau khi bổ sung qua WebSocket, người phỏng vấn đánh giá cao.
✏️ Mảng kiến thức: Security, Auth
1. OAuth2 + JWT hệ thống có những lỗ hổng gì thường gặp? Làm sao để chống lại lỗ hổng đó?
Câu hỏi đặt ra bài toán: Xác định các lỗ hổng bảo mật phổ biến trong hệ thống dùng OAuth2 và JWT, đồng thời đưa ra cách khắc phục.
Giải pháp:
- Làm rõ các lỗ hổng thường gặp:
- Token bị đánh cắp: Access token hoặc refresh token bị chặn (qua MITM, XSS).
- JWT không được xác minh đúng cách: Nếu không kiểm tra chữ ký hoặc thuật toán (alg: none), kẻ tấn công có thể giả mạo token.
- Refresh token bị reuse: Nếu refresh token không được xoay vòng (rotation), kẻ tấn công có thể dùng lại token cũ.
- Thiếu kiểm soát truy cập: Token không bị giới hạn scope, dẫn đến truy cập trái phép.
- Chống lại:
- Lưu access token và refresh token an toàn: Sử dụng HTTP-only cookie với
Secure
vàSameSite=Strict
. - Token rotation: Tạo refresh token mới mỗi lần refresh, vô hiệu hóa token cũ.
- Xác minh JWT chặt chẽ: Luôn kiểm tra chữ ký, thuật toán, và thời gian hết hạn (
exp
). - Giới hạn scope: Chỉ cấp quyền tối thiểu cần thiết trong access token.
- Kiểm tra IP/device: Nếu phát hiện hành vi bất thường (IP thay đổi), yêu cầu xác thực lại.
- Lưu access token và refresh token an toàn: Sử dụng HTTP-only cookie với
Ví dụ:
- Refresh token lưu trong HTTP-only cookie, đặt thời gian sống 7 ngày.
- Khi refresh, server tạo refresh token mới, vô hiệu hóa token cũ, và lưu vào Redis với trạng thái revoked.
- Access token có scope
read:user
, không cho phép truy cập endpoint/admin
. - Server kiểm tra thuật toán JWT (
HS256
), từ chối nếualg: none
. - Nếu IP client thay đổi bất thường, yêu cầu đăng nhập lại.
Bài học:
Người phỏng vấn hỏi thêm về trường hợp XSS tấn công cookie. Tôi trả lời rằng cần thêm biện pháp chống XSS (sanitize input, dùng Content Security Policy) và có thể kết hợp với 2FA để tăng bảo mật.
2. Tình huống quy trình refresh token an toàn không bị reuse attack?
Câu hỏi đặt ra bài toán: Làm sao để refresh token không bị tấn công reuse.
Giải pháp:
- Dùng refresh token rotation: Tạo token mới mỗi lần refresh, vô hiệu hóa token cũ.
- Lưu refresh token trong database, kiểm tra trạng thái trước khi cấp access token.
- Áp dụng short-lived access token (thời hạn 15 phút).
Ví dụ:
- User A có refresh token
RT1
. - Khi refresh, server tạo
RT2
, vô hiệu hóaRT1
. - Nếu kẻ tấn công dùng
RT1
, server từ chối vì token đã bị vô hiệu.
Bài học:
Câu hỏi kiểm tra hiểu biết về bảo mật. Tôi đề cập đến short-lived token, nhưng người phỏng vấn hỏi thêm về overhead của việc lưu token trong DB. Tôi giải thích rằng có thể dùng Redis để tối ưu.
3. Nếu dùng Keycloak làm SSO thì bảo vệ resource internal (ex: /admin) như thế nào? Cấu hình thực tế?
Câu hỏi đặt ra bài toán: Bảo vệ resource internal khi dùng Keycloak làm SSO.
Giải pháp:
- Trong Keycloak, tạo role (
admin
) và gán cho user. - Cấu hình policy: Chỉ user có role
admin
truy cập/admin
. - Dùng Keycloak adapter (Spring Security) để kiểm tra token.
Ví dụ:
- Trong Keycloak: Tạo role
admin
, gán cho userjohn
. Ví dụ cấu hình Spring Security:
1 2 3
http.authorizeRequests() .antMatchers("/admin/**").hasRole("admin") .and().oauth2ResourceServer().jwt();
- User không có role
admin
gọi/admin
→ nhận lỗi 403.
Bài học:
Tôi từng quên đề cập đến Keycloak adapter. Sau khi bổ sung, người phỏng vấn hỏi thêm về performance impact của việc gọi Keycloak thường xuyên. Tôi trả lời rằng có thể cache token validation.
4. Khi nào thì nên dùng Mutual TLS?
Câu hỏi đặt ra bài toán: Xác định trường hợp áp dụng Mutual TLS.
Giải pháp:
- Dùng Mutual TLS khi cần bảo mật cao trong giao tiếp giữa server và client (microservices, API nhạy cảm).
- Cả server và client phải có certificate để xác thực lẫn nhau.
Ví dụ: Service A và B giao tiếp qua Mutual TLS
- A có client certificate, B có server certificate.
- A gửi request → B kiểm tra certificate của A trước khi xử lý.
- Thích hợp cho API tài chính (như chuyển tiền).
Bài học:
Câu hỏi khá đơn giản, nhưng người phỏng vấn hỏi thêm về overhead của Mutual TLS. Tôi trả lời rằng cần cân nhắc giữa bảo mật và hiệu suất, có thể dùng cho các API nhạy cảm nhất.
✏️ Mảng kiến thức: Database, Data Strategy
1. Trong hệ thống với MySQL, dữ liệu >100M bản ghi, làm thế nào để query nhanh cho báo cáo phức tạp (group by, join, filter)?
Câu hỏi đặt ra bài toán: Tối ưu query cho báo cáo phức tạp với dữ liệu lớn trên MySQL.
Giải pháp:
- Tối ưu index: Tạo index cho các cột thường dùng trong
WHERE
,JOIN
,GROUP BY
. - Sử dụng partitioning: Chia table thành các partition nhỏ (theo thời gian, ID).
- Dùng materialized view hoặc denormalized table để lưu kết quả báo cáo.
- Tận dụng caching (Redis) cho các query lặp lại.
- Nếu cần scale lớn hơn, cân nhắc data warehouse (như Snowflake, BigQuery).
Ví dụ: Table orders
có 100M bản ghi
- Tạo index trên cột
order_date
vàcustomer_id
cho querySELECT customer_id, SUM(amount) FROM orders WHERE order_date > '2024-01-01' GROUP BY customer_id
. - Partition table
orders
theoorder_date
(mỗi năm 1 partition). - Lưu kết quả báo cáo vào table
order_summary
(cập nhật hàng đêm). - Cache kết quả query trong Redis với key
report:customer:2024
.
Bài học:
Người phỏng vấn hỏi về trade-off của materialized view (cần update định kỳ). Tôi trả lời rằng có thể dùng job scheduler (như Cron) để update vào giờ thấp điểm.
2. So sánh vertical partitioning vs horizontal sharding, khi nào dùng cái nào?
Câu hỏi đặt ra bài toán: Phân biệt và áp dụng vertical partitioning và horizontal sharding.
Giải pháp:
- Vertical Partitioning: Chia table thành các table nhỏ hơn theo cột (ví dụ: cột ít dùng để riêng).
- Dùng khi: Một số cột ít truy cập, giảm kích thước table chính.
- Horizontal Sharding: Chia dữ liệu thành các shard theo hàng (ví dụ: theo user ID).
- Dùng khi: Dữ liệu lớn, cần scale đọc/ghi trên nhiều server.
- So sánh: Vertical partitioning đơn giản hơn, nhưng không scale tốt bằng sharding. Sharding phức tạp hơn (phân phối dữ liệu, query phức tạp).
Ví dụ:
- Vertical Partitioning: Table
users
có cộtbio
ít dùng → tách thành tableuser_bios
. - Horizontal Sharding: Table
orders
chia thành 4 shard theouser_id % 4
→ mỗi shard lưu trên 1 server MySQL riêng.
Bài học:
Người phỏng vấn hỏi thêm về việc nếu shard không đều thì sao. Tôi trả lời rằng cần chọn key sharding hợp lý (như user ID) và có thể rebalance định kỳ.
3. Khi nào NoSQL là lựa chọn tối ưu? Từng gap case nào chưa?
Câu hỏi đặt ra bài toán: Xác định khi nào nên chọn NoSQL và áp dụng thực tế.
Giải pháp:
- Dùng NoSQL khi:
- Dữ liệu không có cấu trúc cố định (JSON, key-value).
- Cần scale ngang dễ dàng (MongoDB, DynamoDB).
- Xử lý khối lượng đọc/ghi lớn (Cassandra).
- Case phổ biến: Lưu log, dữ liệu thời gian thực, hoặc dữ liệu không cần transaction phức tạp.
Ví dụ:
- Dùng MongoDB để lưu dữ liệu user profile (các field linh hoạt:
name
,email
,settings
). - Dùng Cassandra để lưu log truy cập website (1 tỷ bản ghi/ngày, đọc/ghi nhanh).
- Case thực tế: Một ứng dụng chat dùng DynamoDB để lưu tin nhắn (key:
user_id
, value: danh sách tin nhắn).
Bài học:
Người phỏng vấn hỏi về nhược điểm của NoSQL (khó transaction). Tôi giải thích rằng nếu cần transaction, có thể dùng hybrid approach (kết hợp SQL và NoSQL).
4. Làm sao để migrate dữ liệu, không downtime, không mất mát?
Câu hỏi đặt ra bài toán: Migrate dữ liệu mà không gây downtime hay mất mát.
Giải pháp:
- Dùng dual-write strategy: Ghi dữ liệu vào cả DB cũ và mới trong quá trình migrate.
- Migrate dữ liệu cũ sang DB mới bằng batch (dùng tool như Apache Spark).
- Xác minh tính toàn vẹn dữ liệu (so sánh bản ghi).
- Chuyển traffic sang DB mới và dừng ghi vào DB cũ.
- Theo dõi lỗi và có rollback plan.
Ví dụ: Migrate từ MySQL sang PostgreSQL
- Viết code ghi đồng thời vào MySQL và PostgreSQL.
- Dùng script Python + Spark để copy 100M bản ghi từ MySQL sang PostgreSQL (batch 1M bản ghi/lần).
- So sánh số lượng bản ghi giữa 2 DB → đảm bảo khớp.
- Chuyển traffic sang PostgreSQL, nếu có lỗi thì quay lại MySQL.
Bài học:
Tôi từng không đề cập đến rollback plan. Sau khi bị hỏi, tôi bổ sung và học được rằng luôn cần plan B khi migrate dữ liệu lớn.
✏️ Mảng kiến thức: Messaging, Async
1. Khi nào thì dùng Kafka thay vì RabbitMQ? Từng gặp tình huống thực tế nào chưa?
Câu hỏi đặt ra bài toán: lựa chọn giữa Kafka và RabbitMQ cho hệ thống.
Giải pháp:
- Kafka: Dùng khi cần xử lý khối lượng lớn dữ liệu thời gian thực, lưu trữ log lâu dài, hoặc cần replay message.
- RabbitMQ: Dùng khi cần hàng đợi đơn giản, xử lý message nhanh, không cần lưu trữ lâu dài.
- Tình huống thực tế: Kafka thường dùng trong hệ thống log, streaming. RabbitMQ dùng cho task queue.
Ví dụ:
- Kafka: Hệ thống phân tích log website, 1M event/giây → đẩy vào Kafka topic
web-logs
, xử lý bằng Spark Streaming. - RabbitMQ: Hệ thống gửi thông báo → đẩy task vào queue
notifications
, worker lấy và gửi qua Firebase. - Case thực tế: Một hệ thống e-commerce dùng Kafka để theo dõi hành vi user (click, xem sản phẩm) và RabbitMQ để xử lý đơn hàng.
Bài học:
Câu hỏi kiểm tra kinh nghiệm thực tế. Tôi từng chỉ nói lý thuyết, nhưng sau khi bổ sung case e-commerce, người phỏng vấn đánh giá cao.
2. Kafka có thể đảm bảo exactly-once không? Làm được không? Nếu không thì workaround thế nào?
Câu hỏi đặt ra bài toán: Đảm bảo exactly-once delivery trong Kafka.
Giải pháp:
- Kafka có thể đảm bảo exactly-once (từ version 0.11) với Transactional Producer API: Đảm bảo message được ghi và xử lý đúng 1 lần.
- Nếu không dùng exactly-once:
- Dùng idempotency (message có ID duy nhất, consumer kiểm tra trước khi xử lý).
- Lưu trạng thái xử lý vào DB (offset, message ID).
Ví dụ:
- Exactly-once: Producer gửi message vào topic
orders
với transaction ID → consumer đọc và commit offset trong cùng transaction. - Workaround: Consumer lưu
message_id
vào Redis → nếumessage_id
đã tồn tại, bỏ qua message.
Bài học:
Người phỏng vấn hỏi thêm về overhead của exactly-once. Tôi trả lời rằng cần cân nhắc giữa độ chính xác và performance, có thể dùng idempotency nếu không cần exactly-once.
3. Triển khai distributed transaction qua message queue thì dùng giải pháp gì?
Câu hỏi đặt ra bài toán: Triển khai distributed transaction qua message queue.
Giải pháp:
- Dùng Saga Pattern: Chia transaction thành các bước nhỏ, mỗi bước gửi message qua queue.
- Nếu bước nào thất bại, gửi message bù trừ (compensating transaction).
- Sử dụng choreography (các service tự xử lý) hoặc orchestration (có service điều phối).
Ví dụ:
Hệ thống đặt hàng:
- Service
order
tạo đơn hàng → gửi messagecreate-order
vào Kafka. - Service
payment
xử lý thanh toán → nếu thất bại, gửicancel-order
để hủy. - Dùng choreography: Mỗi service tự lắng nghe và phản ứng với message.
Bài học:
Người phỏng vấn hỏi về ưu nhược điểm của choreography vs orchestration. Tôi trả lời rằng choreography dễ scale nhưng khó debug, còn orchestration dễ quản lý hơn.
✏️ Mảng kiến thức: DevOps / Monitoring / Logging
1. Hệ thống production bị tăng CPU đột biến, request timeout, các bước điều tra?
Câu hỏi đặt ra bài toán: Điều tra nguyên nhân CPU tăng đột biến và request timeout trong production.
Giải pháp:
- Kiểm tra monitoring dashboard (Prometheus/Grafana) để xem CPU, memory, request rate.
- Xem logs (ELK Stack) để tìm lỗi hoặc request chậm.
- Dùng profiling (như Java Flight Recorder) để xác định hàm gây CPU cao.
- Kiểm tra external dependencies (DB, API bên ngoài) xem có chậm không.
- Scale ngang nếu cần (thêm instance).
Ví dụ:
- Grafana báo CPU tăng 90% lúc 10:00 AM.
- Log trong Kibana cho thấy query DB
/orders
mất 5 giây → kiểm tra DB, thấy thiếu index trên cộtorder_date
. - Thêm index → CPU giảm xuống 40%, timeout biến mất.
Bài học:
Câu hỏi kiểm tra kỹ năng troubleshooting. Tôi từng bỏ qua bước profiling, nhưng sau khi bổ sung, người phỏng vấn đánh giá cao cách tiếp cận hệ thống.
2. Tư duy thiết kế logging & tracing ở môi trường microservice?
Câu hỏi đặt ra bài toán: Thiết kế logging và tracing hiệu quả trong hệ thống microservice.
Giải pháp:
- Logging:
- Mỗi service ghi log có cấu trúc (JSON) với
trace ID
,timestamp
,level
. - Dùng log aggregator (ELK Stack, Fluentd).
- Mỗi service ghi log có cấu trúc (JSON) với
- Tracing:
- Gắn
trace ID
vàspan ID
vào mỗi request (Jaeger/Zipkin). - Theo dõi toàn bộ timeline request qua các service.
- Gắn
- Đảm bảo log và trace có thể truy xuất nhanh.
Ví dụ:
- Service A ghi log:
{"trace_id": "12345", "message": "Processing request", "timestamp": "2025-04-21T10:00:00Z"}
. - Jaeger tracing: Request đi qua A → B → C, tổng thời gian 150ms, B chậm nhất (100ms).
- Dùng Kibana để tìm log liên quan với
trace_id: 12345
.
Bài học:
Người phỏng vấn hỏi thêm về chi phí lưu trữ log. Tôi trả lời rằng có thể dùng log rotation và chỉ lưu log quan trọng trong 30 ngày.
3. Dùng Prometheus, Grafana để monitor nhiệm vụ gì? Alert rule setup thế nào?
Câu hỏi đặt ra bài toán: Thiết lập monitoring và alerting với Prometheus và Grafana.
Giải pháp:
- Monitor: CPU, memory, request latency, error rate, throughput.
- Alert Rule:
- Dùng Prometheus để thu thập metrics.
- Cấu hình alert trong Prometheus → gửi qua Alertmanager (email, Slack).
- Grafana hiển thị dashboard và alert trực quan.
Ví dụ:
- Monitor: Prometheus thu thập metric
http_request_duration_seconds
. Alert Rule trong Prometheus:
1 2 3 4 5 6 7
- alert: HighLatency expr: avg(http_request_duration_seconds) > 1 for: 5m labels: severity: critical annotations: summary: "High request latency detected"
- Grafana hiển thị biểu đồ latency, gửi alert qua Slack nếu latency > 1 giây trong 5 phút.
Bài học:
Câu hỏi kiểm tra kinh nghiệm thực tế với monitoring. Tôi từng không đề cập đến Grafana dashboard, nhưng sau khi bổ sung, người phỏng vấn đánh giá cao tính trực quan.
4. Làm CI/CD pipeline tự động deploy Docker image lên Kubernetes, rollback khi có lỗi?
Câu hỏi đặt ra bài toán: Xây dựng CI/CD pipeline tự động và xử lý lỗi khi deploy.
Giải pháp:
- CI/CD Pipeline:
- CI: Build Docker image, push lên registry (Docker Hub).
- CD: Deploy image lên Kubernetes qua Helm hoặc manifest.
- Rollback:
- Dùng Kubernetes rollout history để quay lại phiên bản trước.
- Cấu hình health check để tự động rollback nếu pod không sẵn sàng.
Ví dụ:
- Pipeline trong GitHub Actions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
jobs: build: runs-on: ubuntu-latest steps: - name: Build and push Docker image run: | docker build -t myapp:$ . docker push myapp:$ deploy: runs-on: ubuntu-latest steps: - name: Deploy to Kubernetes run: | kubectl apply -f deployment.yaml kubectl rollout status deployment/myapp
- Rollback:
kubectl rollout undo deployment/myapp
.
Bài học:
Người phỏng vấn hỏi về cách phát hiện lỗi sớm. Tôi trả lời rằng cần thêm health check và smoke test sau khi deploy để đảm bảo hệ thống hoạt động đúng.
✏️ Mảng kiến thức: Code quality, Process
1. Làm thế nào để đảm bảo codebase dễ maintain trong team 10 người dev?
Câu hỏi đặt ra bài toán: Duy trì codebase chất lượng trong team lớn.
Giải pháp:
- Code style: Dùng linter (ESLint, Prettier) để chuẩn hóa code.
- Modularization: Chia code thành module nhỏ, dễ đọc.
- Documentation: Viết README, comment rõ ràng.
- Testing: Viết unit test, integration test (80% coverage).
- Code review: Mỗi pull request phải được review bởi ít nhất 1 người.
Ví dụ:
- Dùng ESLint trong dự án JavaScript:
.eslintrc.json
yêu cầu 2 space indent, không dùngvar
. - Chia code thành module:
userService.js
,authService.js
. - Viết test với Jest:
userService.test.js
kiểm tra hàmgetUserById
. - PR trên GitHub phải có ít nhất 1 approve từ team lead.
Bài học:
Câu hỏi kiểm tra kỹ năng teamwork. Tôi từng không đề cập đến documentation, nhưng sau khi bổ sung, người phỏng vấn đánh giá cao việc chú trọng readability.
2. Review code người khác, quan tâm điều gì nhất?
Câu hỏi đặt ra bài toán: Xác định các yếu tố quan trọng khi review code.
Giải pháp:
- Đúng yêu cầu: Code có giải quyết vấn đề không?
- Đọc hiểu: Code có dễ hiểu, đặt tên biến rõ ràng không?
- Hiệu suất: Có điểm nào chậm, dư thừa không?
- Bảo mật: Có lỗ hổng (SQL injection, XSS) không?
- Test: Có viết test cho code mới không?
Ví dụ:
Review một API /users
:
- Kiểm tra: API có trả về đúng dữ liệu (name, email)?
- Tên biến:
usr
→ đổi thànhuser
cho rõ nghĩa. - Hiệu suất: Tránh query DB trong vòng lặp → dùng batch query.
- Bảo mật: Đảm bảo sanitize input để tránh SQL injection.
- Yêu cầu thêm unit test cho endpoint
/users
.
Bài học:
Người phỏng vấn hỏi thêm về cách xử lý xung đột khi review. Tôi trả lời rằng cần thảo luận dựa trên dữ liệu (profiling, test) để thuyết phục.
3. Từng cải thiện performance 1 function/API nào? Trước và sau như thế nào?
Câu hỏi đặt ra bài toán: Cải thiện performance cho một API cụ thể.
Giải pháp:
- Tối ưu query (thêm index, giảm JOIN).
- Dùng caching (Redis).
- Giảm logic dư thừa.
Ví dụ:
API /products
:
- Trước:
SELECT * FROM products WHERE category_id = 5
(không index) → 2 giây. - Sau:
- Thêm index trên
category_id
. - Cache kết quả trong Redis với key
products:category:5
→ 200ms.
- Thêm index trên
- Kết quả: Latency giảm 90%, user experience cải thiện.
Bài học:
Câu hỏi kiểm tra kinh nghiệm thực tế. Tôi từng không đề cập đến user experience, nhưng sau khi bổ sung, người phỏng vấn đánh giá cao tư duy toàn diện.
Comments powered by Disqus.