hbase snapshot源码分析

snapshot操作在硬盘上形式:

/hbase/.snapshots
       /.tmp                <---- working directory
       /[snapshot name]     <----- completed snapshot

当snapshot完成时的形式展示:

     /hbase/.snapshots/[snapshot name]
                .snapshotinfo          <--- Description of the snapshot
                .tableinfo             <--- Copy of the tableinfo
               /.logs
                     /[server_name]
                         /... [log files]
                      ...
                /[region name]           <---- All the region's information
                .regioninfo              <---- Copy of the HRegionInfo
                   /[column family name]
                       /[hfile name]     <--- name of the hfile in the real region
                       ...
                   ...

snapshot基本步骤:

1.执行前会枷锁操作,不允许删除添加操作;

2.在hdfs在创建指定目录,写入相关的信息进去;

3.刷新memstore中的数据到hfile,

4.为hfile文件创建引用指针.

以下是大体的代码流程。

hbaseAdmin执行发起的snapshot:

    public void snapshot(final String snapshotName, final TableName tableName, SnapshotDescription.Type type) throws IOException,       SnapshotCreationException, IllegalArgumentException {
        SnapshotDescription.Builder builder = SnapshotDescription.newBuilder();
        builder.setTable(tableName.getNameAsString());
        builder.setName(snapshotName);
        builder.setType(type);
        snapshot(builder.build());
    }

执行快照并等待服务器完成该快照(阻止)。HBase实例一次只能有一个快照,或者结果可能是未定义(你可以告诉多个HBase集群同时快照,但只有一个在单个群集同时)。

    public void snapshot(SnapshotDescription snapshot) throws IOException, SnapshotCreationException, IllegalArgumentException {
        // actually take the snapshot
        SnapshotResponse response = takeSnapshotAsync(snapshot);

MasterRpcService:异步触发并完成一次snapshot:

        `master.snapshotManager.takeSnapshot(snapshot);`

SnapshotManager类:完成一次snapshot需要根据表的状态:disabled或者enabled

    if (assignmentMgr.getTableStateManager().isTableState(snapshotTable, ZooKeeperProtos.Table.State.ENABLED)) {
            LOG.debug("Table enabled, starting distributed snapshot.");
            snapshotEnabledTable(snapshot);
            LOG.debug("Started snapshot: " + ClientSnapshotDescriptionUtils.toString(snapshot));
        }
        // For disabled table, snapshot is created by the master
        else if (assignmentMgr.getTableStateManager().isTableState(snapshotTable, ZooKeeperProtos.Table.State.DISABLED)) {
            LOG.debug("Table is disabled, running snapshot entirely on master.");
            snapshotDisabledTable(snapshot);
            LOG.debug("Started snapshot: " + ClientSnapshotDescriptionUtils.toString(snapshot));
        } 

        private synchronized void snapshotEnabledTable(SnapshotDescription snapshot) throws HBaseSnapshotException {
        // setup the snapshot
        prepareToTakeSnapshot(snapshot);

        // Take the snapshot of the enabled table
        EnabledTableSnapshotHandler handler = new EnabledTableSnapshotHandler(snapshot, master, this);
        snapshotTable(snapshot, handler);
    }

enabled状态下执行表的snapshot:

        // setup the snapshot
        准备工作
        prepareToTakeSnapshot(snapshot);

        // Take the snapshot of the enabled table
        EnabledTableSnapshotHandler handler = new EnabledTableSnapshotHandler(snapshot, master, this);
        开始执行snapshot
        snapshotTable(snapshot, handler);
    }

snapshot开始之前的设置准备:检查是否有一个在运行的snapshot工作以及还原snapshot工作的请求存在。#

        // make sure we aren't already running a snapshot 
        if (isTakingSnapshot(snapshot)) {
            SnapshotSentinel handler = this.snapshotHandlers.get(snapshotTable);
            throw new SnapshotCreationException("Rejected taking " + ClientSnapshotDescriptionUtils.toString(snapshot) + " because we are already running another snapshot " + (handler != null ? ("on the same table " + ClientSnapshotDescriptionUtils.toString(handler.getSnapshot())) : "with the same name"), snapshot);
        }

        // make sure we aren't running a restore on the same table
        if (isRestoringTable(snapshotTable)) {
            SnapshotSentinel handler = restoreHandlers.get(snapshotTable);
            throw new SnapshotCreationException("Rejected taking " + ClientSnapshotDescriptionUtils.toString(snapshot) + " because we are already have a restore in progress on the same snapshot " + ClientSnapshotDescriptionUtils.toString(handler.getSnapshot()), snapshot);
        }

        try {
            // delete the working directory, since we aren't running the snapshot. Likely leftovers
            // from a failed attempt.
            fs.delete(workingDir, true);

            // recreate the working directory for the snapshot
            if (!fs.mkdirs(workingDir)) {
                throw new SnapshotCreationException("Couldn't create working directory (" + workingDir + ") for snapshot", snapshot);
            }

设置准备工作完成就开始进行snapshot用指定的handler进行snapshot工作:

            handler.prepare();
            this.executorService.submit(handler);
            this.snapshotHandlers.put(TableName.valueOf(snapshot.getTable()), handler);
            ...

TakeSnapshotHandler真正开始处理snapshot操作:

1.将snapshot描述信息写入.snapshotinfo目录

FsPermission perms = FSUtils.getFilePermissions(fs, fs.getConf(), HConstants.DATA_FILE_UMASK_KEY);
        Path snapshotInfo = new Path(workingDir, SnapshotDescriptionUtils.SNAPSHOTINFO_FILE);
        try {
            FSDataOutputStream out = FSUtils.create(fs, snapshotInfo, perms, true);
            try {
                snapshot.writeTo(out);
            } finally {
                out.close();
            }
        }

2.复制表的信息:

snapshotManifest.addTableDescriptor(this.htd);

3.获取hregionserver上的regions以及位置信息 ##:

List<Pair<HRegionInfo, ServerName>> regionsAndLocations;
            if (TableName.META_TABLE_NAME.equals(snapshotTable)) {
                regionsAndLocations = new MetaTableLocator().getMetaRegionsAndLocations(server.getZooKeeper());
            } else {
                regionsAndLocations = MetaTableAccessor.getTableRegionsAndLocations(server.getZooKeeper(), server.getConnection(), snapshotTable, false);
            }

4.开始执行snapshot操作,上面获取到的region信息及位置信息

 // run the snapshot
snapshotRegions(regionsAndLocations);
启动snapshot程序:::

在regionserver上开始snapshot // start the snapshot on the RS所有的snapshot操作的具体细节

    Procedure proc = coordinator.startProcedure(this.monitor, this.snapshot.getName(), this.snapshot.toByteArray(), 

    Lists.newArrayList(regionServers));
    if (proc == null) {
        String msg = "Failed to submit distributed procedure for snapshot '" + snapshot.getName() + "'";
        LOG.error(msg);
        throw new HBaseSnapshotException(msg);
    }

等待snapshot完成:

proc.waitForCompleted();

将下线的region作为disabled处理

// Take the offline regions as disabled
        for (Pair<HRegionInfo, ServerName> region : regions) {
            HRegionInfo regionInfo = region.getFirst();
            if (regionInfo.isOffline() && (regionInfo.isSplit() || regionInfo.isSplitParent())) {
                LOG.info("Take disabled snapshot of offline region=" + regionInfo);
                snapshotDisabledRegion(regionInfo);
            }
        }

5.相关region信息以及servername,用来验证snapshot的有效性

// extract each pair to separate lists
            Set<String> serverNames = new HashSet<String>();
            for (Pair<HRegionInfo, ServerName> p : regionsAndLocations) {
                if (p != null && p.getFirst() != null && p.getSecond() != null) {
                    HRegionInfo hri = p.getFirst();
                    if (hri.isOffline() && (hri.isSplit() || hri.isSplitParent()))
                        continue;
                    serverNames.add(p.getSecond().toString());
                }
            }

6.刷新内存状态,写snapshot-mnifest信息到目录

// flush the in-memory state, and write the single manifest
            status.setStatus("Consolidate snapshot: " + snapshot.getName());
            snapshotManifest.consolidate();

7.开始验证snapshot的有效性

// verify the snapshot is valid
            status.setStatus("Verifying snapshot: " + snapshot.getName());
            verifier.verifySnapshot(this.workingDir, serverNames);

8.完成snapshot,转移目录等

// complete the snapshot, atomically moving from tmp to .snapshot dir.
completeSnapshot(this.snapshotDir, this.workingDir, this.fs);
msg = "Snapshot " + snapshot.getName() + " of table " + snapshotTable + " completed";
status.markComplete(msg);
LOG.info(msg);
metricsSnapshot.addSnapshot(status.getCompletionTimestamp() - status.getStartTime());
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,423评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,147评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,019评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,443评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,535评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,798评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,941评论 3 407
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,704评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,152评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,494评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,629评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,295评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,901评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,742评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,978评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,333评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,499评论 2 348

推荐阅读更多精彩内容