Blog

  • MySQL hoạt động ở tầng hiệu năng

    Nắm chắc cách MySQL hoạt động ở tầng hiệu năng, không chỉ biết CRUD hay query thông thường.

    1. Performance Tuning (Tối ưu hiệu năng tổng thể)

    Bạn cần hiểu cách MySQL tiêu tốn tài nguyên và cách cấu hình để tối ưu.

    • Server-level tuning:
      • innodb_buffer_pool_sizequery_cache_sizetmp_table_sizemax_connectionsinnodb_flush_log_at_trx_commit — hiểu công dụng, khi nào chỉnh.
      • Xác định bottleneck qua SHOW STATUSSHOW ENGINE INNODB STATUS.
    • Schema-level tuning:
      • Chọn kiểu dữ liệu phù hợp (INT vs BIGINTCHAR vs VARCHAR).
      • Thiết kế quan hệ và ràng buộc hợp lý để tránh join nặng.
    • Connection & pool tuning khi backend dùng ORM (Laravel Eloquent):
      Tránh N+1 query, bật query caching hợp lý, và batch insert/update.

    2. Indexing (Tối ưu chỉ mục)

    Bạn cần hiểu sâu cách MySQL dùng chỉ mục.

    • Loại index: B-Tree, Hash (trên MEMORY engine), Fulltext, Composite.
    • Khi nào index hữu ích:
      • WHERE, JOIN, ORDER BY, GROUP BY – nhưng phải đúng thứ tự cột.
    • Composite index: quy tắc “leftmost prefix”.
      • Ví dụ: index (user_id, created_at) có hiệu lực cho WHERE user_id = ? và WHERE user_id = ? AND created_at > ?nhưng không cho WHERE created_at > ? riêng.
    • Over-indexing: nhiều index → làm INSERT/UPDATE chậm, bạn cần biết cách cân đối.

    3. Query Optimization

    Khi query phức tạp (JOIN nhiều bảng, GROUP BY, ORDER BY, subquery), bạn cần biết:

    • Sử dụng EXPLAIN để xem query plan (key, type, rows, extra).
    • Tối ưu bằng:
      • Giảm join nếu không cần thiết.
      • Chuyển subquery thành join hoặc CTE.
      • Dùng index phù hợp với WHERE + ORDER BY cùng lúc.
    • Denormalization hợp lý:
      Khi load data nặng, bạn có thể lưu sẵn dữ liệu tổng hợp (materialized view / summary table).
    • Caching layer: Redis hoặc query cache trong Laravel để tránh query lặp.

    4. Laravel Context (thực tế trong công việc)

    • Tránh N+1 bằng with() hoặc load().
    • Dùng chunk()cursor() cho dataset lớn.
    • Query log + DB::listen() để tìm query chậm.
    • Cấu hình slow_query_log trên MySQL để theo dõi query > X ms.
    • Khi cần: viết query tay (raw SQL) thay vì rely hoàn toàn vào Eloquent.

    5. Thực tế yêu cầu

    • Giải thích vì sao query này chậm và cách tối ưu.
    • Viết SQL truy vấn nhanh hơn từ ví dụ thực tế.
    • So sánh INNER JOIN vs EXISTS trong tối ưu hóa.
    • Cách bạn debug query chậm trong production.
    • Khi nào bạn dùng index hint.
  • 6 câu hỏi về tối ưu MySQL Performance Optimization

    6 câu hỏi về tối ưu MySQL Performance Optimization

    Dưới đây là 6 câu hỏi phỏng vấn MySQL Performance/Optimization thường gặp ở cấp Senior Backend (Laravel/MySQL/Kafka) – cùng với cách trả lời gọn, kỹ thuật

    1. Tại sao query này chậm? Làm sao phân tích và tối ưu?

    Trả lời:
    Tôi bắt đầu bằng việc chạy EXPLAIN để xem MySQL chọn index nào, kiểu truy vấn (type = ALL, ref, index…), và số dòng ước tính (rows).
    Nếu thấy type=ALL → full table scan → cần thêm hoặc tối ưu index.
    Sau đó kiểm tra:

    • WHEREORDER BYGROUP BY có dùng cột chưa được index.
    • Query có LIKE '%abc' → không dùng index.
    • Có JOIN bảng lớn không dùng khóa ngoài → xem lại quan hệ hoặc thêm index.
      Cuối cùng, tôi dùng SHOW PROFILES hoặc slow query log để xác định bottleneck CPU/I/O.

    Kết luận: tối ưu bằng index đúng cột, tránh hàm trên cột (LOWER(col)), và refactor query logic.

    2. Khi nào bạn dùng Composite Index?

    Trả lời:
    Khi query lọc theo nhiều cột có thứ tự cố định.
    Ví dụ:

    SELECT * FROM orders WHERE user_id = ? AND created_at > ?

    → tạo index (user_id, created_at) giúp MySQL chỉ quét phạm vi cần.
    Tôi nhớ quy tắc “Leftmost Prefix”: MySQL chỉ dùng index từ trái sang phải, nên thứ tự cột trong index phải trùng với pattern query phổ biến.
    Cũng lưu ý: không nên tạo index trùng lặp ((user_id) và (user_id, created_at)).

    3. EXPLAIN cho thấy “Using filesort” — nghĩa là gì và xử lý ra sao?

    Trả lời:
    Using filesort nghĩa là MySQL phải tự sắp xếp dữ liệu trong RAM hoặc disk (thay vì đọc theo index).
    Tôi sẽ kiểm tra ORDER BY và WHERE có thể dùng cùng một index không.
    Nếu query:

    SELECT * FROM orders WHERE user_id = ? ORDER BY created_at DESC

    → tạo index (user_id, created_at DESC) sẽ tránh “Using filesort”.
    Ngoài ra, giảm số lượng cột select (chỉ lấy cần thiết) cũng giảm tải sorting.

    4. Khi nào nên dùng EXISTS thay vì JOIN hoặc IN?

    Trả lời:

    • EXISTS tốt hơn IN khi bảng con lớn vì nó dừng kiểm tra ngay khi tìm thấy match đầu tiên.
    • JOIN hữu ích khi cần lấy dữ liệu từ nhiều bảng, nhưng có thể sinh duplicate hoặc scan lớn.
      Ví dụ:
    SELECT * FROM users u WHERE EXISTS (
      SELECT 1 FROM orders o WHERE o.user_id = u.id
    )
    

    → hiệu quả hơn WHERE u.id IN (SELECT user_id FROM orders) nếu orders có nhiều dòng.

    5. Làm thế nào để phát hiện và xử lý query chậm trong production Laravel?

    Trả lời:

    • Bật slow_query_log trên MySQL, đặt threshold (vd: 0.5s).
    • Trong Laravel, dùng DB::listen() để log query > X ms.
    • Dùng công cụ profiling như Laravel TelescopeClockwork hoặc Blackfire.
    • Khi xác định query chậm, tôi chạy EXPLAIN, thêm index, refactor logic hoặc caching (Redis).
    • Nếu query do ORM sinh ra, tôi kiểm tra N+1 queries, thêm with() để eager load.

    6. Giải thích cách tối ưu MySQL cho hệ thống có lượng ghi (write) cao.

    Trả lời:

    • Tối ưu InnoDB configinnodb_flush_log_at_trx_commit=2innodb_buffer_pool_size chiếm ~70% RAM.
    • Giảm khóa (locking): chia transaction nhỏ, tránh update hàng loạt.
    • Sharding hoặc phân vùng (partitioning) dữ liệu lớn.
    • Batch insert/update thay vì từng dòng.
    • Dùng queue (Kafka) để xử lý async thay vì ghi đồng bộ ngay.
    • Index tối thiểu cần thiết: vì mỗi index thêm chi phí ghi.