"Đừng chỉ viết câu query chạy được. Hãy viết câu query mà SQL Server có thể hiểu và tối ưu được."
1️⃣ Tư Duy Về Query Optimizer (QO)
Query Optimizer của SQL Server là một cỗ máy dựa trên chi phí (Cost-Based). Nó không tìm ra execution plan "hoàn hảo nhất" vì việc tính toán mọi trường hợp sẽ mất quá nhiều thời gian. Nó tìm ra plan đủ tốt (Good Enough Plan) dựa trên Statistics hiện có.
- Sai lầm thường gặp: Developer nghĩ rằng dùng
INDEXA thì SQL Server chắc chắn phải dùng Index A. - Thực tế: Nếu SQL Server thấy việc "Scan càn quét" (Table Scan/Clustered Index Scan) rẻ hơn "Tìm kiếm từ khóa + Lật sách" (Non-Clustered Index Seek + Key Lookup), nó sẽ bỏ qua Index của bạn. Hiện tượng "Tipping Point" này thường xảy ra khi query lấy ra hơn 1% - 3% dữ liệu của bảng.
2️⃣ Hiểu Sâu Về Index: B-Tree và SARGability
SARGability (Search Argument Able)
Một mệnh đề WHERE được gọi là SARGable nếu SQL Server có thể dùng Index (Seek) để tìm ra dữ liệu.
❌ Non-SARGable (Gây Scan - Sát thủ hiệu năng):
sql-- Dùng hàm trên cột (SQL Server phải chạy hàm này cho toàn bộ dòng)
SELECT * FROM Users WHERE YEAR(CreatedAt) = 2024
-- Dùng wildcard ở đầu
SELECT * FROM Users WHERE Email LIKE '%@gmail.com'
✅ SARGable (Dịch chuyển hàm sang biến):
sqlSELECT * FROM Users WHERE CreatedAt >= '2024-01-01' AND CreatedAt < '2025-01-01'
SELECT * FROM Users WHERE Email LIKE 'john%' -- Wildcard ở cuối vẫn SARGable
3️⃣ Locking, Blocking & Deadlock
- Locking: Là cơ chế SQL Server bảo vệ Data Integrity.
- Blocking: Xảy ra khi Session A đang giữ Lock trên Data X, và Session B cũng muốn Lock Data X nhưng không tương thích, nên Session B phải đợi.
- Deadlock: A đợi B nhả Lock, và B đợi A nhả Lock. Cả 2 chờ nhau mãi mãi.
Gỡ rối (Mindset):
- Giữ transaction càng ngắn càng tốt.
- Truy cập vào các bảng theo một thứ tự nhất định trong mọi Stored Procedure (để tránh Deadlock).
- Nếu không cần đọc dữ liệu chuẩn 100% tại thời điểm đó, xem xét dùng
WITH (NOLOCK)(nhưng cảnh giác với Dirty Read). - Tuyệt chiêu: Kích hoạt tính năng RCSI (Read Committed Snapshot Isolation) để Reader không block Writer, Writer không block Reader (như cách Oracle hoặc PostgreSQL hoạt động mặc định).
sql-- Bật RCSI cho database
ALTER DATABASE [YourDB] SET READ_COMMITTED_SNAPSHOT ON;
4️⃣ Implicit Conversion Trap
Khi truyền parameter không đúng kiểu dữ liệu của cột, SQL Server sẽ tự ép kiểu (Implicit Conversion).
Vấn đề lớn: Việc ép kiểu thường khiến độ ưu tiên thay đổi, ví dụ biến NVARCHAR so sánh với cột VARCHAR. Do NVARCHAR ưu tiên cao hơn, SQL Server ép cột VARCHAR của toàn bộ bảng thành NVARCHAR -> Gây Index Scan.
✅ Giải pháp: Nếu cột là VARCHAR, truyền tham số đúng kiểu VARCHAR (Trong EF Core: Dùng .HasColumnType("varchar(100)")).
Tóm tắt bài học: Execution Plan là "bản đồ", Statistics là "la bàn". Hãy tối ưu query bằng việc đọc Execution Plan và nhìn vào cảnh báo Implicit Conversion hay Missing Index.