Thư Viện Câu Hỏi Phỏng Vấn
Tổng hợp các câu hỏi tuyển dụng thực tế theo nhiều cấp độ từ Entry đến Expert để bạn tự tin chinh phục nhà tuyển dụng.
Làm thế nào để xử lý các tác vụ nặng (như gửi email hàng loạt, xử lý video) bất đồng bộ trong Laravel?
Sử dụng hệ thống Queues & Job Workers của Laravel:
- Cấu hình Queue Connection: Thiết lập driver (như Redis, Database, SQS) trong file
.env(khuyên dùngredischo môi trường production). - Tạo Job Class: Sử dụng lệnh
php artisan make:job ProcessPodcast. Trong phương thứchandle(), viết logic xử lý nặng. - Dispatch Job: Gọi
ProcessPodcast::dispatch($data)từ Controller để đẩy tác vụ vào hàng đợi và phản hồi ngay lập tức cho client. - Chạy Worker: Chạy lệnh
php artisan queue:worktrên server (thường quản lý bởi Supervisor) để liên tục lấy các jobs ra khỏi hàng đợi và xử lý bất đồng bộ.
Cơ chế Route Caching và Config Caching trong Laravel hoạt động như thế nào và tại sao cần lưu ý khi deploy?
route:cache): Biên dịch tất cả các khai báo route trong file routes/*.php thành một mảng phẳng duy nhất được lưu trong bộ nhớ đệm của framework, giúp tăng tốc độ điều hướng đáng kể.config:cache): Gộp toàn bộ các file cấu hình trong thư mục config/*.php thành một file duy nhất.env() sẽ luôn trả về null bên ngoài các file config. Do đó, Best Practice là chỉ gọi env() trong các file ở thư mục config, còn trong Controller/Service bắt buộc phải gọi thông qua config('key').React Server Components (RSC) là gì và khác gì so với Server-Side Rendering (SSR) truyền thống?
State Management: So sánh Redux Toolkit và Zustand? Khi nào nên chọn cái nào?
Sự khác biệt giữa Worker Threads và Cluster Module trong Node.js? Khi nào dùng cái nào?
Cơ chế hoạt động của Stream API trong Node.js? Tại sao nó giúp tối ưu hóa bộ nhớ RAM?
Stream là cơ chế đọc/ghi dữ liệu theo từng phần nhỏ (chunks) liên tục, thay vì tải toàn bộ file vào RAM một lần duy nhất.
- Ví dụ: Khi đọc một file dung lượng 4GB trên server có RAM 2GB, nếu đọc bình thường bằng
fs.readFile, server sẽ bị crash do tràn RAM (Out of Memory). Nhưng nếu dùngfs.createReadStream, dữ liệu được chia nhỏ thành các buffer chunk khoảng vài chục KB và truyền đi liên tục (pipe) sang writable stream, giúp lượng RAM tiêu thụ luôn ở mức rất thấp và ổn định.
Làm thế nào để phát hiện và xử lý hiện tượng rò rỉ bộ nhớ (Memory Leak) trong ứng dụng Node.js chạy production?
v8 hoặc cắm --inspect để dùng Chrome DevTools so sánh sự khác biệt (đặc biệt tìm các đối tượng tăng đột biến không bị giải phóng).removeListener.Spring Boot Starter là gì và cơ chế Auto-Configuration hoạt động như thế nào?
spring-boot-starter-web bao gồm cả Spring MVC, Tomcat, Jackson...).@EnableAutoConfiguration (nằm trong @SpringBootApplication). Lúc khởi động, Spring Boot sẽ quét classpath, nếu phát hiện sự hiện diện của một thư viện nhất định (ví dụ H2 Database), nó sẽ tự động cấu hình các Beans mặc định cho thư viện đó mà không cần viết cấu hình thủ công, trừ khi lập trình viên tự ghi đè cấu hình.Làm thế nào để xử lý các giao dịch (Transactions) đồng thời trong Spring Boot bằng @Transactional?
Chú thích @Transactional của Spring được dùng để tự động hóa việc quản lý transaction (commit nếu thành công, rollback nếu có runtime exception):
- Cơ chế: Spring sử dụng AOP (Aspect-Oriented Programming) tạo ra một proxy bao quanh class/method đó để mở và đóng kết nối transaction.
- Lưu ý quan trọng: Lời gọi tự thân (Self-invocation - gọi phương thức
@Transactionaltừ một phương thức khác bên trong cùng một class) sẽ không kích hoạt transaction vì proxy bị bỏ qua. Để khắc phục, cần tách phương thức đó sang một bean khác hoặc inject chính bean đó thông qua Spring Context.
Làm thế nào để phát hiện chu trình (loop) trong một Danh sách liên kết đơn (Singly Linked List)?
Sử dụng thuật toán Floyd\'s Cycle-Finding Algorithm (còn gọi là thuật toán rùa và thỏ - Tortoise and Hare):
- Khởi tạo hai con trỏ:
slow(rùa) vàfast(thỏ) cùng trỏ vào node đầu tiên (head). - Duyệt qua danh sách liên kết: Mỗi bước, di chuyển con trỏ
slowđi 1 bước (slow = slow.next) và con trỏfastđi 2 bước (fast = fast.next.next). - Nếu không có chu trình, con trỏ
fastsẽ đi đến cuối danh sách (gặp null) và thuật toán kết thúc với kết quả false. - Nếu có chu trình, con trỏ
fastchắc chắn sẽ đuổi kịp và gặp lại con trỏslowtại một node nào đó (slow == fast). Lúc này thuật toán trả về true với độ phức tạp thời gian cực kỳ tối ưu là O(n) và độ phức tạp không gian là O(1).
Thuật toán Dijkstra tìm đường đi ngắn nhất hoạt động như thế nào? Điều kiện áp dụng là gì?
Thuật toán Dijkstra dùng để tìm đường đi ngắn nhất từ một đỉnh nguồn đến tất cả các đỉnh khác trên đồ thị có trọng số:
- Cơ chế: Sử dụng một hàng đợi ưu tiên (Priority Queue / Min-Heap) để liên tục chọn đỉnh có khoảng cách ngắn nhất chưa được tối ưu từ đỉnh nguồn, sau đó cập nhật khoảng cách tới các đỉnh kề với nó (quá trình Relax).
- Độ phức tạp: Sử dụng Min-Heap giúp thuật toán đạt hiệu năng O((V + E) log V) với V là số đỉnh và E là số cạnh.
- Điều kiện: Trọng số của tất cả các cạnh phải không âm (non-negative). Nếu đồ thị có cạnh âm, ta bắt buộc phải sử dụng thuật toán Bellman-Ford để tránh rơi vào vòng lặp vô hạn.
Làm thế nào để phát hiện chu trình (cycle) trong đồ thị có hướng (Directed Graph)?
Có thể sử dụng thuật toán duyệt đồ thị DFS (Depth-First Search) kết hợp kỹ thuật tô màu các đỉnh (hoặc sử dụng Recursion Stack):
- Trạng thái 3 màu:
- Trắng (Unvisited): Đỉnh chưa được duyệt.
- Xám (Visiting): Đỉnh đang nằm trong nhánh đệ quy hiện tại (chưa duyệt xong các đỉnh kề).
- Đen (Visited): Đỉnh và toàn bộ các đỉnh kề của nó đã duyệt xong.
- Phát hiện chu trình: Trong quá trình duyệt DFS, nếu gặp một đỉnh kề đang ở trạng thái Xám, điều đó có nghĩa là có một cạnh ngược (back edge) trỏ về tổ tiên của nó trên nhánh đệ quy, tức là đồ thị tồn tại chu trình.
Hãy giải thích cơ chế của kỹ thuật Sliding Window (Cửa sổ trượt) trên mảng dữ liệu?
Sliding Window được dùng để tìm kiếm một phân đoạn con (subarray) thỏa mãn điều kiện nhất định trên mảng mà không cần tính toán lại từ đầu các phần tử trùng lặp:
- Cơ chế: Duy trì một \'cửa sổ\' được định nghĩa bởi hai chỉ số
startvàend. Khi mở rộng cửa sổ sang phải (end++), ta cộng thêm phần tử mới vào kết quả tạm thời. Khi điều kiện bị vi phạm, ta thu hẹp cửa sổ từ bên trái (start++) và trừ bớt giá trị của phần tử bị loại bỏ. - Hiệu năng: Giúp chuyển đổi các thuật toán vét cạn độ phức tạp O(n^2) thành thuật toán tuyến tính O(n) vì mỗi phần tử chỉ được duyệt qua tối đa 2 lần.
Hãy phân biệt sự khác nhau giữa tiếp cận Bottom-Up (Tabulation) và Top-Down (Memoization) trong Quy hoạch động?
Bài toán cái túi (0/1 Knapsack Problem) được giải quyết bằng Quy hoạch động như thế nào?
Bài toán yêu cầu chọn các đồ vật có khối lượng và giá trị sao cho tổng khối lượng không vượt quá W và tổng giá trị lớn nhất:
- Trạng thái: Gọi
dp[i][w]là giá trị lớn nhất có thể đạt được khi chọn trongiđồ vật đầu tiên với giới hạn khối lượng làw. - Công thức truy hồi: Với đồ vật thứ
icó khối lượngweight[i]và giá trịval[i]:- Nếu
weight[i] > w: Không thể chọn đồ vật này ->dp[i][w] = dp[i-1][w]. - Nếu
weight[i] <= w: Ta chọn giá trị lớn hơn giữa việc lấy hoặc không lấy đồ vật ->dp[i][w] = max(dp[i-1][w], dp[i-1][w - weight[i]] + val[i]).
- Nếu
- Kết quả: Nằm ở ô
dp[n][W]với độ phức tạp thời gian là O(n*W).




