elasticsearch index之Translog


跟大多數分布式系統一樣,es也通過臨時寫入寫操作來保證數據安全。因為lucene索引過程中,數據會首先據緩存在內存中直到達到一個量(文檔數或是占用空間大小)才會寫入到磁盤。這就會帶來一個風險,如果在寫入磁盤前系統崩潰,那么這些緩存數據就會丟失。es通過translog解決了這個問題,每次寫操作都會寫入一個臨時文件translog中,這樣如果系統需要恢復數據可以從translog中讀取。本篇就主要分析translog的結構及寫入方式。

這一部分主要包括兩部分translog和tanslogFile,前者對外提供了對translogFile操作的相關接口,后者則是具體的translogFile,它是具體的文件。首先看一下translogFile的繼承關系,如下圖所示:

實現了兩種translogFile,它們的最大區別如名字所示就是寫入時是否緩存。FsTranslogFile的接口如下所示:

每一個translogFile都會有一個唯一Id,兩個非常重要的方法add和write。add是添加對應的操作,這些操作都是在translog中定義,這里寫入的只是byte類型的文件,不關注是何種操作。所有的操作都是順序寫入,因此讀取的時候需要一個位置信息。add方法代碼如下所示:

 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文件中。代碼如下:

    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代碼如下所示:

    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是具體的文件抽象,提供了對於文件的具體操作。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM