Khi phỏng vấn vị trí Python Backend từ Mid đến Senior, nhà tuyển dụng hiếm khi chỉ dừng lại ở cú pháp cơ bản. Họ thường xoáy sâu vào cơ chế vận hành bên dưới (internals) như GIL, mô hình bất đồng bộ, cách quản lý bộ nhớ hay hiệu năng cơ sở dữ liệu để đánh giá tư duy kỹ thuật thực tế của bạn. Bài viết này tổng hợp và phân tích 5 chủ đề nền tảng mấu chốt giúp bạn tự tin ghi điểm tuyệt đối trong vòng phỏng vấn kỹ thuật.
1. Concurrency Model: GIL, Threading, Multiprocessing và Asyncio
Một trong những câu hỏi kinh điển nhất là: 'Python có thực sự chạy đa luồng (multi-threading) được không?'. Để trả lời thuyết phục, bạn cần phân tích rõ bản chất của Global Interpreter Lock (GIL) trong CPython: đây là cơ chế mutex bảo vệ bộ nhớ của Python bằng cách chỉ cho phép một native thread thực thi Python bytecode tại một thời điểm.
Điều này đồng nghĩa với việc threading trong Python không mang lại tính song song thực sự (true parallelism) cho các tác vụ tính toán nặng (CPU-bound). Tuy nhiên, với các tác vụ chờ mạng hoặc ổ đĩa (I/O-bound), GIL sẽ được giải phóng trong lúc chờ I/O, giúp threading hoặc asyncio phát huy hiệu quả cao.
Ứng viên xuất sắc cần chỉ ra được ranh giới rõ ràng khi lựa chọn giải pháp kỹ thuật cho từng bài toán cụ thể.
- CPU-bound (xử lý ảnh, mã hóa dữ liệu, machine learning): Sử dụng multiprocessing hoặc ProcessPoolExecutor để tạo nhiều process riêng biệt với Python interpreter và vùng nhớ độc lập, tận dụng tối đa đa nhân CPU.
- I/O-bound quy mô lớn (microservices, REST API gateway, web scraper): Sử dụng asyncio với mô hình cooperative multitasking dựa trên Event Loop. Cơ chế non-blocking giúp một thread duy nhất xử lý hàng chục nghìn kết nối đồng thời mà không tốn overhead context switch của OS thread.
- I/O-bound truyền thống hoặc tích hợp thư viện legacy: Sử dụng threading hoặc ThreadPoolExecutor khi thư viện không hỗ trợ async/await.
2. Quản Lý Bộ Nhớ: Reference Counting, Garbage Collection và Memory Leaks
Người phỏng vấn thường hỏi cách Python thu hồi bộ nhớ để kiểm tra xem bạn có hiểu sâu về runtime lifecycle hay không. Cơ chế dọn rác chính của CPython là Reference Counting: mỗi object có một biến đếm số lượng tham chiếu trỏ tới nó. Khi biến đếm giảm về 0, bộ nhớ được giải phóng ngay lập tức.
Tuy nhiên, Reference Counting bất lực trước vấn đề tham chiếu vòng (circular reference), ví dụ object A trỏ tới B và B lại trỏ ngược lại A. Để giải quyết, Python trang bị thêm Generational Garbage Collector (chia các object thành 3 thế hệ: Gen 0, 1, 2) hoạt động định kỳ để phát hiện và dọn dẹp các chu trình khép kín này.
Trong môi trường backend production, việc không kiểm soát tốt vòng đời object sẽ dẫn đến rò rỉ bộ nhớ (memory leaks) làm sập worker processes.
- Nguyên nhân memory leak phổ biến: Lưu trữ state trong global variables, đính kèm dữ liệu vào class attributes tồn tại suốt vòng đời process, hoặc sử dụng mutable default arguments trong function.
- Tối ưu bộ nhớ với __slots__: Khi định nghĩa các class có hàng triệu instances (như data point, DTO), sử dụng __slots__ để loại bỏ __dict__ nội tại của mỗi object, giúp giảm 40-60% dung lượng RAM tiêu thụ.
- Cách debug thực tế: Sử dụng các công cụ như tracemalloc, objgraph hoặc memory_profiler để theo dõi biến động heap allocation trong quá trình chạy stress test.
3. Generators, Iterators và Context Managers: Viết Code Tối Ưu Tài Nguyên
Nhà tuyển dụng rất thích đưa ra bài toán: 'Làm thế nào để xử lý file log 50GB trên một server chỉ có 2GB RAM?'. Đây là lúc kiến thức về Generator và Streaming Data phát huy tác dụng.
Thay vì load toàn bộ dữ liệu vào memory bằng List comprehension (tốn O(N) memory), Generator sử dụng từ khóa yield để thực hiện cơ chế Lazy Evaluation (chỉ sinh dữ liệu tại thời điểm được yêu cầu, tiêu tốn O(1) memory). Hiểu sâu giao thức Iterator Protocol (__iter__ và __next__) cho phép bạn xây dựng các data pipeline xử lý dữ liệu liên tục một cách mượt mà.
Bên cạnh đó, Context Manager (__enter__ và __exit__) là nền tảng quản lý an toàn tài nguyên ngoại vi (database connections, file descriptors, distributed locks), đảm bảo tài nguyên luôn được giải phóng ngay cả khi xảy ra unhandled exceptions.
- Generator Pipelines: Kết nối nhiều generators lại với nhau để tạo thành chuỗi xử lý (read -> parse -> filter -> insert DB) mà không cần lưu trữ intermediate state trên RAM.
- Custom Context Manager: Có thể hiện thực bằng class với cặp phương thức __enter__ / __exit__ hoặc ngắn gọn hơn thông qua decorator @contextlib.contextmanager.
- Ứng dụng thực tế: Quản lý session của SQLAlchemy, acquire/release Redis lock để tránh deadlock khi triển khai cronjob hoặc task Celery.
4. WSGI vs ASGI và Kiến Trúc Web Server Trong Production
Rất nhiều lập trình viên chỉ biết deploy ứng dụng với Gunicorn hoặc Uvicorn theo template có sẵn mà không hiểu luồng đi của request. Nhà tuyển dụng sẽ đào sâu: Sự khác biệt giữa WSGI và ASGI là gì? Mô hình pre-fork worker hoạt động ra sao?
WSGI (Web Server Gateway Interface) là chuẩn đồng bộ truyền thống dành cho Flask, Django cũ. Mỗi worker process/thread xử lý trọn vẹn một request từ đầu đến cuối; nếu request đó phải đợi bên thứ 3 mất 3 giây thì worker đó hoàn toàn bị block trong 3 giây.
ASGI (Asynchronous Server Gateway Interface) ra đời nhằm hỗ trợ async/await natively cho các framework như FastAPI, Starlette hay Django Channels. ASGI cho phép một worker duy nhất xử lý hàng nghìn kết nối đồng thời, hỗ trợ hoàn hảo cho WebSockets, HTTP/2 và Server-Sent Events (SSE).
- Mô hình Pre-fork Worker (Gunicorn): Master process fork ra N worker processes con độc lập. Nếu một worker bị crash do segmentation fault hoặc OOM (Out Of Memory), master process sẽ tự động khởi tạo worker mới thay thế.
- Mô hình phối hợp chuẩn Production: Đặt Nginx làm Reverse Proxy ở phía trước (xử lý SSL termination, static files, rate limiting, buffering request), chuyển tiếp request vào Gunicorn quản lý Uvicorn Workers (gunicorn -k uvicorn.workers.UvicornWorker) để vừa tận dụng đa nhân CPU vừa xử lý async non-blocking.
- Worker sizing rule: Công thức kinh nghiệm phổ biến cho WSGI worker là (2 x CPU cores) + 1; còn với ASGI, số lượng worker thường chỉ cần bằng số CPU cores vì mỗi worker đã có riêng một Event Loop xử lý concurrency cực tốt.
5. Database Optimization: Connection Pooling, N+1 Query và Transaction Isolation
Phần lớn bottleneck của hệ thống Backend nằm ở Database layer, do đó các câu hỏi liên quan đến ORM, pooling và lock luôn là trọng tâm của vòng phỏng vấn chuyên sâu.
Vấn đề kinh điển nhất là N+1 Query Problem khi dùng ORM (Django ORM, SQLAlchemy). Khi load danh sách N bản ghi cha và truy cập thuộc tính của bảng con trong vòng lặp, ORM sẽ bắn thêm N câu query riêng lẻ xuống database thay vì chỉ cần 1 câu JOIN hoặc IN query, làm tê liệt DB khi lượng dữ liệu lớn.
Ngoài ra, việc hiểu rõ cơ chế Database Connection Pool và Transaction Isolation Levels giúp bạn xử lý các tình huống race condition nghiêm trọng như overselling hàng hóa hay double spending trong ví điện tử.
- Giải quyết N+1 Query: Sử dụng select_related (INNER/LEFT JOIN cho quan hệ 1-1, 1-n) hoặc prefetch_related (query thứ 2 dùng IN clause cho quan hệ n-n, 1-n) trong Django ORM; sử dụng joinedload / selectinload trong SQLAlchemy.
- Connection Pool Exhaustion: Tránh mở kết nối DB trực tiếp ở mỗi request. Thiết lập kích thước Pool Size và Max Overflow hợp lý, đồng thời giải phóng kết nối về pool ngay khi câu lệnh kết thúc.
- Xử lý Concurrency & Race Condition: Áp dụng Pessimistic Locking (SELECT ... FOR UPDATE) cho các nghiệp vụ tiền tệ nhạy cảm, hoặc Optimistic Locking (dùng version column) cho các bài toán high throughput ít xảy ra xung đột ghi.
Ghi nhớ nhanh
- Nắm vững bản chất GIL, Asyncio và Memory Management để tự tin phân tích trade-off về hiệu năng và tài nguyên khi thiết kế hệ thống.
- Luôn tư duy theo hướng tối ưu tài nguyên I/O: dùng Generators để xử lý dữ liệu lớn, áp dụng Eager Loading để triệt tiêu vấn đề N+1 queries.
- Hiểu rõ kiến trúc WSGI/ASGI và mô hình Process/Thread giúp bạn cấu hình web server (Gunicorn, Uvicorn, Nginx) chuẩn xác và debug hiệu quả trong môi trường production chịu tải cao.