文章摘抄自:windows
translog是elasticsearch的事務日誌文件,它記錄了全部對索引分片的事務操做(add/update/delete),每一個分片對應一個translog文件。緩存
跟大多數分佈式系統同樣,es也經過臨時寫入寫操做來保證數據安全。由於lucene索引過程當中,數據會首先緩存在內存中直到達到一個量(文檔數或是佔用空間大小)纔會寫入到磁盤。這就會帶來一個風險,若是在寫入磁盤前系統崩潰,那麼這些緩存數據就會丟失。es經過translog解決了這個問題,每次寫操做都會寫入一個臨時文件translog中,這樣若是系統須要恢復數據能夠從translog中讀取。本篇就主要分析translog的結構及寫入方式。安全
這一部分主要包括兩部分translog和tanslogFile,前者對外提供了對translogFile操做的相關接口,後者則是具體的translogFile,它是具體的文件。首先看一下translogFile的繼承關係,以下圖所示:
實現了兩種translogFile,它們的最大區別如名字所示就是寫入時是否緩存。FsTranslogFile的接口以下所示:
每個translogFile都會有一個惟一Id,兩個很是重要的方法add和write。add是添加對應的操做,這些操做都是在translog中定義,這裏寫入的只是byte類型的文件,不關注是何種操做。全部的操做都是順序寫入,所以讀取的時候須要一個位置信息。add方法代碼以下所示:app
public Translog.Location add(BytesReference data) throws IOException {
rwl.writeLock().lock();//獲取讀寫鎖,每一個文件的寫入都是順序的。
try {
operationCounter++;
long position = lastPosition;
if (data.length() >= buffer.length) {
flushBuffer();
// we use the channel to write, since on windows, writing to the RAF might not be reflected
// when reading through the channel
data.writeTo(raf.channel());//寫入數據
lastWrittenPosition += data.length();
lastPosition += data.length();//記錄位置
return new Translog.Location(id, position, data.length());//返回由id,位置及長度肯定的操做位置信息。
}
if (data.length() > buffer.length - bufferCount) {
flushBuffer();
}
data.writeTo(bufferOs);
lastPosition += data.length();
return new Translog.Location(id, position, data.length());
} finally {
rwl.writeLock().unlock();
}
}
這是SimpleTranslogFile寫入操做,BufferedTransLogFile寫入邏輯基本相同,只是它不會馬上寫入到硬盤,先進行緩存。另外TranslogFile還提供了一個快照的方法,該方法返回一個FileChannelSnapshot,能夠經過它next方法將translogFile中全部的操做都讀出來,寫入到一個shapshot文件中。代碼以下:elasticsearch
public FsChannelSnapshot snapshot() throws TranslogException {
if (raf.increaseRefCount()) {
boolean success = false;
try {
rwl.writeLock().lock();
try {
FsChannelSnapshot snapshot = new FsChannelSnapshot(this.id, raf, lastWrittenPosition, operationCounter);
snapshot.seekTo(this.headsuccess = true;
returnerSize);
snapshot;
} finally {
rwl.writeLock().unlock();
}
} catch (FileNotFoundException e) {
throw new TranslogException(shardId, "failed to create snapshot", e);
} finally {
if (!success) {
raf.decreaseRefCount(false);
}
}
}
return null;
}
TransLogFile是具體文件的抽象,它只是負責寫入和讀取,並不關心讀取和寫入的操做類型。各類操做的定義及對TransLogFile的定義到在Translog中。它的接口以下所示:
這裏的寫入(add)就是一個具體的操做,這是一個外部調用接口,索引、刪除等修改索引的操做都會構造一個對應的Operation在對索引進行相關操做的同時調用該方法。這裏還要着重說明一下makeTransientCurrent方法。操做的寫入時刻進行,可是根據配置TransLogFile超過限度時須要刪除從新開始一個新的文件。所以在transLog中存在兩個TransLogFile,current和transient。當須要更換時須要經過讀寫鎖確保單線程操做,將current切換到transient上來,而後刪除以前的current。代碼以下所示:分佈式
public void revertTransient() {
FsTranslogFile tmpTransient;
rwl.writeLock().lock();
try {
tmpTransient = trans;//交換
this.trans = null;
} finally {
rwl.writeLock().unlock();
}
logger.trace("revert transient {}", tmpTransient);
// previous transient might be null because it was failed on its creation
// for example
if (tmpTransient != null) {
tmpTransient.close(true);
}
}
translog中定義了index,create,delete及deletebyquery四種操做它們都繼承自Operation。這四種操做也是四種可以改變索引數據的操做。operation代碼以下所示:ide
static interface Operation extends Streamable {
static enum Type {
CREATE((byte) 1),
SAVE((byte) 2),
DELETE((byte) 3),
DELETE_BY_QUERY((byte) 4);
private final byte id;
private Type(byte id) {
this.id = id;
}
public byte id() {
return this.id;
}
public static Type fromId(byte id) {
switch (id) {
case 1:
return CREATE;
case 2:
return SAVE;
case 3:
return DELETE;
case 4:
return DELETE_BY_QUERY;
default:
throw new ElasticsearchIllegalArgumentException("No type mapped for [" + id + "]");
}
}
}
Type opType();
long estimateSize();
Source getSource();
}
tanslog部分就是實時記錄全部的修改索引操做確保數據不丟失,所以它的實現上不上很是複雜。性能
總結: TransLog主要做用是實時記錄對於索引的修改操做,確保在索引寫入磁盤前出現系統故障不丟失數據。tanslog的主要做用就是索引恢復,正常狀況下須要恢復索引的時候很是少,它以stream的形式順序寫入,不會消耗太多資源,不會成爲性能瓶頸。它的實現上,translog提供了對外的接口,translogFile是具體的文件抽象,提供了對於文件的具體操做。this