流程:
1. Presto Client 提交SQL
入口:com.facebook.presto.cli.Console.run()
// Session 初始化
ClientSession session = clientOptions.toClientSession();
// 判断是否存在SQL,即判断当前为 命令模式还是会话模式。会话模式需实时获取命令再二次提交给服务端。
if (hasQuery) {}
// 文件模式检测
if (isFromFile) {}
//生存QueryRunner对象,包装请求参数及配置
QueryRunner queryRunner = new QueryRunner()
//SQL切分,经过queryRunner.startQuery提交给服务端 /v1/statement接口。获得Response
if (hasQuery) {
return executeCommand(queryRunner, query, clientOptions.outputFormat, clientOptions.ignoreErrors);
}
queryRunner.startQuery.xxxx
//Response 返回请求被接收及部分taskID等参数,随后进入advance 循环获取Nexturl
boolean success = query.renderOutput(System.out, outputFormat, interactive);
renderQueryOutput(out, outputFormat, interactive);
private void processInitialStatusUpdates(WarningsPrinter warningsPrinter)
client.advance();
URI nextUri = currentStatusInfo().getNextUri();
//后续逻辑为:频繁请求Nexturl。 若JSON<QueryResults>不包含nextUri,说明查询已结束(成功|失败)随后获取执行结果;若包含nextUri,则还需要继续访问nextUri以获取下一批的查询结果
2. 请求提交至服务端,服务端对象初始化及statement校验
//Server端构建 交互状态接口
com.facebook.presto.server.protocol
// 创建Query对象,初始化taskID 等,返回nexturl
@Path("/v1/statement")
public class StatementResource{
Query query = Query.create()
}
//指定Task nextURL请求路径
@Path("{queryId}/{token}")
public void getQueryResults(
asyncQueryResults(query, OptionalLong.of(token), maxWait, targetResultSize, uriInfo, proto, asyncResponse);
)
ListenableFuture<QueryResults> queryResultsFuture = query.waitForResults(token, uriInfo, scheme, wait, targetResultSize);
getFutureStateChange()
//检测SQL状态,新请求|已提交|运行中。若是新请求则调用SqlQueryManager.createQuery
// ensure the query has been submitted
submissionFuture.submitQuery();
querySubmissionFuture = queryManager.createQuery(queryId, sessionContext, this.query);
// if query query submission has not finished, wait for it to finish
if (!submissionFuture.isDone()) {
return submissionFuture;
}
// decode session
// prepare query
/*
此时:Antlr解析语法,生成基础Statement,获得结构化解析结果
*/
preparedQuery = queryPreparer.prepareQuery(session, query, warningCollector);
// select resource group
// apply system defaults for query
// create query execution
queryExecution = queryExecutionFactory.createQueryExecution()
/*
此时:创建QueryExecution后,会生成Analyzer对象,进行SQL权限校验及语法语义校验
例如:权限检查
– 库表 增删改查权限
语法检查
- Where 后条件判断
- Group 1,2,3 字段判断
- Order By 是否可行 等常规语法语义判断
*/
StatementAnalyzer analyzer = new Analyzer()
// 识别并处理 EXPLAIN xxx ,DESCRIBE xxx,SHOW xxx 等特殊情况
Statement rewrittenStatement = StatementRewrite.rewrite
// vistor分析语义
Analysis analysis = new Analysis(rewrittenStatement, parameters, isDescribe);
StatementAnalyzer analyzer = new StatementAnalyzer(analysis, metadata, sqlParser, accessControl, session, warningCollector);
analyzer.analyze(rewrittenStatement, Optional.empty());
// check column access permissions for each table 遍历库表字段,检测字段权限
analysis.getTableColumnReferences().forEach.xxxxx
accessControlInfo.getAccessControl().checkCanSelectFromColumns
AccessControlManager.checkCanSelectFromColumns
//检测catalog
authenticationCheck(() -> checkCanAccessCatalog(identity, tableName.getCatalogName()));
FileBasedSystemAccessControl.checkCanAccessCatalog
//检测查询字段
authorizationCheck(() -> systemAccessControl.get().checkCanSelectFromColumns(identity, tableName.asCatalogSchemaTableName(), columnNames));
ForwardingSystemAccessControl.checkCanSelectFromColumns
3. Presto execution.resourceGroups 资源组调用
// resourceGroupManager 资源组提交任务
resourceGroupManager.submit(preparedQuery.getStatement(), queryExecution, selectionContext, queryExecutor);
/* 关于资源组:
当PrestoServer通过main方法run初始化时
1.首先执行PluginManager的loadPlugins,向InternalResourceGroupManager中一个存放ResourceGroupManagerFactory类型元素的Map 添加可用的资源组管理工厂类。
2.调用 InternalResourceGroupManager 的 loadConfigurationManager 方法,读取etc/resource-groups.properties文件中的配置项resource-groups.configuration-manager
3.读取configurationMF中对应的ResourceGroupCMFactory,调用create方法构建ResourceGroupCM对象,赋值给InternalResourceGroupManager类的configurationManager
此处,即触发资源组调用
*/
//Group调用
package com.facebook.presto.execution.resourceGroups;
submit();
createGroupIfNecessary(selectionContext, executor);
groups.get(selectionContext.getResourceGroupId()).run(queryExecution);
public void run(ManagedQueryExecution query)
executor.execute(query::start);
private void waitForMinimumWorkers()
private void startExecution()
//当前Group是否可运行
canRun &= group.canRunMore();
//runningQueries上锁,开始执行
executor.execute(query::start);
addSuccessCallback(minimumWorkerFuture, () -> queryExecutor.submit(this::startExecution));
4.构建执行计划
private void startExecution()
SetThreadName ignored = new SetThreadName("Query-%s", stateMachine.getQueryId())
// analyze query 生成逻辑执行计划
PlanRoot plan = analyzeQuery();
// plan distribution of query 生成物理执行计划
planDistribution(plan);
// if query is not finished, start the scheduler, otherwise cancel it 任务调度
SqlQueryScheduler scheduler = queryScheduler.get();
if (!stateMachine.isDone()) {
scheduler.start();
}
下文详细介绍各个执行计划构建过程