ES-Fastloader工作流程
1. 扫描Hive表数据,计算路由
- MapReduce作业读取Hive表数据
- 根据分片数和扩展因子(EXPANFACTOR)计算数据路由
- 将数据分配到不同的Reducer任务
2. 生成Lucene文件
- 每个Reducer启动一个嵌入式ES节点
- 通过Bulk API将数据写入这个本地ES节点
- ES自动在本地生成Lucene索引文件
- 这些文件存储在ariusFastIndex/data目录下
3. 复制Lucene文件到HDFS
- MapReduce作业完成后,将生成的Lucene文件复制到HDFS
- 记录这些文件的路径,供后续加载使用
4. 通过插件加载Lucene文件到ES
- Server组件为每个ES分片创建加载任务
- 这些任务将HDFS上的Lucene文件复制到目标ES节点
- 通过Plugin插件的API触发加载操作
- Plugin使用indexWriter.addIndexes(indexes)将Lucene文件直接加载到目标分片
核心优势
- 绕过了ES的标准索引API,直接在Lucene层面操作
- 利用MapReduce的并行处理能力,加速索引构建
- 通过直接加载Lucene文件,大大提高了数据导入效率
- 处理主键冲突,确保数据一致性
这种方法比传统的文档索引方式快几个数量级,特别适合大规模数据导入场景。整个过程是高度并行的,多个分片可以同时加载数据,进一步提高效率。
ES-Fastloader工作流程举例
场景
假设我们有一个Hive表order_table,包含以下字段:
order_id:订单ID(主键)customer_name:客户名称product_id:产品IDamount:订单金额order_time:订单时间
我们想将这个表的数据导入到Elasticsearch索引orders_20230101中。
1. 启动MapReduce作业
hadoop jar ES-Fastloader.jar com.didichuxing.datachannel.arius.fastindex.FastIndex \
--template orders \
--time 20230101 \
--hiveDB sales \
--hiveTable order_table \
--key order_id \
--server http://es-server:8080
2. Mapper阶段(FastIndexMapper)
假设表中有这些数据:
order_id=1001, customer_name="张三", product_id=101, amount=100, order_time="2023-01-01 10:00:00"
order_id=1002, customer_name="李四", product_id=102, amount=200, order_time="2023-01-01 11:00:00"
order_id=1003, customer_name="王五", product_id=103, amount=300, order_time="2023-01-01 12:00:00"
Mapper会读取这些记录,并根据order_id计算分片号:
String keyStr = getKeyValue(keyList, hCatRecord); // 提取"1001"
shardNo = CommonUtils.getShardId(keyStr, templateConfig.getReducerNum()); // 计算分片号,如0
context.write(new IntWritable(0), hCatRecord); // 写入Reducer 0
/* 基于es murmur3hash计算docId所属分片 */
public static int getShardId(String docId, int shardsNum) {
int hash = murmur3hash(docId);
return mod(hash, shardsNum);
}
假设我们有2个Reducer,计算结果可能是:
- order_id=1001 → Reducer 0
- order_id=1002 → Reducer 1
- order_id=1003 → Reducer 0
3. Reducer阶段(FastIndexReducer)
Reducer 0处理:
-
启动嵌入式ES节点:
esNode = new ESNode(taskConfig, indexInfo); esNode.init(context); -
创建索引并写入数据:
// 对于order_id=1001的记录 JSONObject jsonObject = transformer.tranform(record.getAll()); // jsonObject = {"order_id":"1001","customer_name":"张三","product_id":101,"amount":100,"order_time":"2023-01-01 10:00:00"} esWriter.bulk("1001", jsonObject); // 对于order_id=1003的记录 jsonObject = {"order_id":"1003","customer_name":"王五","product_id":103,"amount":300,"order_time":"2023-01-01 12:00:00"} esWriter.bulk("1003", jsonObject); -
生成Lucene文件:
- ES将数据写入内存并生成Lucene索引文件
- 文件存储在
ariusFastIndex/data目录
-
复制Lucene文件到HDFS:
- 将
ariusFastIndex/data目录下的文件复制到HDFS路径,如/user/hadoop/es-fastloader/orders_20230101/reducer0/
- 将
Reducer 1执行类似操作,处理order_id=1002的记录,并将Lucene文件复制到/user/hadoop/es-fastloader/orders_20230101/reducer1/。
4. 服务端调度(FastIndexService)
-
创建加载任务:
// 为每个ES分片创建任务 FastIndexLoadDataPo fastIndexLoadDataPo = new FastIndexLoadDataPo(); fastIndexLoadDataPo.setIndexName("orders_20230101"); fastIndexLoadDataPo.setShardNum(0); // 分片0 fastIndexLoadDataPo.setRedcueIds("0,2"); // 使用Reducer 0和2的输出 fastIndexLoadDataPo.setHdfsSrcDir("/user/hadoop/es-fastloader/orders_20230101"); // ...设置其他参数 fastIndexLoadDataPos.add(fastIndexLoadDataPo); -
执行加载任务:
- 调度器定期执行任务
- 将HDFS上的Lucene文件复制到目标ES节点
- 通过HTTP调用Plugin API:
curl "es-node1:9200/lucene/append?indexName=orders_20230101&uuid=abc123&shardId=0&append=/tmp/lucene/reducer0,/tmp/lucene/reducer2&primeKey=order_id"
5. 插件处理(AppendLuceneTransportAction)
-
处理主键冲突:
- 假设ES中已有order_id=1001的文档
- 插件检测到冲突,删除现有文档:
dstIndexWriter.deleteDocuments(srcTerm); // 删除order_id=1001的文档
-
加载Lucene文件:
Directory[] indexes = new Directory[appendDirs.size()]; for (int i = 0; i < appendDirs.size(); i++) { indexes[i] = FSDirectory.open(Paths.get(appendDirs.get(i))); // 打开/tmp/lucene/reducer0目录 } indexWriter.addIndexes(indexes); // 将Lucene文件添加到分片 indexWriter.commit(); // 提交更改
6. 完成索引设置
-
更新索引设置:
indexOpDao.updateSetting("orders_20230101", "routing.rebalance.enable", "all"); indexOpDao.updateSetting("orders_20230101", "number_of_replicas", "1"); -
标记任务完成:
po.setFinish(true); po.setFinishTime(System.currentTimeMillis());
结果
最终,Hive表中的数据被成功导入到Elasticsearch索引orders_20230101中,整个过程通过MapReduce并行处理和直接操作Lucene文件,比传统的文档索引方式快几个数量级。