本文是對SQL Server事務日志的總結,文章有一些內容和知識來源於官方文檔或一些技術博客,本文對引用部分的出處都有標注。
事務日志介紹
在SQL Server中,事務日志是數據庫的重要組件,如果系統出現故障,則可能需要使用事務日志將數據庫恢復到一致狀態。每個SQL Server數據庫都擁有自己的事務日志,用於記錄所有事務以及每個事務對數據庫所做的修改。那么數據庫的哪些操作會記錄在事務日志中呢?具體一點的說,這些操作包括:
· 每個事務的開始和結束。
· 每次數據修改(插入、更新或刪除)。 這包括系統存儲過程或數據定義語言 (DDL) 語句對包括系統表在內的任何表所做的更改。
· 每次分配或釋放區和頁。
· 創建或刪除表或索引。
另外,像SELECT這樣的操作是不會記錄在事務日志當中的。如果你想對事務日志記錄信息有一個直觀的認識,那么你可以在測試環境做一些SELECT、INSERT、UPDATE、DDL等操作,然后使用ApexSQL Log這款工具查看具體的事務日志記錄信息。
USE YourSQLDba;
GO
CREATE TABLE dbo.TEST(ID INT);
GO
INSERT INTO dbo.TEST SELECT 100;
GO
SELECT * FROM dbo.TEST;
GO
UPDATE dbo.TEST SET ID=101;
GO
DELETE FROM dbo.TEST WHERE ID=101;
GO
如上所示,像DDL、DML操作都會記錄在事務日志當中,但是SELECT是不會記錄在事務日志當中(當然SELECT INTO除外,其實SELECT INTO在事務日志里面轉化為了CREATE TABLE形式)。另外,需要注意: 事務日志並不是審計跟蹤。也就是說事務日志並不能完全替代審計跟蹤。它不提供對數據庫做出改變的審計跟蹤;它不保持對數據庫已執行命令的記錄,只有數據如何改變的結果。其實很多對事務日志了解不深入的人都以為事務日志可以代替審計跟蹤(曾經有項目經理想讓我從事務日志當中挖掘出誰誤刪了數據,其實事務日志只會記錄那個賬號刪除了記錄,並不會記錄客戶端信息,所以不能定位到誰刪除了數據)。如下所示,我們多了一個DROP TABLE操作。你會看到跟上面不一樣的結果。
USE YourSQLDba;
GO
CREATE TABLE dbo.TEST(ID INT);
GO
INSERT INTO dbo.TEST SELECT 100;
GO
SELECT * FROM dbo.TEST;
GO
UPDATE dbo.TEST SET ID=101;
GO
DELETE FROM dbo.TEST WHERE ID=101;
GO
DROP TABLE dbo.Test;
GO
這篇博客transactionlog中有一張圖,描述了一個更新操作的流程中,事務日志在這個流程中的位置以及作用。想必看過這張圖后,大家在大腦中會對事務日志的功能作用有一個初步的形象認識。
其實這張圖還包含了很多隱藏的重要信息,下面我們一一來述說一下:
預寫式日志(Write-Ahead Logging)
什么是預寫式日志呢? 其實其核心思想就是在變化的數據寫入到數據庫之前,將相關日志記錄信息先寫入到日志. SQL Server的預寫式日志(Write-Ahead Logging)機制保證修改的描述(例如日志記錄)會在數據本身修改寫入數據文件前寫入,會寫入磁盤上的事務日志文件。它是SQL Server保證事務持久性(Durability)的基本機制。一個日志記錄會包含已提交事務或未提交事務的詳細信息,在數據被事務修改的不同情況下,可能已經寫入數據文件或還沒來得及寫入數據文件,這取決於檢查點是否已發生。
淺談SQL Server中的事務日志(二)----事務日志在修改數據時的角色 這篇博客有深入淺出的介紹(如下所示):
Write-Ahead Logging的核心思想是:在數據寫入到數據庫之前,先寫入到日志.
因為對於數據的每筆修改都記錄在日志中,所以將對於數據的修改實時寫入到磁盤並沒有太大意義,即使當SQL Server發生意外崩潰時,在恢復(recovery)過程中那些不該寫入已經寫入到磁盤的數據會被回滾(RollBack),而那些應該寫入磁盤卻沒有寫入的數據會被重做(Redo)。從而保證了持久性(Durability)。
但WAL不僅僅是保證了原子性和持久性。還會提高性能.
硬盤是通過旋轉來讀取數據,通過WAL技術,每次提交的修改數據的事務並不會馬上反映到數據庫中,而是先記錄到日志.在隨后的CheckPoint和Lazy Writer中一並提交,如果沒有WAL技術則需要每次提交數據時寫入數據庫......
官方文檔SQL Server 事務日志體系結構和管理指南介紹如下(個人對翻譯做了一下調整,也增加了一點點內容):
要了解預寫日志的工作方式,了解如何將修改的數據寫入磁盤很重要。SQL Server維護一個緩沖區緩存(buffer cache),在必須檢索數據時從其中讀取數據頁。 在緩沖區緩存中修改頁后,不會將其立即寫回磁盤;而是將其標記為“臟”數據。在將數據頁物理寫入磁盤之前,這些臟數據可以多次被修改。 對於每次邏輯寫入,都會在日志緩存(log cache)中插入一條事務日志記錄記錄這些修改。在將關聯的臟頁從緩沖區緩存中刪除並寫入磁盤之前,必須將這條些日志記錄寫入磁盤。檢查點進程定期在緩沖區高速緩存中掃描包含來自指定數據庫的頁的緩沖區,然后將所有臟頁寫入磁盤。 CHECKPOINT 可創建一個檢查點,在該點保證全部臟頁都已寫入磁盤,從而在以后的恢復過程中節省時間。
將修改后的數據頁從高速緩沖存儲器寫入磁盤的操作稱為刷新頁。 SQL Server具有一個邏輯,它可以在寫入關聯的日志記錄前防止刷新臟頁。 日志記錄將在提交事務時寫入磁盤。
檢查點作用
檢查點將臟數據頁從當前數據庫的緩沖區高速緩存刷新到磁盤上。這最大限度地減少了數據庫完整恢復時必須處理的活動日志,減少的崩潰恢復需要的時間。其實CheckPoint是為了優化IO和減少Recovery時間 在完整恢復時,需執行下列操作:
§ 前滾系統停止之前尚未刷新到磁盤上的日志記錄修改信息。
§ 回滾與未完成的事務(如沒有 COMMIT 或 ROLLBACK 日志記錄的事務)相關聯的所有修改。
檢查點操作
檢查點在數據庫中執行下列過程:
· 將記錄寫入日志文件,標記檢查點的開始。
· 將為檢查點記錄的信息存儲在檢查點日志記錄鏈內。
· 記錄在檢查點中的一條信息是第一條日志記錄的日志序列號 (LSN),它必須存在才能成功進行數據庫范圍內的回滾。 該 LSN 稱為“最小恢復 LSN”(“MinLSN”)。 MinLSN 是下列各項中的最小者:
o 檢查點開始的 LSN。
o 最早的活動事務起點的 LSN。
o 尚未傳遞給分發數據庫的最早的復制事務起點的 LSN。
o 檢查點記錄還包含所有已修改數據庫的活動事務的列表。
· 如果數據庫使用簡單恢復模式,檢查點則標記在 MinLSN 前重用的空間。
· 將所有臟日志和臟數據頁寫入磁盤。
· 將標記檢查點結束的記錄寫入日志文件。
· 將這條鏈起點的 LSN 寫入數據庫引導頁。
導致檢查點的活動
下列情形下將出現檢查點:
· 顯式執行 CHECKPOINT 語句。 用於連接的當前數據庫中出現檢查點。
· 在數據庫中執行了最小日志記錄操作,例如,在使用大容量日志恢復模式的數據庫中執行大容量復制操作。
· 已經使用 ALTER DATABASE 添加或刪除了數據庫文件。
· 通過 SHUTDOWN 語句或通過停止 SQL Server (MSSQLSERVER) 服務停止了 SQL Server 實例。 任一操作都會在 SQL Server 實例的每個數據庫中生成一個檢查點。
· SQL Server 實例在每個數據庫內定期生成自動檢查點,以減少實例恢復數據庫所需的時間。
· 進行了數據庫備份。
· 執行了需要關閉數據庫的活動。 例如,AUTO_CLOSE 設置為 ON ,並且關閉了數據庫的最后一個用戶連接,或者執行了需要重新啟動數據庫的數據庫選項更改。
事務日志物理結構
SQL Server數據庫中的事務日志可以有一個或多個事務日志文件。當存在多個事務日志文件時,這些日志文件也只能順序調用,並不能並行使用,因此使用多個日志文件並不會帶來性能上的提升(后面內容會展開討論這個)。其實,如果你對ORACLE當中聯機重做日志體系結構非常熟悉的話,多個事務日志文件就相當於多個redo log file,不同的是,ORACLE下面的redo log可以實現多路復用(日志組可以有一個或多個同樣的日志成員redo log file,多個日志成員的原因是防止日志文件組內某個日志文件損壞后及時提供備份,所以同一組的日志成員一般內容信息相同,但是存放位置不同)。一般會將同一組的不同日志成員文件放到不同的磁盤或不同的裸設備上。以提高安全性。SQL Server似乎沒有這個架構設計。另外,ORACLE的REDO 與UNDO在結構設計上是分開的。而SQL Server可以通過事務日志進行REDO和UNDO操作。
事務日志邏輯結構
從邏輯結構上看,SQL Server對於日志文件的管理,是將邏輯上一個ldf文件划分成多個邏輯上的虛擬日志文件(virtual log files,簡稱VLFs).以便於管理。SQL Server事務日志按邏輯運行,就好像事務日志是一串日志記錄一樣。每條日志記錄由一個日志序列號 (LSN) 標識。 每條新日志記錄均寫入日志的邏輯結尾處,並使用一個比前面記錄的 LSN 更高的 LSN。 日志記錄按創建時的串行序列存儲。 每條日志記錄都包含其所屬事務的 ID。對於每個事務,與事務相關聯的所有日志記錄通過使用可提高事務回滾速度的向后指針挨個鏈接在一個鏈中。 虛擬日志文件沒有固定大小,且物理日志文件所包含的虛擬日志文件數不固定。 數據庫引擎在創建或擴展日志文件時動態選擇虛擬日志文件的大小。 數據庫引擎嘗試維護少量的虛擬文件。 在擴展日志文件后,虛擬文件的大小是現有日志大小和新文件增量大小之和。 管理員不能配置或設置虛擬日志文件的大小或數量。但是如果設置日志文件的增量過小,則會產生過多的VLFS,也就是日志文件碎片,過多的日志文件碎片會拖累SQL Server性能.因此,指定合適的日志文件初始大小和增長,是減少日志碎片最關鍵的部分.
事務日志是一種回繞的文件。 例如,假設有一個數據庫,它包含一個分成四個虛擬日志文件的物理日志文件。 當創建數據庫時,邏輯日志文件從物理日志文件的始端開始。 新日志記錄被添加到邏輯日志的末端,然后向物理日志的末端擴張。 日志截斷將釋放記錄全部在最小恢復日志序列號 (MinLSN) 之前出現的所有虛擬日志。 MinLSN 是成功進行數據庫范圍內回滾所需的最早日志記錄的日志序列號。 示例數據庫中的事務日志的外觀與下圖所示相似。
當邏輯日志的末端到達物理日志文件的末端時,新的日志記錄將回繞到物理日志文件的始端。
上面關於事務日志的虛擬日志循環覆蓋使用是否有點眼熟的感覺,這個跟ORACLE下REDO LOG的循環覆蓋使用的理念是一模一樣的。只不過是不同的概念和不同的實現方式。
事務日志功能
事務日志有啥功能呢?關於事務日志的功能,詳細具體內容可以參考官方文檔事務日志 (SQL Server),里面已經詳細介紹了事務日志的幾個功能,在此不做展開。
事務日志支持以下操作:
· 恢復個別的事務。
· 在SQL Server啟動時恢復所有未完成的事務。
· 將還原的數據庫、文件、文件組或頁前滾至故障點。
· 支持事務復制。
· 支持高可用性和災難恢復解決方案: AlwaysOn 可用性組、數據庫鏡像和日志傳送。
事務日志截斷
什么是事務日志截斷呢? 在介紹事務日志截斷前,我們必須先了解一下MinLSN、活動日志(Actvie Log)等概念。
最小恢復LSN(Minimum Recovery LSN(MinLSN))概念
MinLSN是在還未結束的事務記錄在日志中最小的LSN號,MinLSN是下列三者之一的最小值:
· CheckPoint的開始LSN
· 還未結束的事務在日志的最小LSN
· 尚未傳遞給分發數據庫的最早的復制事務起點的 LSN.
從MinLSN到日志的邏輯結尾處,則稱為活動日志(Active Log)。日志文件中從 MinLSN 到最后寫入的日志記錄這一部分稱為日志的活動部分,或者稱為活動日志(Active log)。 這是進行數據庫完整恢復所需的日志部分。 永遠不能截斷活動日志的任何部分。所有的日志記錄都必須從 MinLSN 之前的日志部分截斷。也就是說永遠不能截斷活動日志的任何部分。
下圖顯示了具有兩個活動事務的結束事務日志的簡化版本。 檢查點記錄已壓縮成單個記錄。
LSN 148 是事務日志中的最后一條記錄。 在處理 LSN 147 處記錄的檢查點時,Tran 1 已經提交,而 Tran 2 是唯一的活動事務。 這就使 Tran 2 的第一條日志記錄成為執行最后一個檢查點時處於活動狀態的事務的最舊日志記錄。 這使 LSN 142(Tran 2 的開始事務記錄)成為 MinLSN。
活動日志必須包括所有未提交事務的每一部分。如果應用程序開始執行一個事務但未提交或回滾,將會阻止數據庫引擎推進 MinLSN。 這可能會導致兩種問題:
如果系統在事務執行了許多未提交的修改后關閉,以后重新啟動時,恢復階段所用的時間將比“恢復間隔”選項指定的時間長得多。
因為不能截斷 MinLSN 之后的日志部分,日志可能變得很大。 即使數據庫使用的是簡單恢復模式,這種情況也有可能出現,在簡單恢復模式下,每次執行自動檢查點操作時通常都會截斷事務日志。
日志截斷其實指從SQL Server數據庫的邏輯事務日志中刪除不活動的虛擬日志文件,釋放邏輯日志中的空間以便物理事務日志重用這些空間。 如果事務日志從不截斷,它最終將填滿分配給物理日志文件的所有磁盤空間。 但是,在截斷日志前,必須執行檢查點操作。檢查點將當前內存中已修改的頁(稱為“臟頁”)和事務日志信息從內存寫入磁盤。 執行檢查點時,事務日志的不活動部分將標記為可重用。 此后,日志截斷可以釋放不活動的部分。有關檢查點的詳細信息,請參閱數據庫檢查點 (SQL Server)。
關於日志截斷,必須定期截斷事務日志,防止其占滿分配給物理日志文件的磁盤空間。日志截斷並不減小物理日志文件的大小。 若要減少物理日志文件的物理大小,則必須收縮日志文件。
日志截斷會在下面事件后自動進行截斷:
簡單恢復模式下,在檢查點之后發生。
在完整恢復模式或大容量日志恢復模式下,如果自上一次備份后生成檢查點,則在日志備份后進行截斷(除非是僅復制日志備份)。
CHECKPOINT only truncates the transaction log (marks the VLF for reuse) only in simple recovery model. In Full recovery, you have to take log backup.
實際上,日志截斷會由於多種原因發生延遲。 查詢 sys.databases 目錄視圖的 log_reuse_wait 和 log_reuse_wait_desc 列,了解哪些因素(如果存在)阻止日志截斷。 下表對這些列的值進行了說明:
Log_reuse_wait 值 |
Log_reuse_wait_desc 值 |
說明 |
0 |
NOTHING |
當前有一個或多個可重復使用的虛擬日志文件。 |
1 |
CHECKPOINT |
自上次日志截斷之后,尚未生成檢查點,或者日志頭尚未跨一個虛擬日志文件移動。 (所有恢復模式) |
2 |
LOG_BACKUP |
在截斷事務日志前,需要進行日志備份。 (僅限完整恢復模式或大容量日志恢復模式) |
3 |
ACTIVE_BACKUP_OR_RESTORE |
數據備份或還原正在進行(所有恢復模式)。 |
4 |
ACTIVE_TRANSACTION |
事務處於活動狀態(所有恢復模式): |
5 |
DATABASE_MIRRORING |
數據庫鏡像暫停,或者在高性能模式下,鏡像數據庫明顯滯后於主體數據庫。 (僅限完整恢復模式) |
6 |
REPLICATION |
在事務復制過程中,與發布相關的事務仍未傳遞到分發數據庫。 (僅限完整恢復模式) |
7 |
DATABASE_SNAPSHOT_CREATION |
正在創建數據庫快照。 (所有恢復模式) |
8 |
LOG_SCAN |
發生日志掃描。 (所有恢復模式) |
9 |
AVAILABILITY_REPLICA |
可用性組的輔助副本正將此數據庫的事務日志記錄應用到相應的輔助數據庫。 (完整恢復模式) |
10 |
— |
僅供內部使用 |
11 |
— |
僅供內部使用 |
12 |
— |
僅供內部使用 |
13 |
OLDEST_PAGE |
如果將數據庫配置為使用間接檢查點,數據庫中最早的頁可能比檢查點 LSN 早。 在這種情況下,最早的頁可以延遲日志截斷。 (所有恢復模式) |
14 |
OTHER_TRANSIENT |
當前未使用此值。 |
事務日志收縮
有時候我們監控告警會發現事務日志出現暴增的情況,那么此時就必須對是事務日志進行收縮,不管數據庫處於那種恢復模式,簡單、完整模式。都可以按下面流程進行收縮。
1:查看對應數據庫事務日志的邏輯名稱(name),后續操作需要用到。
SELECT database_id ,
name ,
type_desc ,
physical_name
FROM sys.master_files
WHERE database_id = DB_ID('YourSQLDba')
AND type_desc='LOG'
2: 使用DBCC SQLPERF查看事務日志空間使用情況統計信息:
DBCC SQLPERF (LOGSPACE)
如果對應數據庫的Log Space Used(%)的值較小,那么就可以收縮事務日志。
3:執行類似下面的收縮事務日志文件語句。
USE YourSQLDba;
GO
DBCC SHRINKFILE('YourSQLDba_Log', 128);
如果Log Space Used(%)很小,而收縮效果又不佳,那么一般是因為日志截斷延遲造成,一般可以通過下面腳本檢查原因,大部分情況是因為等待LOG_BACKUP緣故。所以你對事務日志做一次備份后,再進行收縮即可解決。
SELECT name ,
log_reuse_wait ,
log_reuse_wait_desc
FROM sys.databases
WHERE database_id = DB_ID('YourSQLDba');
backup log [YourSQLDba]
to disk = 'M:\DB_BACKUP\LOG_BACKUP\YourSQLDba_[2018-01-11_06h37m26_Thu]_logs.TRN'
with noInit, checksum, name = 'YourSQLDba:15h40: M:\DB_BACKUP\LOG_BACKUP\YourSQLDba_[2018-01-11_06h37m26_Thu]_logs.TRN'
增加事務日志文件
SQL Server數據庫中的事務日志可以有一個或多個事務日志文件,但是即使有多個事務日志文件,也不能並行寫入多個事務日志文件,數據庫引擎還是會串行使用多個事務日志文件。也就是說大多數場景,多個事務日志文件其實並沒有什意義,那么它存在的意義是什么呢?例如,當你當前磁盤告警,事務日志無法繼續增長,你需要在其他磁盤新增一個事務日志文件,讓數據庫繼續順暢運行。個人覺得多個事務日志文件確實是一個很雞肋的東西。Paul S. Randal在“了解SQL Server的日志記錄和恢復”中明確指出:不要創建多個的日志文件,因為它不會導致性能增益。
下面是如何增加一個事務日志文件的樣例:
USE [master]
GO
ALTER DATABASE [YourSQLDba] ADD LOG FILE ( NAME = N'YourSQLDba_Log2', FILENAME = N'D:\SQL_LOG\YourSQLDba_Log1.LDF' , SIZE = 65536KB , MAXSIZE = 55296KB , FILEGROWTH = 10%)
GO
刪除事務日志文件
既然可以增加事務日志文件,那么當然也可以刪除事務日志文件,但是這個刪除操作是有限制的。主日志文件(primary log)是不能刪除的。如果你刪除primary log就會報“不能從數據庫中刪除主數據文件或主日志文件。”,下面我們來測試一下。
准備測試環境如下:
USE master;
GO
CREATE DATABASE [TEST]
CONTAINMENT = NONE
ON PRIMARY
( NAME = N'TEST', FILENAME = N'D:\SQL_DATA\TEST.mdf' , SIZE = 100MB , MAXSIZE = 40GB, FILEGROWTH = 64MB )
LOG ON
( NAME = N'TEST_log' , FILENAME = N'D:\SQL_LOG\TEST_LOG_1.ldf' , SIZE = 20MB , MAXSIZE = 40MB , FILEGROWTH = 10MB),
( NAME = N'TEST_log2', FILENAME = N'D:\SQL_LOG\TEST_LOG_2.ldf' , SIZE = 20MB , MAXSIZE = 20GB , FILEGROWTH = 10MB)
GO
BACKUP DATABASE [TEST] TO DISK = N'D:\DB_BACKUP\Test.bak'
WITH NOFORMAT, NOINIT,
NAME = N'TEST-Full Database Backup',
SKIP, NOREWIND, NOUNLOAD, STATS = 10;
GO
USE TEST;
GO
SELECT * INTO mytest FROM sys.objects;
GO
INSERT INTO mytest
SELECT * FROM mytest
GO 12
DBCC SQLPERF(LOGSPACE)
DBCC LOGINFO('TEST')
注意,此時DBCC LOGINFO顯示FileId=3的日志文件對應的虛擬日志(VLF)的Status為2,此時刪除事務日志文件會提示文件無法刪除,因為Status=2意味着VLF不能被覆蓋和重用。
Status = 2 means that VLF can't be reused (overwritten) at this time and it doesn't necessarily mean that VLF is still active and writing transactions to that VLF. As Jonathan already mentioned, it means that the VLF is waiting for backup/REPL/Mirroring etc..
USE master;
GO
ALTER DATABASE TEST REMOVE FILE TEST_log2
備份事務日志后,你會發現FileId=3的日志文件對應的虛擬日志(VLF)的Status變為了0,那么此時就可以移除事務日志文件了。
BACKUP LOG TEST TO DISK = 'D:\SQL_LOG\Test.Trn'
GO
DBCC LOGINFO('TEST')
GO
USE master;
GO
ALTER DATABASE TEST REMOVE FILE TEST_log2
如果是生產環境或者在上述備份事務日志后,對應日志文件的VLF的狀態仍然為2,那么可以用收縮日志文件和備份事務日志循環處理,直至對應日志文件下所有的VLF狀態全部為0,就可以刪除事務日志文件。
USE TEST;
GO
DBCC SHRINKFILE(TEST_log2);
BACKUP LOG TEST TO DISK = 'D:\SQL_LOG\Test.Trn'
注意,主日志文件(primary log)是不能刪除的,如下測試所示:
USE master;
GO
ALTER DATABASE TEST REMOVE FILE TEST_log
Msg 5020, Level 16, State 1, Line 35
The primary data or log file cannot be removed from a database.
但是當你需要規划存儲路徑、移動事務日志文件時,你可以使用折中的方法將主事務日志文件(primary log)移動到其它目錄。如下所示:
1: 將當前數據庫脫機;
ALTER DATABASE TEST SET OFFLINE;
2: 修改數據庫的事務日志位置
ALTER DATABASE TEST
MODIFY FILE
(
NAME = N'TEST_log'
, FILENAME = N'E:\SQL_LOG\TEST_LOG_1.ldf'
)
3: 手工將事務日志文件移動到上面位置
4:將數據庫聯機操作。
ALTER DATABASE TEST SET ONLINE;
另外,如何判斷那個日志文件是主事務日志文件?目前來說,我只能這樣判斷, sys.master_files當中,file_id最小值對應的日志文件為主事務日志文件。用腳本判斷如下:
SELECT f.database_id AS database_id ,
DB_NAME(f.database_id) AS database_name,
MIN(f.file_id) AS primary_log_id ,
f.type_desc AS type_desc
FROM sys.master_files f
WHERE f.database_id= DB_ID('databasename') AND type = 1
GROUP BY f.database_id,f.type_desc;
另外,你也可以用下面腳本查出哪些數據庫擁有兩個或以上事務日志。
SELECT f.database_id AS database_id ,
d.name AS database_name,
f.type_desc AS type_desc ,
COUNT(*) AS log_count
FROM sys.master_files f
INNER JOIN sys.databases d ON f.database_id = d.database_id
WHERE type = 1
GROUP BY f.database_id ,
f.type_desc,
d.name
HAVING COUNT(*) >= 2;
參考資料:
https://docs.microsoft.com/zh-cn/sql/relational-databases/sql-server-transaction-log-architecture-and-management-guide#physical_arch
https://docs.microsoft.com/zh-cn/sql/relational-databases/logs/the-transaction-log-sql-server#FactorsThatDelayTruncation
https://docs.microsoft.com/zh-cn/sql/relational-databases/logs/database-checkpoints-sql-server
https://technet.microsoft.com/zh-cn/library/2009.02.logging.aspx
http://www.cnblogs.com/CareySon/archive/2012/02/13/2349751.html
http://www.cnblogs.com/CareySon/p/3315041.html
http://www.cnblogs.com/CareySon/archive/2012/02/17/2355200.html