最低水位線是指在 wal(write ahead log)預寫日誌這種設計模式中,標記在這個位置之前的日誌可以被丟棄。
wal(write ahead log)預寫日誌維護了對於儲存的每次更新,隨著時間不斷增長,這個日誌檔案會變得無限大。segmented log 分割日誌這種設計模式可以讓我們每次只處理乙個更小的檔案,但是日誌如果不清理,會無休止增長以至於硬碟被佔滿。
最低水位線這種設計模式會告訴系統哪一部分的日誌可以被刪除了,即在最低水位線之前的所有日誌可以被清理掉。一般的方式是,程式內有乙個執行緒執行乙個定時任務,不斷地檢查哪一部分的日誌可以被清理並且刪除這些日誌檔案。
this.logcleaner = newlogcleaner(config);
this.logcleaner.startup();
這裡的 logcleaner 可以用定時任務實現:
public void startup()
private void schedulelogcleaning() , config.getcleantaskintervalms(), timeunit.milliseconds);
}
大部分的分布式一致性系統(例如 zookeeper(zab 簡化 paxos協議),etcd(raft協議)),都實現了快照機制。在這種機制下,他們的儲存引擎會定時的進行全量快照,並且記錄下快照對應的日誌位置,將這個位置作為最低水位線。
//進行快照
public snapshot takesnapshot()
當生成了快照並成功儲存到了磁碟上,對應的最低水位線將用來清理老的日誌:
//根據位置獲取這個位置之前的所有日誌檔案
listgetsegmentsbefore(long snapshotindex)
}return markedfordeletion;
}
定時任務位於datadircleanupmanager
的start
方法:
public void start()
//檢查定時間隔有效性
if (purgeinterval <= 0)
//啟動定時任務
timer = new timer("purgetask", true);
timertask task = new purgetask(datalogdir, snapdir,snapretaincount);
timer.scheduleatfixedrate(task, 0, timeunit.hours.tomillis(purgeinterval));
purgetaskstatus = purgetaskstatus.started;
}
核心方法為purgetxnlog
的purge
方法:
public static void purge(file datadir, file snapdir, int num) throws ioexception
filetxnsnaplog txnlog = new filetxnsnaplog(datadir, snapdir);
//統計檔案數量
listsnaps = txnlog.findnvalidsnapshots(num);
int numsnaps = snaps.size();
if (numsnaps > 0)
}static void purgeoldersnapshots(filetxnsnaplog txnlog, file snapshot)
public boolean accept(file f)
if (retainedtxnlogs.contains(f))
long fzxid = util.getzxidfromname(f.getname(), prefix);
//根據檔名稱代表的zxid,過濾出要刪除的檔案
return fzxid < leastzxidtoberetain;}}
//篩選出符合條件的 log 檔案和 snapshot 檔案
file logs = txnlog.getdatadir().listfiles(new myfilefilter(prefix_log));
listfiles = new arraylist<>();
if (logs != null)
file snapshots = txnlog.getsnapdir().listfiles(new myfilefilter(prefix_snapshot));
if (snapshots != null)
//進行刪除
for (file f : files) }}
那麼是什麼時候 snapshot 呢?檢視syncrequestprocessor
的run
方法,這個方法時處理請求,處理請求的時候記錄操作日誌到 log 檔案,同時在有需要進行 snapshot 的時候進行 snapshot:
public void run() else catch (exception e) finally
}}.start();}}
} //省略其他
}} catch (throwable t)
}
resetsnapshotstats()
設定隨機起始位,避免集群內所有例項同時進行 snapshot:
private void resetsnapshotstats()
shouldsnapshot()
根據啟動時設定的隨機起始位以及配置,判斷是否需要 snapshot
private boolean shouldsnapshot()
在某些系統中,日誌不是用來更新系統的狀態,可以在一段時間之後刪除,並且不用考慮任何子系統這個最低水位線之前的是否可以刪除。例如,kafka 預設保留 7 天的 log,rocketmq 預設保留 3 天的 commit log。
在defaultmeesagestore
的addscheduletask()
方法中,定義了清理的定時任務:
private void addscheduletask()
}, 1000 * 60, this.messagestoreconfig.getcleanresourceinterval(), timeunit.milliseconds);
//忽略其他定時任務
}private void cleanfilesperiodically()
我們這裡只關心清理訊息儲存檔案,即defaultmessagestore
的deleteexpiredfiles
方法:
private void deleteexpiredfiles() else if (spacefull) }}
public int deleteexpiredfilebytime(final long expiredtime,
final int deletefilesinterval,
final long intervalforcibly,
final boolean cleanimmediately)
//如果配置了刪除檔案時間間隔,則需要等待
if (deletefilesinterval > 0 && (i + 1) < mfslength) catch (interruptedexception e)
}} else
} else }}
//從檔案列表裡面裡將本次刪除的檔案剔除
deleteexpiredfile(files);
return deletecount;
}
每日一刷,輕鬆提公升技術,斬獲各種offer: 請問你知道分布式系統設計模式的分割日誌思想麼?
將大檔案切分為更容易處理的多個更小的檔案。單一的日誌檔案可能會增長到很大,並且在程式啟動時讀取從而成為效能瓶頸。老的日誌需要定時清理,但是對於乙個大檔案進行清理操作很費勁。將單一日誌切分為多個,日誌在達到一定大小時,會切換到新檔案繼續寫。寫入日誌 public long writeentry wal...
分布式 分布式系統的設計
在計算機領域,當單機效能達到瓶頸時,一般有兩種方式解決效能問題 而分布式系統的設計說白了就是 如何合理將乙個系統拆分成多個子系統部署到不同機器上。講設計方法前,先介紹分布式系統的特性 1 分布性 空間中隨機分布。這些計算機可以分布在不同的機房,不同的城市,甚至不同的國家。2 對等性 分布式系統中的計...
分布式系統設計準則
該文章需要整理 2015.3.13修改 分布式系統通常服務大請求 維護著大資料 快速響應 長時間可用。設計分布式後台服務需要考慮的東西很多,本文給出一些常用的設計準則,以備檢視。效能 快速響應 低延遲 可靠性 系統可靠就是同樣的請求返回同樣的資料 更新能夠持久化 資料不會丟失 可管理性 便於運維,整...