下面的图来自ElasticSearch——刷盘原理流程,这篇文章主要讲的是人工通过
flush
命令把批量写入内存segment的数据刷新进磁盘,不涉及到在translog.log
和Lucene
的数据结构。
通过这个流程知道ES如何把数据刷新进磁盘的,主要是下图的下半部分中fsync部分
图片中主节点同时写入Es缓冲区和translog部分,请看Elasticsearch 8.9 Bulk批量给索引增加数据源码
图片中refresh部分,请看Elasticsearch 8.9 refresh刷Es缓冲区的数据到Lucene,更新segemnt,使数据可见
一、相关API的handler
在ActionModule.java
中
registerHandler.accept(new RestFlushAction());
actions.register(FlushAction.INSTANCE, TransportFlushAction.class);
actions.register(TransportShardFlushAction.TYPE, TransportShardFlushAction.class);
1、接收HTTP请求的hander
public class RestFlushAction extends BaseRestHandler {
@Override
public List<Route> routes() {
return List.of(
new Route(GET, "/_flush"),
new Route(POST, "/_flush"),
new Route(GET, "/{index}/_flush"),
new Route(POST, "/{index}/_flush")
);
}
@Override
public String getName() {
return "flush_action";
}
@Override
public RestChannelConsumer prepareRequest(final RestRequest request, final NodeClient client) throws IOException {
FlushRequest flushRequest = new FlushRequest(Strings.splitStringByCommaToArray(request.param("index")));
flushRequest.indicesOptions(IndicesOptions.fromRequest(request, flushRequest.indicesOptions()));
flushRequest.force(request.paramAsBoolean("force", flushRequest.force()));
flushRequest.waitIfOngoing(request.paramAsBoolean("wait_if_ongoing", flushRequest.waitIfOngoing()));
return channel -> client.admin().indices().flush(flushRequest, new RestToXContentListener<>(channel));
}
}
上面会执行下面这个,至于怎么到这里的,可以看Elasticsearch 8.9 Master节点处理请求源码
/**
* Flush Action.
*/
public class TransportFlushAction extends TransportBroadcastReplicationAction<
FlushRequest,
FlushResponse,
ShardFlushRequest,
ReplicationResponse> {
@Inject
public TransportFlushAction(
ClusterService clusterService,
TransportService transportService,
NodeClient client,
ActionFilters actionFilters,
IndexNameExpressionResolver indexNameExpressionResolver
) {
super(
FlushAction.NAME,
FlushRequest::new,
clusterService,
transportService,
client,
actionFilters,
indexNameExpressionResolver,
TransportShardFlushAction.TYPE, //注意这个
ThreadPool.Names.FLUSH
);
}
//省略代码
}
这里需要注意上面的TransportShardFlushAction.TYPE
这里看一下它的父类TransportBroadcastReplicationAction
public abstract class TransportBroadcastReplicationAction<
Request extends BroadcastRequest<Request>,
Response extends BaseBroadcastResponse,
ShardRequest extends ReplicationRequest<ShardRequest>,
ShardResponse extends ReplicationResponse> extends HandledTransportAction<Request, Response> {
private final ActionType<ShardResponse> replicatedBroadcastShardAction;
public TransportBroadcastReplicationAction(
String name,
Writeable.Reader<Request> requestReader,
ClusterService clusterService,
TransportService transportService,
NodeClient client,
ActionFilters actionFilters,
IndexNameExpressionResolver indexNameExpressionResolver,
ActionType<ShardResponse> replicatedBroadcastShardAction,
String executor
) {
//省略代码
//这里即上面的TransportShardFlushAction.TYPE
this.replicatedBroadcastShardAction = replicatedBroadcastShardAction;
}
@Override
protected void doExecute(Task task, Request request, ActionListener<Response> listener) {
clusterService.threadPool().executor(executor).execute(ActionRunnable.wrap(listener, createAsyncAction(task, request)));
}
private CheckedConsumer<ActionListener<Response>, Exception> createAsyncAction(Task task, Request request) {
return new CheckedConsumer<ActionListener<Response>, Exception>() {
//省略代码
@Override
public void accept(ActionListener<Response> listener) {
final ClusterState clusterState = clusterService.state();
final List<ShardId> shards = shards(request, clusterState);
final Map<String, IndexMetadata> indexMetadataByName = clusterState.getMetadata().indices();
//遍历分片
try (var refs = new RefCountingRunnable(() -> finish(listener))) {
for (final ShardId shardId : shards) {
// NB This sends O(#shards) requests in a tight loop; TODO add some throttling here?
shardExecute(
task,
request,
shardId,
ActionListener.releaseAfter(new ReplicationResponseActionListener(shardId, indexMetadataByName), refs.acquire())
);
}
}
}
//省略代码
};
}
protected void shardExecute(Task task, Request request, ShardId shardId, ActionListener<ShardResponse> shardActionListener) {
assert Transports.assertNotTransportThread("may hit all the shards");
ShardRequest shardRequest = newShardRequest(request, shardId);
shardRequest.setParentTask(clusterService.localNode().getId(), task.getId());
//通过执行replicatedBroadcastShardAction,即TransportShardFlushAction.class来实现分片的刷新
client.executeLocally(replicatedBroadcastShardAction, shardRequest, shardActionListener);
}
}