`
ahuaxuan
  • 浏览: 640651 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

深入浅出jcr之十 redolog 和 recovery.docx

阅读更多
作者:ahuaxuan

在前面的文章中,我们对jackrabbit做索引的流程有了较为深刻的认识,这个过程中包含了很多的特性,比如多线程作内存索引啊,文件系统的目录算法啊,文本提取的策略等等,在本文中,ahuaxuan将会继续描述jackrabbit在索引过程中的另一个特性。

Redolog是jackrabbit中保证数据一致性的又一个特色。
本文将会阐述以下几点内容:
1. Redolog的存储方式
2. Redolog和indexes还有deletable这两个文件之前的亲密关系
3. Redolog的恢复方式



Redolog的存储介质。
其实在前面的文章中,我们已经接触过它了,之前我们在执行Action的时候,总是会进入这个方法multiIndex#executeAndLog(Action a):
private Action executeAndLog(Action a)
            throws IOException {
a.	execute(this);
/*从这个代码,我们可以看出,每执行一个Action,都会将这个action追加到redoLog中*/
        redoLog.append(a);
        // please note that flushing the redo log is only required on
        // commit, but we also want to keep track of new indexes for sure.
        // otherwise it might happen that unused index folders are orphaned
        // after a crash.
        if (a.getType() == Action.TYPE_COMMIT || a.getType() == Action.TYPE_ADD_INDEX) {
            redoLog.flush();
            // also flush indexing queue
            indexingQueue.commit();
        }
        return a;
}

从上面的代码中,我们得到了一个重要的信息,redolog有append和flush的功能,没啥好说的,其实就是一个文件操作。

关键的在于什么时候把数据写到磁盘上,这个才是重点。从上面的代码中看到,只有是事务提交Action.TYPE_COMMIT或者添加一个索引目录Action.TYPE_ADD_INDEX的时候才会把redolog中的数据刷到磁盘上。

这里面有两个原因,第一个原因是缓存的问题,由于VolatileIndex的存在,导致很多新的document对应的索引数据其实是放在内存中的,这一点在前面的文章中有详细的描述,那么一旦crash,内存中的数据毫无疑问会消失,索引线程成功的将document的索引数据生成在内存中,该索引线程成功返回,这个时候当机,那么这条索引线程之前所做的操作就白费了,所以就需要在索引线程返回之前,将内存中的对应记录放在文件中。这样即使当机,重启之后依然能否通过redolog恢复之前因为当机而消失的索引数据(如果索引线程在做索引的过程中当机,比如说代码还没有运行到redolog的时候当机,这种case神仙也救不了,认了吧)。

第二个原因是因为indexes文件,在前面的索引合并逻辑中,我们详细的讲过,每次合并都会产生新的目录,那么考虑下面这个场景:
1 VolatileIndex中index数据对应的document超过100个
2创建一个PersistentIndex(同时也创建了一个目录),并将这个新建的目录情况加入到redolog,但是并不将其刷到磁盘
3将VolatileIndex中的数据拷贝到PersistentIndex
4 将这个新的PersistentIndex加入到indexes文件中(该目录保存着所有有效索引目录的列表,一旦机器crash,那么就可以根据这个文件中包含的目录来打开indexreader),虽然说加进去,但是这个时候还没有刷到磁盘上,也只是加在内存中。
5 将这次新增的PersistentIndex加入到redolog。并将redolog的数据加刷到磁盘
6或提交事务(之所以”或”,是因为每次新建目录不一定提交事务,因为一个事务中是可以创建多个目录的),将新增的node索引请求记录到redolog,并将redolog的数据刷到磁盘
7 或进入flush,将indexes在内存中的数据刷到磁盘上。
8 在flush中clear redolog

如果我们将第5,6步去掉,那么在4和7步之间当机,那么新增的这个目录则不会记录在indexes对应的文件中,应用程序则不知道这个目录的存在。这个目录就变成了一个没爹没娘的孩子了。(当然,如果我们加上第5步,那么在4-5直接crash,还是有可能产生孤子目录.不过这样的几率非常小,因为4-5之间几乎没有什么逻辑,但是5-8的步骤中存在很多逻辑,相对来讲crash的可能性大很多)

而如果假设,存在第5步,在5和8之间出现了crash,那么显然,indexes对应在磁盘上的文件中是会有这个新的PersistentIndex所对应的目录存在的。那么这个时候当机也不要紧,redolog重做的时候会找到错误的目录,然后将这个错误的目录销毁。事实上,一次MultiIndex#update方法调用的过程中,假设传进来500个nodeid,那么其实在这次操作中,磁盘上会多出5个目录来(默认的minimergefactor=100),那么一旦第四次出错,导致事务不完整,但是前面4个目录就变成了脏目录了。那么在重启应用的时候应该把这个不完整事务带来的脏目录都删除掉。

讲完redolog的作用,存储类型以及和indexes直接的关系之后,我们来看看如何利用这个redolog进行recovery。

Recovery的逻辑集中在Recovery这个类中,在MultiIndex被创建的时候,会先新建这个类,然后通过读取redolog文件的方式来进行之前当机的recovery操作。其主要逻辑在Recovery#run方法中。
private void run() throws IOException {
        List actions = redoLog.getActions();

        /*找到所有只有开始没有结束的事务,并将其加入到losers这个集合中*/
        for (Iterator it = actions.iterator(); it.hasNext();) {
            MultiIndex.Action a = (MultiIndex.Action) it.next();
            if (a.getType() == MultiIndex.Action.TYPE_START) {
                losers.add(new Long(a.getTransactionId()));
            } else if (a.getType() == MultiIndex.Action.TYPE_COMMIT) {
                losers.remove(new Long(a.getTransactionId()));
            }
        }

        /*找到最后一次成功提交的事务在redolog中的位置*/
        int lastSafeVolatileCommit = -1;
        Set transactionIds = new HashSet();
        for (int i = 0; i < actions.size(); i++) {
            MultiIndex.Action a = (MultiIndex.Action) actions.get(i);
            if (a.getType() == MultiIndex.Action.TYPE_COMMIT) {
                transactionIds.clear();
            } else if (a.getType() == MultiIndex.Action.TYPE_VOLATILE_COMMIT) {
                transactionIds.retainAll(losers);
                // check if transactionIds contains losers
                if (transactionIds.size() > 0) {
                    // found dirty volatile commit
                    break;
                } else {
                    lastSafeVolatileCommit = i;
                }
            } else {
                transactionIds.add(new Long(a.getTransactionId()));
            }
        }

        /*最后一次成功的事务之后的数据遍历一下,找到脏目录,脏目录是指在一次事务中创建的目录,但是事务最终没有被完整执行,那么这些目录应该被删除掉*/
        for (int i = lastSafeVolatileCommit + 1; i < actions.size(); i++) {
            MultiIndex.Action a = (MultiIndex.Action) actions.get(i);
            if (a.getType() == MultiIndex.Action.TYPE_CREATE_INDEX) {
                a.undo(index);
            }
        }

        /*将最后一次正确的事务之前的操作都重新做一遍*/
        for (int i = 0; i < actions.size() && i <= lastSafeVolatileCommit; i++) {
            MultiIndex.Action a = (MultiIndex.Action) actions.get(i);
            switch (a.getType()) {
                case MultiIndex.Action.TYPE_ADD_INDEX:
                case MultiIndex.Action.TYPE_CREATE_INDEX:
                case MultiIndex.Action.TYPE_DELETE_INDEX:
                case MultiIndex.Action.TYPE_DELETE_NODE:
                    // ignore actions by the index merger.
                    // the previously created index of a merge has been
                    // deleted because it was considered dirty.
                    // we are conservative here and let the index merger do
                    // its work again.
                    if (a.getTransactionId() == MultiIndex.Action.INTERNAL_TRANS_REPL_INDEXES) {
                        continue;
                    }
                    a.execute(index);
            }
        }

        // now replay the rest until we encounter a loser transaction
        for (int i = lastSafeVolatileCommit + 1; i < actions.size(); i++) {
            MultiIndex.Action a = (MultiIndex.Action) actions.get(i);
            if (losers.contains(new Long(a.getTransactionId()))) {
                break;
            } else {
                // ignore actions by the index merger.
                if (a.getTransactionId() == MultiIndex.Action.INTERNAL_TRANS_REPL_INDEXES) {
                    continue;
                }
                a.execute(index);
            }
        }

        // now we are consistent again -> flush
        index.flush();
        index.closeMultiReader();
    }

从上面的这段代码可以看出,如果一个事务不完整,那么这个事务的操作将会全部回滚,而事务完整的操作将会被全部重做。
通过这些操作,redolog中的信息被解析出来,并执行,那么就可以找回丢失的数据,并且过滤掉错误的数据。

总结:redolog很重要。
TO BE CONTINUE
分享到:
评论

相关推荐

    SCI+SSCI完整版JCR目录(2018版).docx

    - **2018版JCR**涵盖了科学引文索引扩展版(SCIE)和社会科学引文索引(SSCI)中的期刊,是科研人员和图书馆员评估期刊质量的重要工具之一。 ### 2. Clarivate Analytics 公司简介 - **Clarivate Analytics**是...

    docx4j使用说明.docx

    6. **文件保存**:可以将docx文件保存为压缩的docx文件或解压的JCR格式。 7. **转换与导出**:支持将docx文件转换为HTML或PDF格式。 8. **比较与差异**:提供文件、段落或特定内容的比较和差异化功能。 9. **字体...

    modeshape-jcr-api-3.7.4.Final.zip

    _modeshape-jcr-api-3.7.4.Final.zip_ 是一个包含Modeshape JCR API的版本库,这个API是针对Java Content Repository (JCR) 规范的一个实现。JCR是一个标准,它定义了如何在Java应用程序中访问、存储和管理结构化...

    abdera-jcr-0.4.0-incubating-sources.jar

    标签:abdera-jcr-0.4.0-incubating-sources.jar,abdera,jcr,0.4.0,incubating,sources,jar包下载,依赖包

    SCI发表_投稿信(Cover Letter)、催稿信、response letter、审稿意见书写、文章校样邮件回复等万能模板

    1、cover letter万能模板1.docx 2、Cover Letter万能模板_2 .doc 3、Cover Letter万能模板_3 .doc 4、Cover Letter万能模板_4 .doc 5、Cover Letter万能模板_5 .doc 6、Cover Letter万能模板_6 .doc 7、催稿信+稿件...

    SCI分区.docx

    说分区之前要先了解下几个基本概念,...JCR,Journal Citation Reports,期刊引证报告,汤森路透社每年对SCI收录的期刊进行引用和被引用数据进行统计计算,并以影响因子等指数加以报道形成的报告,用来评价期刊的质量。

    JCR-2.0.jar

    JCR-2.0.JARJCR-2.0.JARJCR-2.0.JARJCR-2.0.JARJCR-2.0.JARJCR-2.0.JARJCR-2.0.JARJCR-2.0.JARJCR-2.0.JARJCR-2.0.JARJCR-2.0.JARJCR-2.0.JARJCR-2.0.JAR

    信息检索与利用题库完整.docx

    - **解析**:深入分析检索课题的核心概念和相关概念是提高检索效率的关键。 20. **主题法的优势**: - **知识点**:主题法的特点和适用场景。 - **解析**:主题法能够及时反映新兴领域的最新研究成果,适合快速...

    大学特品教授聘任合同.docx

    - **科研任务**:需在国内外一流学术期刊发表高质量论文,主持国家级科研项目,完成一定的论文发表数量,例如至少6篇,其中包括JCR一区和二区论文。 - **学科建设任务**:协助申报国家重点学科,组建科研创新团队...

    jcr-0.7.2.1.tar

    本文将深入探讨名为"jcr-0.7.2.1.tar"的工具包,以及如何在Unix环境中有效利用这个工具。 首先,让我们理解"jcr-0.7.2.1.tar"这个文件。这是一个tar归档文件,通常用于在Unix或Linux系统中打包和压缩多个文件。"tar...

    应用特征感知与协同表示的高光谱图像分类方法.docx

    Li等人的联合类内协同表示(JCR)则利用相邻像素的相似光谱信息和空间相关性,对局部区域像素进行均值处理,增强了空间特征的联合。 进一步,Jiang等人引入了空间感知的概念,通过构建像素间空间位置信息的权重矩阵...

    apache-james-mailbox-jcr-0.5-test-sources.jar

    jar包,亲测可用

    apache-james-mailbox-jcr-0.2-m1-sources.jar

    jar包,亲测可用

    Docx4J知识积累.pdf

    1. **打开和创建文档**:可以打开存储在文件系统、SMB/CIFS网络共享、或通过WebDAV的.docx、.pptx和.xlsx文件。此外,它还可以创建全新的这些格式的文档。 2. **编程式操作**:开发者可以通过编程方式对打开的文档...

    jcr_in_action.pdf

    《JCR in Action》一书深入探讨了Java Content Repository(JCR)的各个方面,特别是Apache Jackrabbit作为其开源实现的应用。以下是对该书籍所涵盖的关键知识点的详细解析: ### JCR与Apache Jackrabbit JCR是一...

    jcr-1.0.jar

    JSR-170 API jcr-1.0.jar

    重磅!2022年JCR正式发布(附最新影响因子名单),前600

    2022年JCR正式发布(附最新影响因子名单) 重磅!2022年JCR正式发布(附最新影响因子名单,前600) 重磅!2022年JCR正式发布(附最新影响因子名单) 重磅!2022年JCR正式发布(附最新影响因子名单) 重磅!2022年JCR...

    深入浅出 jackrabbit 1

    《深入浅出 Jackrabbit 1》 Jackrabbit 是一个开源的、实现了 Java Content Repository (JCR) API 的内容管理系统,它允许程序通过统一的方式访问、存储和管理各种数据,包括文本、图像、视频等多媒体信息。这篇...

Global site tag (gtag.js) - Google Analytics