Việc làm

2 ngày trước · Hồ Chí Minh · Hybrid

Principal Java/Kotlin Engineer (Spring Security & Auth)

MONEY FORWARD VIETNAM CO.,LTD · Software/Technical Architect

Xem mô tả & ứng tuyển trên ITviecMô tả công việc, lương và ứng tuyển nằm ở ITviec.

10 câu hỏi có thể gặp khi phỏng vấn

Chọn theo kỹ năng và cấp của tin (SQL & Database, Java, Mobile (Android, iOS, Flutter)). Gợi ý trả lời do Drama Công Ty biên soạn để bạn ôn nhanh, không phải đề của công ty.

  1. 01Làm sao migrate schema (thêm cột, đổi kiểu, thêm index) trên bảng lớn mà không downtime?SQL & Database · Senior

    Gợi ý trả lời

    Chia thay đổi thành nhiều bước tương thích ngược để phiên bản code cũ và mới chạy song song được (expand rồi contract).

    1. Expand: thêm cột mới cho phép NULL hoặc có default; tránh câu lệnh viết lại cả bảng.
    2. Code mới ghi vào cả cột cũ và mới.
    3. Backfill dữ liệu cũ theo từng lô nhỏ, có nghỉ giữa các lô để không gây lag replica.
    4. Chuyển code sang đọc cột mới, kiểm tra kỹ.
    5. Contract: bỏ cột cũ ở một lần deploy sau.

    Index trên bảng lớn: PostgreSQL dùng CREATE INDEX CONCURRENTLY; MySQL dùng online DDL hoặc công cụ như gh-ost, pt-online-schema-change.

    Người phỏng vấn muốn nghe: bạn biết DDL có thể giữ lock khiến mọi query xếp hàng, nên đặt lock_timeout và luôn có kế hoạch rollback.

    Ôn thêm câu hỏi SQL & Database →
  2. 02Composite index (index nhiều cột) hoạt động ra sao? Thứ tự cột có quan trọng không?SQL & Database

    Gợi ý trả lời

    Có. Composite index sắp xếp theo cột đầu, rồi đến cột thứ hai trong cùng giá trị cột đầu, v.v. Vì vậy nó chỉ dùng hiệu quả khi query dùng các cột từ trái sang (leftmost prefix).

    Với INDEX (status, created_at):

    • WHERE status = 'paid' AND created_at > ...: dùng tốt.
    • WHERE status = 'paid' ORDER BY created_at DESC: dùng cho cả lọc lẫn sắp xếp.
    • WHERE created_at > ... (không có status): thường không dùng được.

    Nguyên tắc chọn thứ tự: cột so sánh bằng (=) đặt trước, cột so sánh khoảng hoặc sắp xếp đặt sau. Nếu index chứa đủ mọi cột query cần (covering index) thì DB không phải đọc lại bảng.

    Hay sai: tạo riêng index cho từng cột rồi nghĩ rằng chúng sẽ kết hợp hiệu quả như một composite index.

    Ôn thêm câu hỏi SQL & Database →
  3. 03Các isolation level là gì? Dirty read, non-repeatable read, phantom read khác nhau thế nào?SQL & Database

    Gợi ý trả lời

    Isolation level quyết định một transaction thấy được bao nhiêu thay đổi của transaction khác; mức càng cao càng an toàn nhưng càng dễ tranh chấp.

    • Dirty read: đọc dữ liệu transaction khác chưa commit.
    • Non-repeatable read: đọc lại cùng một dòng thấy giá trị đã đổi.
    • Phantom read: chạy lại cùng điều kiện thấy thêm hoặc mất dòng.

    Bốn mức chuẩn: Read Uncommitted, Read Committed, Repeatable Read, Serializable. Mặc định của PostgreSQL, SQL Server, Oracle là Read Committed; MySQL InnoDB là Repeatable Read.

    Thực tế các DB dùng MVCC nên hành vi có khác lý thuyết: Repeatable Read của PostgreSQL dùng snapshot nên không gặp phantom, còn InnoDB dùng gap lock cho câu đọc có khoá.

    Người phỏng vấn muốn nghe: bạn biết DB mình dùng mặc định ở mức nào và đã từng xử lý lost update, vd. bằng SELECT ... FOR UPDATE hoặc optimistic locking.

    Ôn thêm câu hỏi SQL & Database →
  4. 04Dependency Injection trong Spring là gì? Vì sao nên dùng constructor injection thay cho @Autowired trên field?Java

    Gợi ý trả lời

    IoC container của Spring tạo và quản lý các bean, rồi "tiêm" phụ thuộc vào class cần dùng, thay vì class tự new. Nhờ vậy code ít phụ thuộc cứng, dễ thay implementation và dễ test.

    Constructor injection được khuyên dùng vì:

    • Field có thể là final, object luôn ở trạng thái đầy đủ sau khi tạo.
    • Test unit không cần Spring: chỉ cần new OrderService(mockRepo).
    • Constructor quá nhiều tham số là dấu hiệu class đang ôm nhiều việc.
    • Class chỉ có một constructor thì không cần ghi @Autowired.

    Nên nói thêm về scope: mặc định bean là singleton (một instance cho cả context), ngoài ra có prototype, request, session.

    Hay sai: để state thay đổi được (ví dụ biến đếm, list) trong bean singleton. Nhiều request chạy song song sẽ dùng chung và gây race condition.

    Ôn thêm câu hỏi Java →
  5. 05@Transactional hoạt động thế nào? Vì sao có lúc gắn annotation rồi mà transaction không chạy?Java

    Gợi ý trả lời

    Spring tạo proxy bao quanh bean. Khi gọi method có @Transactional qua proxy, proxy mở transaction, gọi method thật, rồi commit hoặc rollback.

    Những lý do hay gặp khiến nó "không ăn":

    • Self-invocation: method A trong cùng class gọi this.b() có @Transactional, lời gọi không đi qua proxy.
    • Method là private hoặc final: proxy không chặn được lời gọi.
    • Mặc định chỉ rollback với unchecked exception và Error. Checked exception vẫn commit, trừ khi khai báo rollbackFor = Exception.class.
    • Exception bị catch và nuốt bên trong method.

    Nên biết thêm propagation (REQUIRED, REQUIRES_NEW) và readOnly = true cho các truy vấn chỉ đọc.

    Người phỏng vấn muốn nghe: bạn giải thích được bằng cơ chế proxy, không chỉ liệt kê lỗi.

    Ôn thêm câu hỏi Java →
  6. 06Swift concurrency: async/await, Task, actor và @MainActor hoạt động thế nào?Mobile (Android, iOS, Flutter)

    Gợi ý trả lời

    Swift concurrency đưa bất đồng bộ vào ngôn ngữ, cùng với kiểm tra data race lúc biên dịch (chặt hơn từ Swift 6).

    • async/await: viết code bất đồng bộ tuần tự, thay cho completion handler lồng nhau.
    • Task { } tạo tác vụ mới; async let và TaskGroup chạy song song có cấu trúc. Task hỗ trợ huỷ hợp tác qua Task.checkCancellation().
    • actor: kiểu tham chiếu mà trạng thái bên trong chỉ được truy cập tuần tự, tránh data race. Gọi từ ngoài phải await.
    • @MainActor: đảm bảo code chạy trên main thread, dùng cho ViewModel và UI.
    • Sendable: đánh dấu kiểu an toàn khi chuyển giữa các vùng concurrency.
    @MainActor final class ProfileViewModel: ObservableObject {
        @Published var user: User?
        func load() async { user = try? await api.fetchUser() }
    }
    

    Hay sai: actor reentrancy: sau mỗi await, trạng thái của actor có thể đã bị thay đổi.

    Ôn thêm câu hỏi Mobile (Android, iOS, Flutter) →
  7. 07Một API chạy chậm, nghi do query. Bạn điều tra và tối ưu thế nào?SQL & Database

    Gợi ý trả lời

    Đo trước rồi mới sửa: tìm đúng query chậm, đọc execution plan, rồi chỉnh index hoặc cách viết query.

    1. Bật slow query log (MySQL) hoặc pg_stat_statements (PostgreSQL) để biết query nào tốn thời gian nhất.
    2. Chạy EXPLAIN (PostgreSQL nên dùng EXPLAIN (ANALYZE, BUFFERS)) xem có full scan, ước lượng số dòng lệch nhiều so với thực tế, sort trên đĩa không.
    3. Sửa theo nguyên nhân: thêm hoặc đổi composite index, bỏ hàm bọc cột, chỉ SELECT cột cần, phân trang bằng keyset thay cho OFFSET lớn.
    4. Kiểm tra phía ứng dụng: lỗi N+1 query, thiếu connection pool, lock chờ lâu.
    5. Cập nhật thống kê (ANALYZE) nếu planner ước lượng sai.

    Người phỏng vấn muốn nghe: quy trình có số liệu trước/sau, không phải đoán rồi thêm index bừa.

    Ôn thêm câu hỏi SQL & Database →
  8. 08Chuẩn hoá (normalization) là gì? Khi nào nên denormalize?SQL & Database

    Gợi ý trả lời

    Chuẩn hoá là tách dữ liệu để mỗi thông tin chỉ lưu ở một chỗ, tránh dư thừa và sai lệch khi cập nhật.

    • 1NF: mỗi ô một giá trị nguyên tử, không lưu danh sách trong một cột.
    • 2NF: cột không khoá phụ thuộc vào toàn bộ khoá chính, không chỉ một phần.
    • 3NF: cột không khoá không phụ thuộc bắc cầu qua cột không khoá khác.

    Denormalize khi đọc nhiều hơn ghi rất nhiều và JOIN trở thành điểm nghẽn: lưu sẵn comment_count trong bảng bài viết, chép customer_name vào đơn hàng để giữ đúng tên tại thời điểm đặt, hoặc dựng bảng báo cáo riêng.

    Hay sai: denormalize mà không có cơ chế đồng bộ (trigger, job, event) nên số liệu lệch dần. Hãy nói rõ cái giá phải trả là logic ghi phức tạp hơn.

    Ôn thêm câu hỏi SQL & Database →
  9. 09Deadlock trong database xảy ra thế nào và xử lý ra sao?SQL & Database · Senior

    Gợi ý trả lời

    Deadlock là khi hai transaction giữ khoá mà bên kia cần và chờ nhau mãi. DB tự phát hiện, chọn một transaction làm nạn nhân và rollback nó.

    Ví dụ: T1 khoá dòng A rồi muốn B, cùng lúc T2 khoá B rồi muốn A.

    Cách giảm deadlock:

    • Luôn khoá tài nguyên theo cùng một thứ tự (vd. sắp xếp theo id trước khi update nhiều dòng).
    • Giữ transaction ngắn, không chờ I/O bên ngoài khi đang giữ khoá.
    • Có index phù hợp để câu UPDATE/DELETE không khoá quá nhiều dòng.
    • Ứng dụng bắt lỗi deadlock và retry có giới hạn.

    Khi điều tra: MySQL dùng SHOW ENGINE INNODB STATUS, PostgreSQL xem log (deadlock được ghi kèm câu lệnh) và pg_locks.

    Người phỏng vấn muốn nghe: bạn coi retry là bình thường nhưng vẫn tìm gốc rễ ở thứ tự khoá.

    Ôn thêm câu hỏi SQL & Database →
  10. 10Bạn thấy mình ở đâu sau 2–3 năm nữa?Câu hỏi hành vi & văn hoá

    Gợi ý trả lời

    Người phỏng vấn muốn biết mục tiêu của bạn có khớp với hướng phát triển ở công ty và bạn có định gắn bó không.

    • Nêu hướng phát triển cụ thể, thực tế: đi sâu chuyên môn (senior, chuyên gia một mảng) hay quản lý (lead, PM).
    • Kèm kế hoạch: kỹ năng cần học, trách nhiệm muốn nhận thêm.
    • Gắn với cơ hội ở vị trí đang ứng tuyển.

    Câu mẫu: "Trong 2–3 năm tới em muốn trở thành senior có thể tự thiết kế và chịu trách nhiệm một module lớn, đồng thời hướng dẫn được các bạn mới. Em đang học thêm về thiết kế hệ thống và thấy team bên mình có nhiều bài toán ở quy mô lớn, rất hợp để em phát triển theo hướng đó."

    Hay sai: "Em muốn mở công ty riêng" hoặc "em chưa nghĩ tới". Cũng đừng nói muốn ngồi vào chỗ của người đang phỏng vấn.

    Ôn thêm câu hỏi Câu hỏi hành vi & văn hoá →

Bạn đã phỏng vấn ở MONEY FORWARD VIETNAM CO.,LTD?

Kể lại các vòng và câu hỏi đã gặp (ẩn danh) để người sau ôn đúng trọng tâm.

Chia sẻ kinh nghiệm phỏng vấn