Việc làm

05/10 · Hà Nội · Tại văn phòng

Technical Principal/Solution Architect

One Mount Group · 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 (Java, Cloud (AWS, Azure, GCP), System Design & Kiến trúc). 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. 01Dependency 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 →
  2. 02@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 →
  3. 03Lỗi N+1 query trong JPA/Hibernate là gì và xử lý ra sao?Java

    Gợi ý trả lời

    N+1 xảy ra khi bạn lấy N bản ghi bằng 1 query, rồi mỗi lần truy cập quan hệ lazy lại phát sinh thêm 1 query. Ví dụ lấy 100 đơn hàng, rồi gọi order.getCustomer() trong vòng lặp là thành 101 query.

    Cách xử lý:

    • JOIN FETCH trong JPQL: select o from Order o join fetch o.customer.
    • @EntityGraph(attributePaths = "customer") trên method của repository.
    • @BatchSize hoặc hibernate.default_batch_fetch_size để gom lazy load thành câu IN (...).
    • Với màn hình chỉ đọc, trả thẳng DTO projection thay vì entity.

    Phát hiện bằng cách bật log SQL (spring.jpa.show-sql hoặc log của Hibernate) khi dev, hoặc dùng thống kê của Hibernate.

    Hay sai: đổi sang FetchType.EAGER cho xong. Cách này chỉ đổi chỗ lỗi và làm mọi truy vấn khác nặng hơn. Fetch join nhiều collection cùng lúc cũng dễ gây tích Descartes.

    Ôn thêm câu hỏi Java →
  4. 04Khi nào bạn chọn serverless (Lambda, Azure Functions, Cloud Run) thay vì container hay VM?Cloud (AWS, Azure, GCP)

    Gợi ý trả lời

    Serverless hợp với tải không đều, xử lý theo sự kiện, đội nhỏ không muốn vận hành server; trả tiền theo lượng dùng.

    Hợp:

    • Xử lý file khi upload, webhook, cron job, API lưu lượng thấp hoặc thất thường.

    Cân nhắc:

    • Cold start làm tăng độ trễ request đầu.
    • Giới hạn thời gian chạy, bộ nhớ, kích thước gói.
    • Tải cao và đều thì container/VM thường rẻ hơn.
    • Kết nối DB: nhiều instance mở nhiều connection, cần connection pooler hoặc proxy.
    • Khó debug và test cục bộ hơn.

    Người phỏng vấn muốn nghe: quyết định dựa trên đặc điểm tải và chi phí, không chọn vì "đang hot".

    Ôn thêm câu hỏi Cloud (AWS, Azure, GCP) →
  5. 05Load balancer tầng 4 và tầng 7 khác nhau thế nào? Khi nào dùng loại nào?Cloud (AWS, Azure, GCP)

    Gợi ý trả lời

    Load balancer tầng 4 phân phối theo IP/port (TCP/UDP), không đọc nội dung; tầng 7 hiểu HTTP nên định tuyến được theo path, host, header.

    • L7 (ALB, Application Gateway, HTTP(S) LB): định tuyến /api và /web tới các nhóm khác nhau, kết thúc TLS, gắn WAF, hợp web/API.
    • L4 (NLB, Azure Load Balancer): độ trễ thấp, thông lượng cao, giao thức không phải HTTP (game, MQTT, DB proxy), cần IP tĩnh.
    • Cả hai đều có health check để loại máy hỏng.

    Hay sai: health check trỏ vào trang nặng hoặc phụ thuộc DB, khiến LB loại hết máy khi DB chậm.

    Ôn thêm câu hỏi Cloud (AWS, Azure, GCP) →
  6. 06Thiết kế rate limiter cho API. Bạn chọn thuật toán nào và đặt ở đâu?System Design & Kiến trúc

    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 (INCR kèm EXPIRE, hoặc Lua script).

    Trả về HTTP 429 kèm header Retry-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.

    Ôn thêm câu hỏi System Design & Kiến trúc →
  7. 07Bộ nhớ JVM gồm những vùng nào? Garbage Collector hoạt động ra sao và Java có bị memory leak không?Java

    Gợi ý trả lời

    Hai vùng chính: stack (mỗi thread một stack, chứa biến cục bộ và tham chiếu) và heap (chứa object, dùng chung). Ngoài ra có Metaspace chứa thông tin class.

    • GC tìm các object không còn được tham chiếu từ "GC roots" (biến trên stack, field static...) và thu hồi.
    • Heap chia theo thế hệ: object mới nằm ở young generation, sống lâu thì được chuyển lên old generation.
    • G1 là GC mặc định; ZGC hợp khi cần pause rất ngắn với heap lớn.

    Java vẫn leak khi object không dùng nữa mà vẫn còn tham chiếu:

    • Cache hoặc static Map chỉ thêm, không bao giờ xoá.
    • ThreadLocal không remove() trong thread pool.
    • Listener đăng ký mà không huỷ.

    Người phỏng vấn muốn nghe: phân biệt được OutOfMemoryError: Java heap space với lỗi Metaspace, và biết dùng heap dump để tìm ai đang giữ object.

    Ôn thêm câu hỏi Java →
  8. 08Virtual threads trong Java 21 là gì? Khi nào nên dùng và cần lưu ý gì?Java · Senior

    Gợi ý trả lời

    Virtual thread là thread nhẹ do JVM quản lý, được gắn lên một số ít platform thread (carrier). Khi gặp I/O blocking, nó nhả carrier để chạy việc khác, nên bạn có thể có hàng trăm nghìn thread mà vẫn viết code blocking dễ đọc.

    try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
        ids.forEach(id -> executor.submit(() -> callApi(id)));
    }
    
    • Hợp với tải nhiều I/O (gọi DB, HTTP). Không giúp gì với tác vụ nặng CPU.
    • Spring Boot 3.2+ bật bằng spring.threads.virtual.enabled=true.
    • Không đưa virtual thread vào pool: tạo mới cho mỗi task là cách dùng đúng.
    • Giới hạn tài nguyên (connection pool DB) vẫn là giới hạn thật; dùng Semaphore nếu cần.
    • Trên Java 21, blocking bên trong synchronized có thể "pin" carrier; các bản JDK mới hơn đã cải thiện. Cẩn thận với ThreadLocal chứa object lớn.

    Người phỏng vấn muốn nghe: virtual threads tăng throughput, không làm từng request nhanh hơn.

    Ôn thêm câu hỏi Java →
  9. 09So sánh synchronized, volatile, ReentrantLock và ConcurrentHashMap. Bạn tránh race condition thế nào?Java · Senior

    Gợi ý trả lời

    Race condition xảy ra khi nhiều thread đọc-sửa-ghi cùng dữ liệu mà không đồng bộ. Mỗi công cụ giải quyết một phần:

    • volatile: đảm bảo visibility (thread khác thấy giá trị mới nhất), nhưng không làm count++ thành atomic.
    • synchronized: vừa loại trừ lẫn nhau vừa đảm bảo visibility (quan hệ happens-before).
    • ReentrantLock: như synchronized nhưng có tryLock với timeout, lock có thể ngắt, nhiều Condition.
    • AtomicInteger/LongAdder: đếm không cần lock.
    • ConcurrentHashMap: thread-safe với độ tranh chấp thấp. Dùng compute/merge/putIfAbsent cho thao tác kép, đừng get rồi put.

    Hay sai: dùng Collections.synchronizedMap rồi duyệt map mà không lock, hoặc kiểm tra if (!map.containsKey(k)) map.put(k, v) trên map đồng thời: hai bước riêng lẻ vẫn bị chen ngang. Cách tốt nhất vẫn là giảm state dùng chung (object bất biến, mỗi thread một bản).

    Ôn thêm câu hỏi Java →
  10. 10Kể về một thất bại hoặc sai lầm của bạn và bài học rút ra.Câu hỏi hành vi & văn hoá

    Gợi ý trả lời

    Chọn sai lầm thật, có hậu quả vừa phải, nhận trách nhiệm rõ ràng, và nhấn mạnh việc bạn đã thay đổi sau đó.

    Cấu trúc: chuyện gì xảy ra, phần lỗi của bạn, bạn khắc phục ngay thế nào, bài học và thay đổi lâu dài.

    Câu mẫu: "Có lần em deploy một thay đổi cấu hình lên production vào chiều thứ Sáu mà không kiểm tra kỹ trên staging, làm một tính năng lỗi khoảng một giờ. Em báo ngay cho lead, rollback và viết báo cáo sự cố. Sau đó em đề xuất checklist trước deploy và quy ước hạn chế deploy cuối tuần; team áp dụng tới giờ."

    Hay sai: kể một "thất bại" thực ra là khoe, đổ lỗi cho người khác, hoặc nói mình chưa từng thất bại.

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

Bạn đã phỏng vấn ở One Mount Group?

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