# Báo Cáo Chi Tiết Về Cơ Chế Khôi Phục Trong SQL Server
## I. Giới Thiệu Về Tính Cần Thiết Của Việc Khôi Phục (Recovery)
Tính cần thiết của cơ chế khôi phục bắt nguồn từ các thuộc tính **ACID** (Atomicity, Consistency, Isolation, Durability) của giao dịch. Các thuật toán khôi phục là kỹ thuật đảm bảo tính nhất quán, tính nguyên tố và tính bền vững của cơ sở dữ liệu (CSDL) bất chấp các sự cố,.
Trong môi trường thực tế, để đạt hiệu suất cao, các Hệ quản trị CSDL (DBMS) như SQL Server thực hiện các thay đổi dữ liệu trong bộ nhớ khả biến (**buffer cache**) thay vì ghi trực tiếp ra đĩa vật lý (non-volatile storage),. Điều này đặt ra thách thức: nếu hệ thống gặp sự cố (ví dụ: cúp điện, lỗi phần cứng), dữ liệu đã được cam kết (committed) nhưng chưa kịp ghi ra đĩa có nguy cơ bị mất.
Mục tiêu của Quản lý Khôi phục là đảm bảo:
* **Tính Nguyên tố (Atomicity):** Đảm bảo tất cả các hành động trong giao tác (transaction) xảy ra hoàn toàn hoặc không có hành động nào xảy ra. Điều này được xử lý bằng cơ chế **UNDO** (Hoàn tác).
* **Tính Bền vững (Durability):** Đảm bảo rằng hiệu ứng của giao tác đã commit phải duy trì sau khi hệ thống khởi động lại (restart),. Điều này được xử lý bằng cơ chế **REDO** (Thực hiện lại).
---
## II. Đối Chiếu: Phương Pháp Undo/Redo Logging Lý Thuyết và Thực Tiễn
Để hiểu sâu sắc cơ chế khôi phục của SQL Server, chúng ta cần xem xét nền tảng lý thuyết **Undo/Redo Logging** (được giảng dạy trong giáo trình) và phân tích lý do tại sao các hệ quản trị CSDL hiện đại cần phát triển nó lên một tầm cao mới.
### 1. Nền Tảng Lý Thuyết: Phương Pháp Undo/Redo Logging
Đây là phương pháp tiêu chuẩn nhằm khắc phục hạn chế về hiệu suất của các mô hình sơ khai (No-Steal/Force). Phương pháp này áp dụng chiến lược **STEAL + NO-FORCE**, cho phép hệ thống linh hoạt trong quản lý bộ đệm.
**Quy tắc Khôi phục (Theo lý thuyết giáo trình):**
Khi hệ thống khởi động lại sau sự cố, thuật toán sẽ phân loại các giao tác dựa trên Nhật ký (Log):
* Gọi **$S$** là tập hợp các giao tác đã hoàn tất (trong log có mẫu tin `<COMMIT T>`).
* Gọi **$S'$** là tập hợp các giao tác chưa hoàn tất (trong log không có commit hoặc có `<ABORT T>`).
Quy trình xử lý:
1. **Với các giao tác $T_i \in S$ (Đã Commit):** Hệ thống thực hiện **REDO** (Làm lại).
* *Lý do:* Do chính sách **NO-FORCE**, dữ liệu đã commit có thể vẫn nằm trên RAM và chưa kịp xuống đĩa khi sập nguồn.
2. **Với các giao tác $T_j \in S'$ (Chưa Commit):** Hệ thống thực hiện **UNDO** (Hoàn tác).
* *Lý do:* Do chính sách **STEAL**, dữ liệu "rác" của giao tác đang chạy dở có thể đã bị ghi đè xuống đĩa.
**Cơ chế Checkpoint (Điểm kiểm tra) trong lý thuyết:**
Để tránh việc phải duyệt log từ đầu (vô hạn), lý thuyết đề xuất các mẫu Checkpoint:
* *Điểm kiểm tra đơn giản:* Tạm dừng hệ thống, chờ mọi giao tác kết thúc rồi mới ghi Checkpoint (Blocking).
* *Điểm kiểm tra linh động:* Ghi lại trạng thái các giao tác đang chạy ($T_1...T_k$) tại thời điểm bắt đầu Checkpoint.
---
### 2. Tại Sao SQL Server Không Sử Dụng Trực Tiếp Phương Pháp "Nguyên Bản"?
Mặc dù lý thuyết trên là chính xác về mặt logic, nhưng các hệ thống DBMS hiện đại như SQL Server không thể áp dụng nó một cách trực tiếp ("nguyên văn" sách giáo khoa) vì những hạn chế về hiệu suất trong môi trường thực tế (Production):
**Hạn chế 1: Vấn đề "Dừng hình" của Checkpoint (Blocking Checkpoint)**
* Trong lý thuyết "Điểm kiểm tra đơn giản", hệ thống phải tạm ngưng nhận giao tác mới. Trong thực tế (ví dụ: hệ thống ngân hàng, thương mại điện tử), việc dừng hệ thống dù chỉ vài giây cũng là không thể chấp nhận.
* *Thực tế:* SQL Server cần một cơ chế Checkpoint hoàn toàn không gây gián đoạn (Non-blocking / Fuzzy Checkpoint).
**Hạn chế 2: Độ phức tạp của Khóa (Locking) và Undo Logic**
* Lý thuyết Undo/Redo nguyên bản thường giả định việc khôi phục hoạt động trên các trang vật lý (Physical Logging). Tuy nhiên, SQL Server hỗ trợ khóa mức dòng (Row-level locking) và chỉ mục (Index).
* Nếu chỉ Undo vật lý đơn thuần, việc hoàn tác một giao tác có thể vô tình làm hỏng thay đổi của một giao tác khác trên cùng một trang dữ liệu. Do đó, cần một cơ chế phức tạp hơn gọi là "Logical Undo".
**Hạn chế 3: Hiệu suất khôi phục**
* Thuật toán lý thuyết thường tách biệt hai pha: Quét log tìm $S$ để Redo, sau đó xử lý $S'$ để Undo. Việc này có thể chưa tối ưu trong việc tái tạo lại trạng thái bộ đệm chính xác nhất trước khi Undo.
---
### 3. Sự Kế Thừa và Phát Triển: Thuật Toán ARIES trong SQL Server
SQL Server **không bỏ đi** phương pháp Undo/Redo Logging. Trái lại, nó sử dụng thuật toán **ARIES** - vốn là sự **hiện thực hóa hoàn hảo nhất** của ý tưởng Undo/Redo Logging (STEAL + NO-FORCE).
SQL Server vẫn tuân thủ nguyên tắc cốt lõi: **Vẫn Redo tập $S$ và Undo tập $S'$**. Tuy nhiên, nó bổ sung các kỹ thuật tiên tiến:
1. **Write-Ahead Logging (WAL):** Một giao thức nghiêm ngặt đảm bảo bản ghi Log Undo phải xuống đĩa trước khi dữ liệu xuống đĩa. Đây là điều kiện tiên quyết để ý tưởng STEAL hoạt động an toàn.
2. **Repeating History (Lặp lại lịch sử):**
* Khác với lý thuyết đơn giản, ARIES thực hiện **Redo tất cả** (bao gồm cả tập $S'$ chưa commit) để đưa Database về đúng trạng thái vật lý tại thời điểm sập nguồn.
* Sau đó mới thực hiện Undo $S'$ (Undo logic). Cách này đảm bảo tính nhất quán dữ liệu cao hơn nhiều so với lý thuyết nguyên bản.
3. **Logging during Undo (Ghi log khi Undo):**
* SQL Server ghi lại cả hành động Undo vào log (gọi là CLR - Compensation Log Record). Điều này giúp quá trình khôi phục không bị kẹt nếu sập nguồn lần 2 - một chi tiết mà lý thuyết cơ bản thường bỏ qua.
## III. Phương Pháp Khôi Phục Trong SQL Server (ARIES)
Như đã phân tích ở phần trước, SQL Server không sử dụng trực tiếp mô hình Undo/Redo Logging lý thuyết (vốn đơn giản hóa quá mức). Thay vào đó, nó sử dụng thuật toán **ARIES** (Algorithms for Recovery and Isolation Exploiting Semantics).
ARIES kế thừa tư tưởng **STEAL + NO-FORCE** của Undo/Redo Logging nhưng bổ sung các cơ chế tối ưu để đảm bảo hiệu suất cao và tính toàn vẹn dữ liệu trong môi trường thực tế.
#### 1. Tổng quát về Quá trình và Các Thuật ngữ
Quá trình khôi phục sau sự cố (Crash Recovery) được thực hiện qua ba pha tuần tự,,:
| Pha | Mục đích Chính | Cơ chế |
| :--- | :--- | :--- |
| **1. Phân tích (Analysis)** | Tái tạo lại trạng thái hệ thống tại thời điểm sự cố. | Quét log tiến lên từ Checkpoint cuối cùng để xây dựng ATT và DPT,. |
| **2. Thực hiện lại (Redo)** | Áp dụng lại mọi thay đổi (cả commit và uncommitted) để khôi phục trạng thái ngay trước khi sập. | Quét log tiến lên từ MinLSN nhỏ nhất trong DPT, lặp lại lịch sử (Repeat History),,. |
| **3. Hoàn tác (Undo)** | Hoàn tác các giao tác chưa commit tại thời điểm sự cố. | Quét log ngược lại, tạo CLR để ghi lại hành động Undo, đảm bảo tính nguyên tố,,. |
#### 2. Giải thích Thuật ngữ
* **Nhật ký Giao tác (Transaction Log/WAL):** Tệp tuần tự ghi lại tất cả các thay đổi và giao dịch,.
* **LSN (Log Sequence Number):** Một số duy nhất, luôn tăng, xác định vị trí của bản ghi log. LSN được sử dụng để liên kết các bản ghi của cùng một giao tác,,.
* **pageLSN:** LSN của bản ghi log mới nhất đã sửa đổi một trang dữ liệu cụ thể. Nó nằm trong header của trang dữ liệu,,.
* **MinLSN (Minimum Recovery LSN):** LSN của bản ghi log sớm nhất cần thiết để đảm bảo khôi phục thành công toàn bộ cơ sở dữ liệu,.
* **Điểm kiểm tra (Checkpoint):** Tạo một điểm đã biết (known good point) trên đĩa bằng cách ghi các trang bị thay đổi (dirty pages) từ bộ đệm ra đĩa, giảm thời gian khôi phục bằng cách giảm lượng log cần quét (log pruning),,.
* **DPT (Dirty Page Table):** Một bảng được xây dựng trong Pha Phân tích, chứa các trang dữ liệu trong bộ đệm đã bị sửa đổi (dirty) và chưa được ghi ra đĩa ổn định, cùng với `recLSN` (LSN của bản ghi log đầu tiên làm cho trang đó bẩn),,.
* **ATT (Active Transaction Table):** Bảng ghi lại các giao tác đang hoạt động (Running/Undo Candidate) tại thời điểm sự cố, cùng với `lastLSN`,,.
* **CLR (Compensation Log Record):** Bản ghi log bù trừ được tạo ra trong Pha UNDO để ghi lại hành động hoàn tác. **CLRs không bao giờ bị UNDO**,.
#### 3. Điểm Cải Tiến Vượt Trội So Với Undo/Redo Logging Nguyên Bản
So với lý thuyết Undo/Redo cơ bản (quét log tìm tập $S$ để Redo, tập $S'$ để Undo), ARIES trong SQL Server có 2 cải tiến mang tính cách mạng:
**A. Nguyên tắc "Lặp lại Lịch sử" (Repeating History)**
* *Lý thuyết cũ:* Chỉ Redo những giao dịch đã Commit.
* *Cải tiến ARIES:* Redo **TẤT CẢ** (cả đã Commit và chưa Commit).
* *Lợi ích:* Việc này đưa Database về trạng thái vật lý đồng bộ hoàn toàn (State-complete). Điều này cực kỳ quan trọng đối với các hệ thống hỗ trợ **khóa mức dòng (Row-level locking)**. Nếu không tái tạo lại chính xác cấu trúc trang dữ liệu (bao gồm cả những thay đổi rác của giao dịch chưa commit), việc thực hiện Undo sau đó có thể gây lỗi cấu trúc dữ liệu.
**B. Ghi Nhật ký khi Hoàn tác (Logging During Undo - CLR)**
* *Lý thuyết cũ:* Không đề cập rõ việc ghi log cho hành động Undo. Nếu hệ thống đang Undo mà bị sập nguồn lần 2, khi khởi động lại nó sẽ phải Undo lại từ đầu (có thể gây lặp vô hạn hoặc sai lệch).
* *Cải tiến ARIES:* Khi Undo, SQL Server ghi lại hành động đó bằng **CLR**. CLR có đặc tính là "không bao giờ bị Undo nữa".
* *Lợi ích:* Nếu sập nguồn lần 2, hệ thống nhìn thấy CLR sẽ biết rằng "Bước Undo này đã làm xong rồi", và sẽ bỏ qua nó để đi tiếp. Điều này đảm bảo quá trình khôi phục luôn tiến về phía trước, không bao giờ bị kẹt.
## IV. Phân Tích Chi Tiết Tình Huống Giả Lập & Đối Chiếu Lý Thuyết
Để minh chứng cho cơ chế hoạt động của thuật toán ARIES và so sánh với lý thuyết Undo/Redo Logging, chúng ta thực hiện phân tích tình huống giả lập sau sự cố (Post-mortem analysis) dựa trên dữ liệu Nhật ký giao tác (Transaction Log).
### 1. Mô Tả Tình Huống Giả Lập
Chúng ta thiết lập một kịch bản giao dịch ngân hàng với bảng `BankAccounts` (X, Y) có số dư ban đầu là 1000$. Hệ thống thực hiện hai giao tác song song và gặp sự cố mất điện đột ngột (`SHUTDOWN WITH NOWAIT`):
* **Giao tác $T_A$ (Mã ID ...446):** Chuyển 100$ từ X sang Y. Trạng thái: **Đã Commit** trước khi sập.
* **Giao tác $T_B$ (Mã ID ...447):** Chuyển 50$ từ Y sang X. Trạng thái: **Đang chạy (Uncommitted)** thì hệ thống sập.
### 2. Phân Tích Quá Trình Phục Hồi Qua File Log
Sau khi khởi động lại SQL Server, chúng ta trích xuất dữ liệu từ `fn_dblog`. Dưới đây là hình ảnh thực tế và các bước ánh xạ vào quy trình khôi phục:
**Hình ảnh Log Chi tiết thu được:**
*(Chèn hình ảnh `fn_dblog` có chứa dòng COMPENSATION mà bạn đã cung cấp)*

#### BƯỚC 1: Pha Phân Tích (Analysis Phase)
* **Hành động của hệ thống:**
* SQL Server đọc file Log bắt đầu từ điểm Checkpoint gần nhất (được đánh dấu là `LOP_BEGIN_CKPT` - không hiển thị trong ảnh cắt nhưng nằm trước dòng 1).
* Hệ thống quét xuôi đến cuối log (thời điểm 17:24:05) để xác định trạng thái các giao tác.
* **Kết quả trên Log:**
* Hệ thống tìm thấy **Giao tác $T_A$ (ID ...446):** Có dòng `LOP_BEGIN_XACT` (Dòng 1) và kết thúc bằng `LOP_COMMIT_XACT` (Dòng 4). $\rightarrow$ Xác định $T_A$ thuộc nhóm **Đã hoàn tất**.
* Hệ thống tìm thấy **Giao tác $T_B$ (ID ...447):** Có dòng `LOP_BEGIN_XACT` (Dòng 5) và các dòng sửa đổi dữ liệu `LOP_MODIFY_ROW`, nhưng **tuyệt đối không có** dòng Commit. $\rightarrow$ Xác định $T_B$ thuộc nhóm **Chưa hoàn tất (Loser)**.
* **Kết quả nội bộ (RAM):**
* $T_B$ được thêm vào **Bảng Giao tác Hoạt động (ATT)** với trạng thái "Undo Candidate".
* ** Đối chiếu với Lý thuyết Undo/Redo Logging:**
* *Lý thuyết:* Xác định tập $S$ (các giao tác có Commit - ở đây là $T_A$) và tập $S'$ (các giao tác không có Commit - ở đây là $T_B$).
* *Thực tế:* Hoàn toàn khớp. Pha này chính là bước phân loại $S$ và $S'$.
---
#### BƯỚC 2: Pha Thực Hiện Lại (Redo Phase)
* **Hành động của hệ thống:**
* Hệ thống thực hiện nguyên tắc **"Lặp lại lịch sử" (Repeating History)**. Nó bắt đầu từ điểm MinLSN và thực hiện lại các lệnh `LOP_MODIFY_ROW` của **CẢ** $T_A$ và $T_B$.
* **Bằng chứng từ Error Log:**
*(Chèn hình ảnh Error Log có dòng "49 transactions rolled forward")*

* Dòng thông báo: `49 transactions rolled forward...`
* **Ý nghĩa:**
* **Với $T_A$ (Dòng 2, 3 trong Log):** SQL Server thực hiện lại việc trừ tiền X và cộng tiền Y. Dữ liệu trên đĩa (Data Page) được cập nhật thành công (X=900, Y=1100).
* **Với $T_B$ (Dòng 6 trong Log):** Mặc dù $T_B$ chưa commit, SQL Server **VẪN REDO** dòng lệnh trừ 50$ của Y (Y giảm xuống 1050 trên bộ nhớ).
* ** Đối chiếu với Lý thuyết Undo/Redo Logging:**
* *Lý thuyết:* Chỉ Redo các giao tác thuộc tập $S$ ($T_A$). Bỏ qua $T_B$.
* *Thực tế (Cải tiến ARIES):* Redo **tất cả** ($T_A$ và $T_B$).
* *Tại sao khác biệt?* Việc Redo cả $T_B$ giúp đưa cấu trúc vật lý của Database về trạng thái chính xác nhất trước khi sập, đảm bảo tính nhất quán của LSN trên trang dữ liệu (pageLSN) trước khi thực hiện Undo logic.
---
#### BƯỚC 3: Pha Hoàn Tác (Undo Phase)
Đây là pha quan trọng nhất để đảm bảo tính Nguyên tử (Atomicity).
* **Hành động của hệ thống:**
* Hệ thống dựa vào bảng ATT (từ pha Phân tích), xác định $T_B$ cần phải Undo.
* Nó đọc log ngược từ dòng 6 (Modify của $T_B$) và thực hiện thao tác nghịch đảo.
* **Kết quả trên Log (Dòng 7 & 8):**
* **Dòng 7:** `LOP_MODIFY_ROW` với mô tả `COMPENSATION`.
* *Ý nghĩa:* Đây là hành động bù trừ. SQL Server cộng lại 50$ vào tài khoản Y. Giá trị Y từ 1050 trở về 1100.
* *Thời gian:* Chú ý timestamp là **17:27:26** (3 phút sau khi sập), chứng tỏ đây là hành động mới được sinh ra sau khi khởi động lại.
* **Dòng 8:** `LOP_ABORT_XACT`.
* *Ý nghĩa:* Đánh dấu giao dịch $T_B$ đã chính thức bị hủy bỏ.
* **Bằng chứng từ Error Log:**
* Dòng thông báo: `1 transactions rolled back...`. Con số 1 này chính là $T_B$.
* ** Đối chiếu với Lý thuyết Undo/Redo Logging:**
* *Lý thuyết:* Undo các giao tác thuộc tập $S'$ ($T_B$).
* *Thực tế:* SQL Server thực hiện đúng như vậy, nhưng có **Cải tiến quan trọng**: Nó ghi lại hành động Undo vào Log (Dòng 7 - CLR).
* *Giá trị:* Nếu hệ thống bị sập nguồn lần nữa ngay sau dòng 7, khi khởi động lại, SQL Server nhìn thấy dòng COMPENSATION này và biết rằng việc Undo đã hoàn tất, không cần làm lại. Điều này khắc phục điểm yếu của lý thuyết nguyên bản.
### 3. Tổng Kết Kết Quả Dữ Liệu
Sau khi hoàn tất 3 pha, truy vấn dữ liệu cho thấy:
| Tài khoản | Giá trị Cuối | Giải thích Quá trình Recovery |
| :--- | :--- | :--- |
| **X** | **900** | Được khôi phục nhờ pha **Redo** (do $T_A$ thuộc tập $S$). |
| **Y** | **1100** | Được khôi phục nhờ pha **Redo** (cộng 100 từ $T_A$) và pha **Undo** (hoàn trả 50 từ $T_B$). |
**Kết luận:** Quá trình thực nghiệm trên SQL Server đã minh chứng hoàn hảo cho sự áp dụng và phát triển của lý thuyết Undo/Redo Logging (mô hình STEAL + NO-FORCE) thông qua thuật toán ARIES, đảm bảo tuyệt đối tính ACID cho hệ thống.
## ** CHI TIẾT THỰC HIỆN TÌNH HUỐNG**
### BƯỚC 1: Chuẩn bị môi trường & "Kích hoạt" chế độ ghi Log thật sự
**Hành động:** Chạy đoạn code sau trong SSMS.
```sql
USE master;
GO
-- 1. Xóa database cũ (nếu có) để làm lại từ đầu cho sạch sẽ
IF EXISTS (SELECT name FROM sys.databases WHERE name = 'BankRecoveryLab')
DROP DATABASE BankRecoveryLab;
GO
-- 2. Tạo Database mới
CREATE DATABASE BankRecoveryLab;
GO
-- 3. Cấu hình Recovery Model là FULL
ALTER DATABASE BankRecoveryLab SET RECOVERY FULL;
GO
-- 4. Tắt tính năng AUTO_CLOSE
ALTER DATABASE BankRecoveryLab SET AUTO_CLOSE OFF;
GO
-- 5. KÍCH HOẠT CHẾ ĐỘ FULL (QUAN TRỌNG NHẤT)
-- Tạo một bản backup giả để đánh dấu mốc bắt đầu chuỗi Log
BACKUP DATABASE BankRecoveryLab TO DISK = 'NUL';
GO
```
**🔍 GIẢI THÍCH KHÁI NIỆM:**
* **Recovery Model = FULL:** Chúng ta nói với SQL Server: *"Hãy ghi lại mọi thứ và KHÔNG BAO GIỜ được xóa (Truncate) log cho đến khi tôi cho phép"*.
* **Tại sao cần dòng Backup (Mục 5)?**
* Nếu không có dòng này, dù bạn set là FULL, SQL Server vẫn chạy ở chế độ **Pseudo-Simple (Giả Simple)**. Nghĩa là nó sẽ tự động xóa log (Truncate) mỗi khi Checkpoint để tiết kiệm chỗ.
* Dòng Backup này chính là "chốt chặn" đầu tiên. Từ giờ trở đi, Log sẽ chất đống lại (Persist) chứ không tự biến mất nữa. Bạn sẽ xem được lịch sử sau khi Crash.
* **AUTO_CLOSE OFF:**
* Mặc định (bản Express), cái này là ON. Nghĩa là khi không ai dùng, Database tự tắt. Khi bạn query, nó tự bật (Start up).
* Điều này làm **Error Log** bị rác (đầy rẫy dòng "Starting up..."). Ta tắt nó đi để Log sạch sẽ, dễ nhìn.
---
### BƯỚC 2: Tạo dữ liệu nền & Điểm mốc an toàn
**Hành động:**
```sql
USE BankRecoveryLab;
GO
-- Tạo bảng Tài khoản
CREATE TABLE BankAccounts (
AccountName CHAR(1) PRIMARY KEY, -- Tên tài khoản (X, Y)
Balance INT -- Số dư
);
-- Nạp trạng thái ban đầu: X có 1000, Y có 1000
INSERT INTO BankAccounts VALUES ('X', 1000), ('Y', 1000);
GO
-- QUAN TRỌNG: Ép dữ liệu xuống đĩa cứng
CHECKPOINT;
GO
```
**🔍 GIẢI THÍCH KHÁI NIỆM:**
* **CHECKPOINT:**
* Lệnh này bảo SQL Server: *"Tất cả những trang dữ liệu đang nằm trên RAM (Dirty Pages), hãy ghi ngay xuống file .mdf (ổ cứng) cho tôi!"*.
* Nó cũng ghi vào Transaction Log một dòng `LOP_BEGIN_CKPT`.
* **Mục đích:** Để khi Recovery chạy, nó biết rằng trước điểm Checkpoint này, dữ liệu đã an toàn, không cần mất công kiểm tra lại (Analysis) đoạn lịch sử cũ rích đó nữa.
---
### BƯỚC 3: Tạo kịch bản Redo & Undo (Hỗn loạn)
**Hành động:**
```sql
-- GIAO DỊCH A (T_A): Chuyển 100$ từ X sang Y -> ĐÃ COMMIT
-- Logic Recovery: Phải REDO (Làm lại) để đảm bảo X mất tiền và Y nhận được tiền.
BEGIN TRAN T_A_Transfer;
UPDATE BankAccounts SET Balance = Balance - 100 WHERE AccountName = 'X'; -- X còn 900
UPDATE BankAccounts SET Balance = Balance + 100 WHERE AccountName = 'Y'; -- Y lên 1100
COMMIT TRAN T_A_Transfer;
-- GIAO DỊCH B (T_B): Chuyển 50$ từ Y sang X -> ĐANG CHẠY THÌ SẬP
-- Logic Recovery: Phải UNDO (Hoàn tác) vì lệnh này chưa hoàn tất.
BEGIN TRAN T_B_Crash;
UPDATE BankAccounts SET Balance = Balance - 50 WHERE AccountName = 'Y'; -- Y bị trừ còn 1050 (trên RAM)
-- Chưa kịp cộng tiền cho X thì...
-- KHÔNG COMMIT. Để treo ở đây.
```
**🔍 GIẢI THÍCH:**
Chúng ta đang tạo ra trạng thái **Inconsistent (Không nhất quán)** giữa RAM và Đĩa cứng. Chỉ có Transaction Log (.ldf) là nơi duy nhất nắm giữ sự thật lúc này.
- T_A_Transfer: Đã Commit. Log đã ghi nhận việc chuyển tiền xong. Nhưng dữ liệu trên đĩa - cứng (.mdf) có thể vẫn là cũ (1000) vì ta chưa chạy Checkpoint lại. -> Cần Redo.
- T_B_Crash: Đã sửa dữ liệu trên RAM (Y còn 1050), Log đã ghi nhận việc trừ tiền. Nhưng chưa có lệnh Commit. -> Cần Undo.
---
### BƯỚC 4: Giả lập sự cố (Hard Crash)
**Hành động:**
Mở một cửa sổ Query mới (New Query), chạy lệnh này:
```sql
USE master;
GO
-- Giết chết SQL Server ngay lập tức, không cho phép Checkpoint cuối cùng
SHUTDOWN WITH NOWAIT;
```
**🔍 GIẢI THÍCH:**
* Nếu bạn Stop Service bình thường, SQL Server sẽ lịch sự chạy `CHECKPOINT` lần cuối rồi mới tắt. Như thế thì Recovery lần sau chả có gì để làm cả.
* `WITH NOWAIT` mô phỏng việc **Rút phích điện**. RAM bị xóa sạch.
* Toàn bộ dữ liệu trên RAM (Buffer Pool) bao gồm cả những thay đổi của T_A và T_B biến mất. Chỉ còn lại file .mdf (cũ) và file .ldf (nhật ký) trên ổ cứng.
---
### BƯỚC 5: Khởi động lại & Kiểm tra Error Log (Bản tin thời sự)
**Hành động:**
1. Vào **Services** của Windows -> Start lại **SQL Server (MSSQLSERVER)**.
2. Mở lại SSMS -> Kết nối lại -> Chạy lệnh sau:
```sql
EXEC sp_readerrorlog 0, 1, 'BankRecoveryLab';
```
**🔍 GIẢI THÍCH KHÁI NIỆM:**
* **Error Log (`sp_readerrorlog`):**
* Đây là file text (`ERRORLOG` trong thư mục cài đặt). Nó là "báo cáo tổng hợp".
* **Mục đích:** Bạn nhìn vào đây để xác nhận xem Recovery CÓ CHẠY KHÔNG?
* **Kết quả mong đợi:** Bạn sẽ thấy dòng *"X transactions rolled forward"* (Redo thành công A) và *"X transactions rolled back"* (Undo thành công B).
* Rolled Forward (Redo): Tái hiện lại T_A.
* Rolled Back (Undo): Hủy bỏ T_B.

#### 1. Dòng số 4: `1 transactions rolled back` (Con số hoàn hảo)
Đây là con số quan trọng nhất.
* **Nó là gì?** Chính là giao dịch **T_B_Crash** (User B chuyển 50$) mà bạn đang chạy dở dang thì bị sập nguồn.
* **Ý nghĩa:** SQL Server nhận thấy giao dịch này chưa Commit -> Nó thực hiện **UNDO** (Hủy bỏ).
* **Kết luận:** Cơ chế **Atomicity** (Nguyên tử) hoạt động đúng 100%.
---
#### 2. Dòng số 3: `49 transactions rolled forward` (Con số bí ẩn)
Tại sao bạn chỉ chạy 1 giao dịch **T_A_Transfer** (User A chuyển 100$) mà hệ thống lại báo là 49?
Lý do là vì SQL Server là một "tảng băng trôi". Những gì bạn gõ lệnh chỉ là phần nổi, còn bên dưới nó có hàng chục **Giao dịch hệ thống (System Transactions)** chạy ngầm mà bạn không biết.
Trong khoảng thời gian từ lần Checkpoint cuối cùng đến lúc sập nguồn, 49 giao dịch này bao gồm:
1. **Giao dịch T_A_Transfer của bạn:** (User A chuyển 100$). Đây là cái duy nhất bạn quan tâm.
2. **Các giao dịch cấp phát trang (Page Allocation):** Khi bạn UPDATE dữ liệu, SQL Server có thể phải chia tách trang dữ liệu (Page Split) hoặc cấp phát thêm vùng nhớ mới trong file MDF. Mỗi lần cấp phát là một transaction nhỏ.
3. **Cập nhật Metadata:** Việc cập nhật thông tin vào các bảng hệ thống (như `sys.objects`, `sys.indexes`) để theo dõi sự thay đổi của bảng `BankAccounts`.
4. **Hệ quả của lệnh Backup:** Lệnh `BACKUP ... TO DISK='NUL'` cũng sinh ra các log record để đánh dấu chuỗi backup.
**=> Kết luận:** Đừng lo về số 49. Miễn là nó **lớn hơn 0**, nghĩa là pha **REDO** đã hoạt động. Nó đã cứu sống T_A của bạn cùng với 48 tác vụ ngầm khác của hệ thống để đảm bảo Database không bị lỗi cấu trúc (Corrupt).
---
#### 3. Dòng số 5: `Recovery is writing a checkpoint`
* **Ý nghĩa:** Sau khi hì hục dọn dẹp bãi chiến trường (Redo 49 cái, Undo 1 cái), SQL Server thở phào nhẹ nhõm: *"Xong rồi, sạch sẽ rồi!"*.
* **Hành động:** Nó ngay lập tức chạy một lệnh **CHECKPOINT** tự động để lưu trạng thái sạch sẽ này xuống đĩa.
* **Mục đích:** Để nếu 5 giây sau bạn lại rút phích điện tiếp, nó sẽ không phải làm lại 49 cái kia nữa (vì đã lưu mốc mới rồi).
---
### BƯỚC 6: Kiểm tra Transaction Log thật sự
Đây là bước quan trọng để thấy "bằng chứng" không bị xóa (nhờ bước 1).
**Hành động:**
```sql
USE BankRecoveryLab;
GO
SELECT
[Current LSN],
[Operation],
[Transaction ID], -- Cột quan trọng để liên kết
[Transaction Name], -- Chỉ hiện ở dòng BEGIN
[AllocUnitName], -- Tên bảng bị tác động
[Description]
FROM fn_dblog(NULL, NULL)
WHERE [Transaction ID] IN (
-- Truy vấn con: Tìm ID của 2 giao dịch T_A và T_B
SELECT [Transaction ID]
FROM fn_dblog(NULL, NULL)
WHERE [Transaction Name] IN ('T_A_Transfer', 'T_B_Crash')
)
OR [Operation] LIKE '%CHECKPOINT%';
```
**🔍 GIẢI THÍCH KHÁI NIỆM:**
* **File Log thật sự (`fn_dblog` đọc file .ldf):**
* Nhờ bước 1 (Full Recovery + Backup), SQL Server **không xóa** (Truncate) các dòng log cũ khi khởi động lại.
* **Mục đích:** Bạn nhìn thấy tận mắt "bằng chứng" những gì đã xảy ra trước khi sập nguồn.
**🔍 GIẢI THÍCH KẾT QUẢ MONG ĐỢI:**
1. **LOP_BEGIN_CKPT**: Điểm mốc an toàn.
2. **Nhóm của T_A_Transfer:**
* `LOP_BEGIN_XACT`: Có tên "T_A_Transfer".
* `LOP_MODIFY_ROW`: (Trừ tiền X) - Tên là NULL, nhưng ID giống dòng trên.
* `LOP_MODIFY_ROW`: (Cộng tiền Y).
* `LOP_COMMIT_XACT`: (Cam kết) -> **Đây là lý do nó được Redo.**
3. **Nhóm của T_B_Crash:**
* `LOP_BEGIN_XACT`: Có tên "T_B_Crash".
* `LOP_MODIFY_ROW`: (Trừ tiền Y).
* **KHÔNG CÓ COMMIT**: Dòng log kết thúc đột ngột tại đây (hoặc có thêm dòng `LOP_ABORT` do Recovery tự sinh ra sau này). -> **Đây là lý do nó bị Undo.**

#### PHẦN 1: Trước khi sập nguồn (Lúc 17:24:05)
Đây là những gì bạn đã gõ lệnh và chạy.
1. **Dòng 1-4 (Transaction ID ...446 - T_A_Transfer):**
* **Dòng 1:** `LOP_BEGIN_XACT`: Bắt đầu giao dịch chuyển tiền.
* **Dòng 2 & 3:** `LOP_MODIFY_ROW`: Thực hiện trừ tiền X và cộng tiền Y.
* **Dòng 4:** `LOP_COMMIT_XACT`: **QUAN TRỌNG.** Dòng này xác nhận giao dịch đã thành công.
* => Nhờ dòng số 4 này, khi Recovery chạy, nó biết phải **REDO** (Giữ lại) toàn bộ các dòng 1, 2, 3.
2. **Dòng 5-6 (Transaction ID ...447 - T_B_Crash):**
* **Dòng 5:** `LOP_BEGIN_XACT`: Bắt đầu giao dịch B.
* **Dòng 6:** `LOP_MODIFY_ROW`: Trừ tiền của Y (50$).
* **STOP!** Tại đây bạn đã rút phích điện (`SHUTDOWN WITH NOWAIT`).
* => Lưu ý là **KHÔNG CÓ** dòng `COMMIT` nào cho ID ...447 cả.
---
#### PHẦN 2: Sau khi bật lại nguồn (Lúc 17:27:26 - 3 phút sau)
Đây là những dòng log do **SQL Server TỰ ĐỘNG sinh ra** trong quá trình Recovery (Pha Undo). Bạn không hề gõ lệnh tạo ra chúng.
3. **Dòng 7 (Transaction ID ...447 - Vẫn là T_B_Crash):**
* **Operation:** `LOP_MODIFY_ROW`.
* **Description:** `COMPENSATION` (Đền bù/Bù trừ).
* **Giải thích:** SQL Server thấy dòng 6 đã trừ 50$ của Y nhưng chưa commit. Để sửa sai, nó tự động tạo ra dòng 7 này để **Cộng trả lại 50$ cho Y**.
* *Đây chính là hành động UNDO được ghi lại trên giấy trắng mực đen.*
4. **Dòng 8:** `LOP_ABORT_XACT`
* **Giải thích:** Thay vì kết thúc bằng `COMMIT` (Thành công), giao dịch này bị đóng dấu là `ABORT` (Hủy bỏ/Chết yểu).
---
#### Tại sao kết quả này lại "Đẹp"?
Hãy nhìn vào cột **Description** (cột cuối cùng bên phải):
* Các dòng 1 đến 6 có thời gian là **17:24:05**.
* Các dòng 7 và 8 có thời gian là **17:27:26**.
**Sự chênh lệch 3 phút này chứng minh:**
Dữ liệu của giao dịch B (dòng 5, 6) đã nằm "chết cứng" trên ổ cứng trong suốt 3 phút sập nguồn. Khi bạn bật máy lên, SQL Server đã đọc nó, và viết thêm dòng 7, 8 để dọn dẹp hậu quả.
---
### BƯỚC 7: Kiểm tra dữ liệu cuối cùng
**Hành động:**
```sql
SELECT * FROM BankAccounts;
```
**🔍 PHÂN TÍCH KẾT QUẢ:**
| Tài khoản | Giá trị Mong đợi | Giải thích cơ chế |
| :--- | :--- | :--- |
| **X** | **900** | **Do T_A (Redo):** Ban đầu 1000. T_A chuyển đi 100 -> Còn 900. Dù sập nguồn nhưng T_A đã Commit nên Log ép dữ liệu phải cập nhật. |
| **Y** | **1100** | **Do T_A (Redo) & T_B (Undo):** <br>1. Ban đầu 1000.<br>2. Nhận từ X 100 (T_A) -> Lên 1100.<br>3. Bị trừ 50 (T_B) -> Xuống 1050 (trên RAM).<br>4. **Sập nguồn & Recovery:** Hệ thống thấy T_B chưa commit -> **Undo (Hoàn tác)** việc trừ 50 -> Trả lại về 1100. |

## V. Tổng Kết Về Cơ Chế Khôi Phục Trong SQL Server
Từ quá trình phân tích lý thuyết và thực nghiệm tình huống giả lập, ta có thể rút ra những kết luận cốt lõi về cơ chế Recovery của SQL Server:
### 1. Sự Cân Bằng Giữa Hiệu Suất và An Toàn
SQL Server không chọn giải pháp an toàn đơn giản (như *No-Steal/Force*), mà chọn giải pháp tối ưu hiệu suất dựa trên kiến trúc **ARIES**.
* **Hiệu suất tối đa:** Bằng việc áp dụng chính sách **STEAL + NO-FORCE**, hệ thống giảm thiểu thao tác ghi đĩa ngẫu nhiên (Random I/O) và tối ưu hóa bộ nhớ đệm. Điều này cho phép xử lý hàng nghìn giao tác mỗi giây mà không bị nghẽn cổ chai tại ổ cứng.
* **Cái giá phải trả:** Sự phức tạp trong quy trình khôi phục. Hệ thống bắt buộc phải thực hiện cả hai pha **Redo** (để tái hiện dữ liệu chưa kịp ghi đĩa) và **Undo** (để dọn dẹp dữ liệu rác đã ghi đĩa).
### 2. Ý Nghĩa Của Việc Duy Trì Tính Toàn Vẹn (ACID)
Cơ chế Recovery không chỉ là công cụ sửa lỗi, mà là "trái tim" bảo vệ nguyên tắc ACID của cơ sở dữ liệu:
* **Tính Bền Vững (Durability):** Được đảm bảo tuyệt đối nhờ nguyên tắc **Write-Ahead Logging (WAL)**.
* *Minh chứng:* Giao tác $T_A$ dù chưa kịp Checkpoint xuống đĩa, nhưng nhờ Log đã ghi xuống trước (WAL), nên pha **Redo** đã khôi phục lại toàn bộ dữ liệu (X=900, Y=1100).
* **Tính Nguyên Tử (Atomicity):** Được đảm bảo bởi pha **Undo** và cơ chế **CLR**.
* *Minh chứng:* Giao tác $T_B$ bị sập giữa chừng được hoàn tác sạch sẽ ("All or Nothing"), trả lại 50$ cho Y thông qua bản ghi bù trừ (Compensation), đảm bảo tiền không tự nhiên mất đi.
* **Tính Nhất Quán (Consistency):** Thuật toán ARIES với nguyên lý **"Lặp lại lịch sử" (Repeating History)** đảm bảo database luôn được đưa về trạng thái vật lý nhất quán trước khi thực hiện các thao tác Undo logic, ngăn chặn các lỗi cấu trúc trang dữ liệu.
### 3. Tầm Quan Trọng Của Cấu Trúc Nội Bộ
Quá trình khôi phục phụ thuộc hoàn toàn vào các cấu trúc dữ liệu tinh vi:
* **LSN (Log Sequence Number):** Là "nhịp tim" của hệ thống, giúp sắp xếp thứ tự các sự kiện.
* **Checkpoint (Fuzzy):** Giúp hệ thống không phải duyệt lại log từ "kỷ phấn trắng", đảm bảo thời gian phục hồi (RTO) nằm trong giới hạn cho phép.
* **ATT & DPT:** Hai bảng quan trọng giúp SQL Server biết chính xác "Ai đang chạy?" và "Cái gì chưa lưu?" ngay khi vừa khởi động lại.
---
## VI. Đánh Giá Về Tình Huống Mô Phỏng
Dựa trên kịch bản thực nghiệm và phương pháp phân tích log (`fn_dblog`, `sp_readerrorlog`) đã thực hiện, ta có thể đánh giá mức độ chuyên sâu của bài thực hành như sau:
### 1. Đánh Giá Mức Độ Thực Hiện
* **Mức độ:** **Chuyên sâu (Advanced / Deep Dive).**
* **Lý do:** Khác với các kiểm thử "hộp đen" (Black-box testing) thông thường chỉ nhìn vào kết quả dữ liệu cuối cùng, bài thực hành này đã thực hiện kiểm thử "hộp trắng" (White-box testing). Chúng ta đã đi sâu vào nội tại của Database Engine để nhìn thấy từng bản ghi Log, từng dòng Compensation và cơ chế Checkpoint.
### 2. Giá Trị Của Bài Thực Hành
Tình huống mô phỏng này đã giải quyết triệt để các mục tiêu học thuật đặt ra:
1. **Trực quan hóa Lý thuyết:** Nó chuyển hóa các khái niệm trừu tượng trong sách giáo khoa (Tập S, Tập S', Undo, Redo) thành các bằng chứng cụ thể (dòng log `COMMIT`, dòng log `COMPENSATION`).
2. **Chứng minh cơ chế ARIES:**
* Việc thấy SQL Server Redo cả giao dịch chưa commit ($T_B$) trong log chính là bằng chứng thép cho nguyên lý **"Repeating History"** của ARIES, điểm khác biệt lớn nhất so với lý thuyết Undo/Redo nguyên bản.
* Việc thấy dòng `COMPENSATION` chứng minh cơ chế **"Logging during Undo"**, giúp hệ thống chống chịu được các lỗi sập nguồn lặp lại.
3. **Hiểu rõ vai trò quản trị:** Qua bài lab, ta thấy rõ tầm quan trọng của việc cấu hình **Recovery Model (Full)** và việc **Backup Log** để ngăn chặn việc mất mát dữ liệu lịch sử (Log Truncation).
**Kết luận chung:**
Bài thực hành không chỉ dừng lại ở việc xác nhận SQL Server có khả năng tự phục hồi, mà còn cung cấp một cái nhìn toàn diện, giải thích **"Tại sao"** và **"Làm thế nào"** nó thực hiện được điều đó dựa trên nền tảng lý thuyết Undo/Redo Logging và thuật toán ARIES. Đây là kiến thức nền tảng thiết yếu cho bất kỳ chuyên gia quản trị cơ sở dữ liệu (DBA) hoặc kỹ sư dữ liệu nào.
---
---
---
## Trong SQL Server, một Database bắt buộc phải được lưu trữ dưới dạng các file trên ổ cứng. Có **3 loại file chính** bạn cần nắm rõ: **MDF**, **LDF**, và **NDF**.
### 1. File MDF (Primary Data File) - "Kho chứa hàng chính"
* **Tên đầy đủ:** Master Data File.
* **Đuôi mở rộng:** `.mdf`
* **Số lượng:** **Bắt buộc có 1 và chỉ 1** file này cho mỗi database.
* **Tác dụng:**
* Đây là file quan trọng nhất. Nó chứa thông tin khởi động của database và trỏ đến các file khác.
* Nó chứa **Dữ liệu thực tế** (Tables, Rows, Columns, Stored Procedures, Views...).
* Khi bạn `SELECT *`, SQL Server chủ yếu đọc dữ liệu từ file này (sau khi đã nạp lên RAM).
* **Trong ví dụ Recovery trước:** Đây là nơi lưu trạng thái cuối cùng của tài khoản ngân hàng (Balance = 1000 hay 2000).
### 2. File LDF (Transaction Log File) - "Cuốn nhật ký giao dịch"
* **Tên đầy đủ:** Log Data File.
* **Đuôi mở rộng:** `.ldf`
* **Số lượng:** **Bắt buộc có ít nhất 1**, nhưng có thể có nhiều file.
* **Tác dụng:**
* **Không chứa dữ liệu dạng bảng** để bạn Select.
* Chứa **lịch sử thay đổi**. Nó ghi lại chi tiết: *"Lúc 10:00:01, User A update ID 1 từ 1000 lên 2000"*.
* Đây là trái tim của cơ chế **Write-Ahead Logging (WAL)** và **Recovery**.
* **Tại sao nó quan trọng?**
* Tốc độ ghi vào LDF rất nhanh (ghi tuần tự - Sequential Write).
* Khi sập điện, dữ liệu trong MDF có thể sai, nhưng LDF (nếu chưa hỏng ổ cứng) sẽ chứa đầy đủ bằng chứng để thực hiện **Redo/Undo**.
### 3. File NDF (Secondary Data File) - "Kho chứa hàng phụ"
* **Tên đầy đủ:** Secondary (Next) Data File.
* **Đuôi mở rộng:** `.ndf`
* **Số lượng:** Không bắt buộc (0 hoặc nhiều file).
* **Tác dụng:**
* Khi database quá lớn, file `.mdf` phình to quá mức, bạn có thể tạo thêm các file `.ndf` để chứa dữ liệu.
* **Phân tải (Performance):** Bạn có thể để `.mdf` ở ổ cứng C, và `.ndf` ở ổ cứng D. Giúp SQL Server đọc/ghi dữ liệu song song trên nhiều ổ đĩa, tăng tốc độ.
---
### So sánh hình tượng để dễ hiểu
Hãy tưởng tượng Database là một **Cửa hàng Kế toán**:
| Loại File | Hình tượng thực tế | Vai trò |
| :--- | :--- | :--- |
| **MDF** | **Quyển Sổ Cái (Ledger)** | Ghi lại số dư hiện tại. Ví dụ: "A đang có 2000$". Đây là cái sếp nhìn vào cuối tháng. |
| **LDF** | **Camera an ninh + Hóa đơn** | Ghi lại hành động. Ví dụ: "Lúc 8h, A nộp 1000$". Nếu Sổ Cái bị rách hoặc viết sai (Crash), ta tua lại Camera (LDF) để viết lại Sổ Cái. |
| **NDF** | **Quyển Sổ Cái tập 2, tập 3...** | Khi quyển sổ chính (MDF) viết hết giấy, ta mua thêm quyển phụ để viết tiếp. |
---
### Chúng được lưu ở đâu? (Vị trí vật lý)
#### 1. Mặc định (Không khuyến nghị cho Production)
Khi bạn cài SQL Server kiểu "Next -> Next -> Finish", tất cả các file này thường chui vào ổ C (ổ cài hệ điều hành):
* Đường dẫn: `C:\Program Files\Microsoft SQL Server\MSSQL...\MSSQL\DATA\`
* **Rủi ro:** Nếu ổ C bị đầy (do Windows update, log file phình to), SQL Server sẽ đứng hình (Crash). Nếu ổ C hỏng, mất cả Win lẫn Data.
#### 2. Mô hình chuyên nghiệp (Best Practice)
Để tối ưu tốc độ và an toàn cho Recovery, quản trị viên (DBA) thường tách chúng ra các ổ đĩa vật lý riêng biệt:
* **Ổ đĩa Data (Ví dụ ổ D: SSD):** Chỉ lưu file **.mdf** và **.ndf**.
* *Lý do:* Dữ liệu cần đọc/ghi ngẫu nhiên (Random Access) liên tục.
* **Ổ đĩa Log (Ví dụ ổ E: SSD/HDD):** Chỉ lưu file **.ldf**.
* *Lý do:* Log ghi theo kiểu nối đuôi liên tục (Sequential Write). Để riêng giúp đầu đọc ổ cứng không phải nhảy qua lại, ghi cực nhanh.
* *An toàn:* **Quan trọng nhất**. Nếu ổ D (Data) bị cháy, nhưng ổ E (Log) còn sống -> Bạn có thể khôi phục lại dữ liệu đến giây cuối cùng nhờ Backup Log và file LDF này (gọi là **Tail-Log Backup**).
### Tóm lại mối liên hệ với Recovery
Quá trình Recovery (Analysis -> Redo -> Undo) chính là quá trình:
1. Đọc file **.ldf** (Nhật ký) để xem chuyện gì đã xảy ra.
2. Sửa lại thông tin trong file **.mdf** (Sổ cái) cho khớp với nhật ký.
Nếu bạn xóa file `.ldf` khi SQL đang chạy -> Database sẽ hỏng ngay lập tức (Suspect Mode) vì mất khả năng đảm bảo tính nhất quán.