收縮SQL Server日志不是那么簡單的(翻譯)
原文地址: http://rusanu.com/2012/07/27/how-to-shrink-the-sql-server-log/
說明:本文為了更好的說明收縮的過程,在原文翻譯的基礎上增加了一些個人的理解,省略了部分內容,建議大家在閱讀本文時參考原文。
?
一、問題場景 |
我的數據庫日志文件已經增大到200G了,我也嘗試去收縮數據庫,但大小沒有改變,請問該如何減小日志文件的大小?這個問題實際上就是說執行 DBCC ? SHRINKFILE 沒有減小日志文件的大小,到底是什么原因導致的呢?
?
二、準備知識 |
?
1、LSN |
LSN用來標識特定日志在日志文件中位置(詳情請見 什么是LSN:日志序列號 ),它由兩部分組成:一部分用來標識VLF(虛擬日志文件)的序列號,剩下的用來標識該日志在VLF中的具體的位置。
根據LSN不同,日志一般分為兩類:首日志(最新的活動日志序號)和尾日志(保留時間最長的活動日志序號)。隨著數據庫的操作不斷增加(如數據庫中的update操作),首日志LSN序號不斷變化。尾日志的序號只有在日志備份后才會變化。
(圖一)日志文件結構圖
?
2、VLF |
?
你可以通過DCC LOGINFO去分析數據庫LDF中VLF(虛擬日志文件),LDF、VLF、日志的關系是:LDF包括多個VLF,每個VLF中包括多個日志記錄。在VLF中,當事物日志增加時,日志的頭部(首日志)不斷向前移動,日志將占用越來越多的剩余空間,當這個VLF被占滿后,新的日志寫入到其他未被使用的VLF中,這個時候LDF并不會增大。當LDF中沒有可用的VLF時,數據庫會創建一個新的VLF。從而使得LDF文件物理增大,占用更多的磁盤空間。
(圖二)日志增長
三、解決方法詳細闡述 |
|
1、日志的截斷 |
?
上圖演示了首日志向前移動的場景,結合圖一和圖二可以看到,當VLF2的空間被日志填滿后,數據庫擴充LDF文件(向操作系統申請更多的磁盤空間),并在擴充后的LDF中新建一個VLF3用來填充新的日志記錄。盡管VLF1中存在剩余空間,但因為VLF1中存在活動日志(哪怕只有一條),所以數據庫無法利用這個VLF的剩余空間,(詳細原因可以參考這篇文章 什么是LSN:日志序列號 )。
這個時候做日志備份就會發生日志截斷的現象。一般會將截斷理解為"刪除"一些日志記錄(非活動),實際上它只是意味著尾日志的向前移動:尾日志序號會被刷新成 最小的活動日志 序號,而從原來尾日志的位置到新位置之間的空間被標記為"可重新利用"。這個過程并不會減少LDF已占用的磁盤空間。如下圖,整個VLF1的和部分VLF2上的日志(非活動)被截斷了。
日志截斷示意圖(圖三)
隨著事務日志不斷增加,VLF3中日志頭部所在的位置將不斷向前移動,當VLF3的空間被占滿后,數據庫會重新利用VLF1的空間,這種寫入、截斷、再寫入的方式形成一個寫日志的循環。在此期間LDF并不會物理上增大。
日志循環使用示意圖(圖四)
2、為什么日志不能收縮 |
?
現在我們再來看一個日志無法收縮的場景:
圖四中,VLF1中的日志不斷增加,直到VLF1的所有空間都被填滿(如圖五),此時因為沒有發生截斷,尾日志都在VLF2上,且VLF2和VLF3都被標記為不可重新利用,數據庫只能擴充LDF、新建一個VLF4用來記錄新的日志,首日志的位置將出現在VLF4中,整個寫日志的(從圖一到圖四)順序為VLF2——>VLF3——>VLF1——>VLF4。這個過程會導致數據庫的日志文件在物理上增大。
日志增長示意圖(圖五)
這時我們再來截斷事物日志,如上文所說,尾日志的會被更新,最后可能出現尾日志和首日志在同一個VLF上的場景。從日志文件記錄的架構上來看,我們可以將這個過程簡單地理解為:截斷的順序會按照首日志移動的順序移動,從VLF2——>VLF3——>VLF1——>VLF4,最終尾日志和首日志出現在同一個VLF上。
日志截斷示意圖二(圖六)
?
如上圖,這個LDF文件包括3個空的和1個只有小部分活動日志的VLF文件,首日志和尾日志在同一個VLF中,這種情況下,試圖通過DBCC SHRINKFILE是不會減小LDF文件的大小的。
日志文件能被收縮的原因是該文件尾部的數據被清除了,使得該部分空間被釋放,而不是逃過尾部去刪除文件首部或者中間部分的內容。這點與MDF文件不同,MDF文件中的數據是不能被刪除的,只能將文件尾部的數據遷移到其他區域的剩余空間上,然后釋放尾部占用的空間。
在LDF中 ,日志是不能被遷移的,而且也沒有遷移的必要,因為當事物被提交后,日志變為不活動狀態,通過事物日志備份即可將其截斷(特殊情況下日志備份不一定能截斷,如發布訂閱的環境)。
綜上所述,日志文件能被收縮的前提是:日志文件的最后一個VLF必須是free狀態,從后向前推,只要是free狀態的VLF都會被收縮,據此可以估算一個日志文件可以釋放的空間大小。
如下我們看一個實際的例子:
USE ? DBname
DBCC ? loginfo
?
VLF 狀態示意圖(圖七)
?
從上圖可以看到,這個數據庫的日志文件共有13個VLF,其中有前12個處于free狀態,最后1個處于活動狀態,因此,我們可以推斷首日志和尾日志的位置都在這個VLF上。這個時候執行文件收縮將看不到文件減小的效果。
3、如何解決這個問題 |
那么碰到這種情況,該怎么去收縮日志呢:盡可能多的執行一些能夠產生大量日志的操作,這些日志將導致數據庫重新利用startoffset靠前的非活動狀態的VLF,將首日志的位置定位到這個startoffset,然后做一次事務日志備份,將尾日志也遷移到startoffset靠前的非活動狀態的VLF中,如下圖,最后再執行 DBCC ? SHRINKFILE 即可收縮日志文件。
?
日志截斷示意圖三(圖六)
四、重要說明 |
?
前文中一直在說通過日志備份即可解決日志截斷的問題,其實這只是最簡單的場景。在實際環境中可能有很多因素會影響日志的截斷,如:
- 活動的事物日志
日志備份只能截斷非活動的日志,如果一個事物長時間運行,此時備份事物日志將不會引起截斷發生。
- 事物日志分發
事物日志分發中,只有當日志讀取器代理已經讀取完待分發的日志后,日志才能變得非活動狀態。(之前我處理過一個類似問題,大家可以通過這個鏈接看看 http://www.cnblogs.com/i6first/p/3281437.html 。)
- 數據庫鏡像和AlwaysOn
這兩種數據庫技術都需要將日志傳遞到接受端,在傳遞還沒有完成時,日志會一直保留,即使是備份日志也無法截斷。
.NET 使用CouchBase 基礎篇
?
2011年2月,CouchOne和memebase合并后,改名為Couchbase,官網地址(www.couchbase.com)。membase最后一個版本為1.7.2,可在Couchbase的官網下載( http://www.couchbase.com/downloads-all )。
這里不介紹couchbase的安裝,只介紹.NET Client Librarye 使用。
-
獲得CouchBase For Net的SDK有兩種方式
- 通過nuget,Install-Package CouchbaseNetClient
- 通過官網下載 http://www.couchbase.com/communities/net
?
-
開始CouchBase之旅
-
創建項目,這里需要提醒的是, 在vs2010中我們創建類Console應用程序時項目默認使用.NET Framework Client Profile,我們需要手動切換至full .NET Framework 。
?
- 在程序中配置CouchBase,CouchBase提供編碼配置和配置文件配置,當然使用app.config是最靈活的,這里是我們的首選,添加以下信息至你的配置文件,
<?xml ? version = "1.0" ?>
<configuration>
?? <configSections>
? ?? <section ? name = "couchbase" ? type = "Couchbase.Configuration.CouchbaseClientSection, Couchbase" />
?? </configSections>
?? <couchbase>
? ?? <servers ? bucket = "default" ? bucketPassword = "" >
? ? ?? <add ? uri = "http://192.168.0.2:8091/pools" />
? ? ?? <add ? uri = "http://192.168.0.3:8091/pools" />
? ?? </servers>
?? </couchbase>
</configuration>這里使用了集群配置的url列表,當然在你本地調試只需要一個地址,默認CouchBase在安裝時會創建一個沒有密碼的default的緩存桶(bucket),你可以自由修改這塊的信息。(如果對bucket不太明白,請自行google)。
?
- 添加引用
-
using Couchbase; using Couchbase.Configuration; using Couchbase.Extensions; using Enyim.Caching; using Enyim.Caching.Configuration; using Enyim.Caching.Memcached; |
根據實際引用添加引用
?
- 創建實例及使用
var client? = ?new CouchbaseClient (); // 創建實例 client . Store ( StoreMode . Add ,? "somekey" ,? "somevalue" ); //存儲數據 var someValue? = ?client . Get ( "somekey" ) ? as ? string ; //獲取數據 var someValue? = ?client . Get < string >( "somekey" ); //獲取數據 |
以上是簡單基本類型的使用,下面我們介紹一下復雜類型。先申明一個類
[
Serializable
]
|
?
var key?
=
?Guid
.
NewGuid
();
? var beer? = ?client . Get < Beer >( "beer_" ?+ ?key ); |
?
在CouchBase2.0正式版就開始支持json,這一點讓人激動人心。
?
存儲json數據
public
?
static
?
bool
?StoreJson
<
T
>(
this
?
CouchbaseClient client, StoreMode storeMode,?
string
?key, T value
)
?
where
?T?
:
?
class
?
{
|
?
獲取json數據
public
?
static
?T GetJson
<
T
>(
this
?CouchbaseClient client,?
string
?key)
?
where
?T?
:
?
class
?
{
?? ?
|
?
Client使用方法
var key = Guid.NewGuid();
var beer = client.GetJson<Beer>("beer_" + key); |
?
-
檢查和操作結果
官方的說明
For check and set operations, the return values are wrapped in a CasResult instance.? The success of the operation is still determined by a Boolean and detailed failures still require logging.?
var result = client.GetWithCas("foo");
|
?
-
獲取詳細操作結果
如果你需要獲取運行時的詳細信息,你可以使用IoperationResult API方法,下面是官方給的API屬性的說明。
Each of these methods shares its name with a method from the single-value return API, but prefixed with "Execute." For example, Get() becomes ExecuteGet() and Store() becomes ExecuteStore().
?
var getResult?
=
?client
.
ExecuteGet
<
Beer
>(
"beer_heady_topper"
);
|
?
-
配置日志
CouchBase支持Log4Net和Nlog,你可以自己現在或者從CouchBase提供的SDK獲取。
?
Log4Net 配置參考。
<?xml version="1.0" encoding="utf-8"?>
|
更多Log4Net配置可參考: http://logging.apache.org/log4net/release/manual/configuration.html .
?
?
Nlog配置參考
<?xml version="1.0" encoding="utf-8"?>
|
更多Nlog配置可參考: http://nlog-project.org/wiki/Configuration_file
?
?
總結 : 以上信息來源官方的Getting Started,另附一份自己整理的Demo。(通過office word 發布的文檔格式有些變形)
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
