ES 中translog是存储于磁盘上的文件,每个ES分片都会一个translog,所以translog的存储路径就位于分片数据目录下。如下图所示:
- 1:索引名称
- 2:分片名称
- 3存放translog文件的目录
Translog目录下有2种格式的文件,tlog后缀的文件和ckp后缀的文件。
translog中存储了ES的操作记录,具体的说是ES还没落盘的数据的操作记录。因此不难看出translog的作用就是保证ES数据不丢失。为了保证性能,插入ES的数据并不会立刻落盘,而是首先存放在内存当中,等到条件成熟后触发flush操作,内存中的数据才会被写入到磁盘当中。如果ES服务器突然断电,那么停留在内存中还没来得及写入磁盘中的数据是不是就丢失了呢?还好translog保留了这些数据的操作日志,在ES服务重启的时候,会读取translog,恢复这部分数据。
经过以上的简要概述,相信大家已经对translog有了个大概的了解。如果再深入的思考,我们可能会引出更多的疑问。
1、 tlog,ckp文件中记录的是什么信息呢?为什么会有多个文件编号?
2、 ES是如何保证translog的完整行的?校验机制如何?
3、 如果translog也损坏了怎么办,数据还能恢复吗?
带着这些问题,我们去源码中寻找答案。
translog创建和读取入口位于InternalEngine 类的构造函数中。InternalEngine实例管理一个分片。new 一个InternalEngine相应的就是建立一个实例来管理ES中的一个分片。
以上是InternalEngine构造函数中我们需要关注的代码。CreateWriter函数用于生成一个IndexWriter实例,这是Lucene的类,这个实例用于管理一个lucene索引,众所周知ES底层是基于Lucene实现的。而ES的一个分片就是一个Lucene索引了。在创建IndexWriter同时会创建SegmentInfos的实例去管理该分配的段信息。
接下来就是openTranslog去将创建translog或读取已经存在的translog。
该方法一开始就做了一个校验:
我们需要知道,如果一个分片已经被创建,那么它一个会拥有自己的translog。上图的代码表示当ES打开一个分片的时候,它会去IndexWriter实例中加载包含translog信息的对象generation(值得注意的是这里的translog信息是从indexWriter中加载,即来自于segment中,而不是translog.tlog文件)。如果这个对象为空,或者其中的translogUUID为空,说明这个分片没有对应的translog,这是不被允许的,ES会抛出异常终止操作。generation中包含了translog的什么信息呢?看下图:
里面仅仅包含2个变量:translogUUID唯一标志一个translog,translogFileGeneration表示translog的迭代版本号,所以在ES的运行过程中translog的版本号应该是会不断递增。Translog的文件编号就是这个版本号了。
接着会创建一个Translog对象,这个对象负责管理该分片目录下translog文件的读写,版本管理。下面看看它的创建过程中值得注意的地方:
1:如果没有translogUUID的(创建新分片的情况),随机生成一个UUID。
2:从配置中获取磁盘中translog文件的文件路径。
如果translogGeneration!=null 则表示translog已经存在,我们需要读取translog文件,不过在此之前还需要检验一把。上文提到的.ckp文件就是checkpoint文件,文件中保存了translog的迭代版本号。通过readCheckpoint()获取来读取translog.ckp这里面是最新的版本号。拿到版本号generation后,我们知道那些版本号大于generation的translog文件都是不应该存在的,所以需要被删除(文件的版本号就标志在文件名之中,比如translog-9.tlog的版本号是9)。这种情况的出现是因为新建了新的translog文件,但是版本号写入checkpoint失败了,此时新的translog是空文件,所以是可以删除的。
规整完版本问题后,通过recoverFromFiles方法将目录下的translog-*. tlog文件读取。注意这些文件的版本号是从translogFileGeneration到checkpoint.generation。translogFileGeneration的值是取自段文件中的,而checkpoint.generation的值是取自translog.ckp文件中的。
最后根据translog.ckp中的版本号生产带版本号的ckp文件存档。恢复完历史的translog.tlog后,需要创建一个最新版的translog.tlog文件用于记录新的translog,它的版本后是checkpoint中版本在加1.
到这里说的是已经有历史文件的操作。如果分片是新建的,没有历史translog,那么处理起来就简单粗暴了
直接删除tanslog目录,然后从版本1开始创建ckp,tlog文件。
分析到这里我们已经知道ckp,tlog文件是成对出现的。如translog-5. tlog 和 translog-5.ckp成一对。tanslog-5.tlog记录着日志内容,translog-5.ckp复制记录版本号5,以及日志的偏移量和操作次数。每次打开translog都会经历2个步骤:1、恢复磁盘中保留的translog;2、创建新版本的translog磁盘文件用于记录新的translog. ckp文件负责维护版本,以及日志提交位置。
那么如何保证translog文件的完整性的呢?
- 1、 前4个字节校验
.tlog文件的前4个字节应该固定是0x3FD76C17,如果校验发现不是该值,说明文件已经损坏。 - 2、 translogUUID校验
前面已经介绍过translogUUID,它是一个translog的唯一标志。当一个新的translog被创建的时候会生成一个translogUUID,它会被保存在translog*.tlog文件中,随后translogUUID会被commit到段文件中。因此段文件中的translogUUID应该与tlog中一致。此处的校验正是校验段文件中读取的translogUUID是否与tlog中保存的一致,如果不一致就会抛异常。
那么如果translog确实已经损坏了该怎么办呢?
- 1、 首先会将其对应的分片设置为失败状态并且拒绝分片该分片。
- 2、 查看该分片是否在其他节点存在备份,如果存在的话就拷贝备份来恢复。
- 3、 如果不存在可用备份分片,那么就需要用elasticsearch-translog工具来修复了。修复命令如下:
$ bin/elasticsearch-translog truncate -d /var/lib/elasticsearchdata/nodes/0/indices/P45vf_YQRhqjfwLMUvSqDw/0/translog/
这个工具真是个好东西啊,可以5.x之后的版本才有。