当前位置: 首页 > news >正文

三合一网站建设什么意思dedecms电影网站模板

三合一网站建设什么意思,dedecms电影网站模板,义乌网站公司,做创意美食的视频网站有哪些注#xff1a;以下分析基于开源 v19.15.2.2-stable 版本进行#xff0c;社区最新版本代码改动较大#xff0c;但是总体思路是不变的。 用户提交一条查询SQL背后发生了什么#xff1f; 在传统关系型数据库中#xff0c;SQL处理器的组件主要包括以下几种#xff1a; • …注以下分析基于开源 v19.15.2.2-stable 版本进行社区最新版本代码改动较大但是总体思路是不变的。 用户提交一条查询SQL背后发生了什么 在传统关系型数据库中SQL处理器的组件主要包括以下几种 • Query Parsing 负责进行词法和语法分析,把程序从人类高可读的格式(即SQL)转化成机器高可读的格式(AST,抽象语法树)。 词法分析指的是把SQL中的字符序列分解成一个个独立的词法单元——Token(类型值)。 语法分析指的是从词法分析器输出的token中识别各类短语并构造出一颗抽象语法树。而按照构造抽象语法树的方向又可以把语法分析分成自顶向下和自底向上分析两种。而ClickHouse采用的则是手写一个递归下降的语法分析器。 • Query Rewrite 即通常我们说的Logical Optimizer或基于规则的优化器(Rule-Based Optimizer,即RBO)。 其负责应用一些启发式规则负责简化和标准化查询无需改变查询的语义。 常见操作有:谓词和算子下推视图展开简化常量运算表达式谓词逻辑的重写语义的优化等。 • Query Optimizer 即通常我们所说的Physical Optimizer负责把内部查询表达转化成一个高效的查询计划指导DBMS如何去取表如何进行排序如何Join。如下图所示一个查询计划可以被认为是一个数据流图在这个数据流图中表数据会像在管道中传输一样从一个查询操作符(operator)传递到另一个查询操作符。 一个查询计划 • Query Executor 查询执行器负责执行具体的查询计划从存储引擎中获取数据并且对数据应用查询计划得到结果。 执行引擎也分为很多种如经典的火山模型(Volcano Model)还有ClickHouse采用的向量化执行模型(Vectorization Model)。 (图来自经典论文 Architecture Of Database System) 但不管是传统的关系型数据库还是非关系型数据库SQL的解析和生成执行计划过程都是大同小异的而纵览ClickHouse的源代码可以把用户提交一条查询SQL背后的过程总结如下 1.服务端接收客户端发来的SQL请求具体形式是一个网络包Server的协议层需要拆包把SQL解析出来 2.Server负责初始化上下文与Network Handler然后 Parser 对Query做词法和语法分析解析成AST 3.Interpreter的 SyntaxAnalyzer 会应用一些启发式规则对AST进行优化重写 4.Interpreter的 ExpressionAnalyzer 根据上下文信息以及优化重写后的AST生成物理执行计划 5.物理执行计划分发到本地或者分布式的executor,各自从存储引擎中获取数据,应用执行计划 6.Server把执行后的结果以Block流的形式输出到Socket缓冲区,Client从Socket中读取即可得到结果 接收客户端请求 我们要以服务端的视角来出发首先来看server.cpp大概做什么事情: 下面只挑选重要的逻辑: • 初始化上下文 • 初始化Zookeeper(ClickHouse的副本复制机制需要依赖ZooKeeper) • 常规配置初始化 • 绑定服务端的端口根据网络协议初始化Handler对客户端提供服务 int Server::main() {// 初始化上下文global_context std::make_uniqueContext(Context::createGlobal());global_context-setApplicationType(Context::ApplicationType::SERVER);// zk初始化zkutil::ZooKeeperNodeCache main_config_zk_node_cache([] { return global_context-getZooKeeper(); });//其他config的初始化//...//绑定端口,对外提供服务auto address make_socket_address(host, port);socket.bind(address, /* reuseAddress */ true);//根据网络协议建立不同的server类型//现在支持的server类型有 HTTP,HTTPS,TCP,Interserver,mysql//以TCP版本为例:create_server(tcp_port, [](UInt16 port){Poco::Net::ServerSocket socket;auto address socket_bind_listen(socket, listen_host, port);servers.emplace_back(std::make_uniquePoco::Net::TCPServer(new TCPHandlerFactory(*this),server_pool,socket,new Poco::Net::TCPServerParams));});//启动serverfor (auto server : servers)server-start();} 客户端发来的请求是由各自网络协议所对应的 Handler 来进行的server在启动的时候 Handler 会被初始化并绑定在指定端口中。我们以TCPHandler为例看看服务端是如何处理客户端发来的请求的重点关注 TCPHandler::runImpl 的函数实现: • 初始化输入和输出流的缓冲区 • 接受请求报文拆包 • 执行Query(包括整个词法语法分析Query重写物理计划生成和生成结果) • 把Query结果保存到输出流然后发送到Socket的缓冲区等待发送回客户端 void TCPHandler::runImpl() {//实例化套接字对应的输入和输出流缓冲区in std::make_sharedReadBufferFromPocoSocket(socket());out std::make_sharedWriteBufferFromPocoSocket(socket());while (1){// 接收请求报文receivePacket();// 执行Query state.io executeQuery(state.query, *query_context, false, state.stage, may_have_embedded_data);//根据Query种类来处理不同的Query//处理insert QueryprocessInsertQuery();//并发处理普通QueryprocessOrdinaryQueryWithProcessors();//单线程处理普通QueryprocessOrdinaryQuery();}} 那CK处理客户端发送过来的Query的具体逻辑是怎样的呢? 我们可以在dbms/src/Interpreters/executeQuery.cpp 中一探究竟: 具体逻辑在 executeQueryImpl 函数中,挑选核心的逻辑进行讲解: static std::tupleASTPtr, BlockIO executeQueryImpl() {//构造ParserParserQuery parser(end, settings.enable_debug_queries);ASTPtr ast;//把Query转化为抽象语法树ast parseQuery(parser, begin, end, , max_query_size);//生成interpreter实例auto interpreter InterpreterFactory::get(ast, context, stage);// interpreter解析AST,结果是BlockIOres interpreter-execute();//返回结果是抽象语法树和解析后的结果组成的二元组return std::make_tuple(ast, res); } 该函数所做的事情 • 构建Parser,把Query解析成AST(抽象语法树) • InterpreterFactory根据AST生成对应的Interpreter实例 • AST是由Interpreter来解析的执行结果是一个BlockIO,BlockIO是对 BlockInputStream 和 BlockOutputStream的一个封装。 总结: • 服务端调用 executeQuery 来处理client发送的Query执行后的结果保存在state这个结构体的io成员中。 每一条Query都会对应一个state结构体记录了这条Query的id处理状态压缩算法Query的文本和Query所处理数据对应的IO流等元信息。 • 然后服务端调用 processOrdinaryQuery 等方法把输出流结果封装成异步的IO流发送到回client。 解析请求(Parser) CK选择采用手写一个递归下降的Parser来对SQL进行解析生成的结果是这个SQL对应的抽象语法树(AST),抽象语法树由表示各个操作的节点(IAST)表示。而本节主要介绍Parser背后的核心逻辑: 词法分析和语法分析的核心逻辑可以在parseQuery.cpp的 tryParseQuery 中一览无余。 该函数利用lexer将扫描Query字符流将其分割为一个个的Token token_iterator 即一个Token流迭代器然后parser再对Token流进行解析生成AST抽象语法树。 ASTPtr tryParseQuery() {//Token为lexer词法分析后的基本单位,词法分析后生成的是Token流Tokens tokens(pos, end, max_query_size);IParser::Pos token_iterator(tokens);ASTPtr res;//Token流经过语法分析生成AST抽象语法树bool parse_res parser.parse(token_iterator, res, expected);return res;} 我们可以看到,语法分析的核心就在于parser执行的parse方法。parse 方法具体的实现在 ParserQuery.cpp 的 parseImpl 中。 bool ParserQuery::parseImpl(Pos pos, ASTPtr node, Expected expected) {ParserQueryWithOutput query_with_output_p(enable_explain);ParserInsertQuery insert_p(end);ParserUseQuery use_p;ParserSetQuery set_p;ParserSystemQuery system_p;bool res query_with_output_p.parse(pos, node, expected)|| insert_p.parse(pos, node, expected)|| use_p.parse(pos, node, expected)|| set_p.parse(pos, node, expected)|| system_p.parse(pos, node, expected);return res; } 我们可以看到,这个方法粗略地把Query分为了五种,但是本质上可以归纳为两种(第一种为有结果输出,对应show,select,create等语句;第二种为无结果输出,对应insert,use,set和与系统相关的语句(如exit)) • QueryWithOutput • InsertQuery • UseQuery • SetQuery • SystemQuery 每一种Query都自定义了其专属的Parser,所以代码逻辑是当接收到一个Query输入的时候会尝试各种Query的Parser直到成功为止。 我们可以select语句对应的parser进行分析: 核心逻辑可以总结为 1.先给出select语句中可能出现的关键词 2.在词法分析生成的Token流中爬取这些关键词 3.如果成功爬取则 setExpression 函数会组装该关键字对应的AST节点 每一种SQL语句(如select,drop,insert,create)都有对应的AST类并且分别包含了这些语句中特有的关键字。 bool ParserSelectQuery::parseImpl(Pos pos, ASTPtr node, Expected expected) {//创建AST树节点auto select_query std::make_sharedASTSelectQuery();node select_query;//select语句中会出现的关键词ParserKeyword s_select(SELECT);ParserKeyword s_distinct(DISTINCT);ParserKeyword s_from(FROM);ParserKeyword s_prewhere(PREWHERE);ParserKeyword s_where(WHERE);ParserKeyword s_group_by(GROUP BY);ParserKeyword s_with(WITH);ParserKeyword s_totals(TOTALS);ParserKeyword s_having(HAVING);ParserKeyword s_order_by(ORDER BY);ParserKeyword s_limit(LIMIT);ParserKeyword s_settings(SETTINGS);ParserKeyword s_by(BY);ParserKeyword s_rollup(ROLLUP);ParserKeyword s_cube(CUBE);ParserKeyword s_top(TOP);ParserKeyword s_with_ties(WITH TIES);ParserKeyword s_offset(OFFSET);//...//依次对Token流爬取上述关键字ParserTablesInSelectQuery().parse(pos, tables, expected)//根据语法分析结果设置AST的Expression属性,可以理解为如果SQL存在该关键字,这个关键字都会转化为AST上的一个节点select_query-setExpression(ASTSelectQuery::Expression::WITH, std::move(with_expression_list));select_query-setExpression(ASTSelectQuery::Expression::SELECT, std::move(select_expression_list));select_query-setExpression(ASTSelectQuery::Expression::TABLES, std::move(tables));select_query-setExpression(ASTSelectQuery::Expression::PREWHERE, std::move(prewhere_expression));select_query-setExpression(ASTSelectQuery::Expression::WHERE, std::move(where_expression));select_query-setExpression(ASTSelectQuery::Expression::GROUP_BY, std::move(group_expression_list));select_query-setExpression(ASTSelectQuery::Expression::HAVING, std::move(having_expression));select_query-setExpression(ASTSelectQuery::Expression::ORDER_BY, std::move(order_expression_list));select_query-setExpression(ASTSelectQuery::Expression::LIMIT_BY_OFFSET, std::move(limit_by_offset));select_query-setExpression(ASTSelectQuery::Expression::LIMIT_BY_LENGTH, std::move(limit_by_length));select_query-setExpression(ASTSelectQuery::Expression::LIMIT_BY, std::move(limit_by_expression_list));select_query-setExpression(ASTSelectQuery::Expression::LIMIT_OFFSET, std::move(limit_offset));select_query-setExpression(ASTSelectQuery::Expression::LIMIT_LENGTH, std::move(limit_length));select_query-setExpression(ASTSelectQuery::Expression::SETTINGS, std::move(settings));} 整个Parser的流程图 执行请求(Interpreter) 解释器(Interpreter)负责从抽象语法树中创建查询执行的流水线整条流水线以 BlockInputStream 和 BlockOutputStream 进行组织。比方说select是基于from的Block输出流来进行选择的选择后的结果也会以Block输出流的形式输出到结果。 首先我们来看: dbms/src/Interpreters/InterpreterFactory.cpp 每一种Query都会有对应的Interpreter这个工厂方法就是根据AST的种类来实例化其对应的Interpreter,由其来具体执行对应AST的执行计划: std::unique_ptrIInterpreter InterpreterFactory::get(ASTPtr query, Context context, QueryProcessingStage::Enum stage) {//举个例子,如果该AST是由select语句转化过来,if (query-asASTSelectQuery()){/// This is internal part of ASTSelectWithUnionQuery./// Even if there is SELECT without union, it is represented by ASTSelectWithUnionQuery with single ASTSelectQuery as a child.return std::make_uniqueInterpreterSelectQuery(query, context, SelectQueryOptions(stage));} } 我们就以 InterpreterSelectQuery 为例了解其实例化的核心逻辑: InterpreterSelectQuery::InterpreterSelectQuery(){//获取ASTauto query getSelectQuery();//对AST做进一步语法分析对语法树做优化重写syntax_analyzer_result SyntaxAnalyzer(context, options).analyze(query_ptr, source_header.getNamesAndTypesList(), required_result_column_names, storage, NamesAndTypesList());//每一种Query都会对应一个特有的表达式分析器,用于爬取AST生成执行计划(操作链)query_analyzer std::make_uniqueSelectQueryExpressionAnalyzer(query_ptr, syntax_analyzer_result, context,NameSet(required_result_column_names.begin(), required_result_column_names.end()),options.subquery_depth, !options.only_analyze); } 语法分析直接生成的AST转化成执行计划可能性能上并不是最优的因此需要SyntaxAnalyzer 对其进行优化重写在其源码中可以看到其涉及到非常多 基规则优化(rule based optimization) 的trick。 SyntaxAnalyzer 会逐个针对这些规则对查询进行检查确定其是否满足转换规则一旦满足就会对其进行转换。 SyntaxAnalyzerResultPtr SyntaxAnalyzer::analyze() {// 剔除冗余列removeDuplicateColumns(result.source_columns);// 根据settings中enable_optimize_predicate_expression配置判断是否进行谓词下移replaceJoinedTable(node);// 根据settings中distributed_product_mode配置重写IN 与 JOIN 表达式InJoinSubqueriesPreprocessor(context).visit(query);// 优化Query内部的布尔表达式LogicalExpressionsOptimizer().perform();// 创建一个从别名到AST节点的映射字典 QueryAliasesVisitor(query_aliases_data, log.stream()).visit(query);// 公共子表达式的消除QueryNormalizer(normalizer_data).visit(query);// 消除select从句后的冗余列removeUnneededColumnsFromSelectClause(select_query, required_result_columns, remove_duplicates);// 执行标量子查询并且用常量替代标量子查询结果executeScalarSubqueries(query, context, subquery_depth);// 如果是select语句还会做下列优化:// 谓词下移优化PredicateExpressionsOptimizer(select_query, settings, context).optimize();/// GROUP BY 从句的优化optimizeGroupBy(select_query, source_columns_set, context);/// ORDER BY 从句的冗余项剔除optimizeOrderBy(select_query);/// LIMIT BY 从句的冗余列剔除optimizeLimitBy(select_query);/// USING语句的冗余列剔除optimizeUsing(select_query);} 这里挑选几个简单介绍一下: • 公共子表达式消除(Common Subexpression Elimination) 如果表达式 x op y 先前被计算过并且从先前的计算到现在其计算表达式对应的值没有改变那么 x op y 就称为公共子表达式。公共子表达式消除会搜索所有相同计算表达式的实例并分析是否值得用保存计算值的单个变量来替换它们以减少计算的开销。 • 标量子查询(Scala Subquery)的常量替换 标量子查询就是返回单一值的子查询和公共子表达式消除相似可以用常量来替换SQL中所有的标量子查询结果以减少计算开销。 • 谓词下移(Predicate Pushdown) 把外层查询块中的WHERE子句的谓词下移到较低层查询块如视图以尽可能把过滤数据的操作移动到靠近数据源的位置。提前进行数据过滤能够大幅减少网络传输或者内存读取访问的数据量以提高查询效率。 而 query_analyzer 的作用可以理解为解析优化重写后的AST然后对所要进行的操作组成一条操作链即物理执行计划如: ExpressionActionsChain chain; analyzer.appendWhere(chain); chain.addStep(); analyzer.appendSelect(chain); analyzer.appendOrderBy(chain); chain.finalize(); 上述代码把where,select,orderby操作都加入到操作链中接下来就可以从Storage层读取Block对Block数据应用上述操作链的操作。而执行的核心逻辑就在对应Interpreter的 executeImpl 方法实现中,这里以select语句的Interpreter来了解下读取Block数据并且对block数据进行相应操作的流程。 void InterpreterSelectQuery::executeImpl(TPipeline pipeline, const BlockInputStreamPtr prepared_input) {// 对应Query的ASTauto query getSelectQuery();AnalysisResult expressions;// 物理计划判断表达式是否有where,aggregate,having,order_by,litmit_by等字段expressions analyzeExpressions(getSelectQuery(),*query_analyzer,QueryProcessingStage::FetchColumns,options.to_stage,context,storage,true,filter_info);// 从Storage读取数据executeFetchColumns(from_stage, pipeline, sorting_info, expressions.prewhere_info, expressions.columns_to_remove_after_prewhere);// eg:根据SQL的关键字在BlockStream流水线中执行相应的操作, 如where,aggregate,distinct都分别由一个函数负责执行executeWhere(pipeline, expressions.before_where, expressions.remove_where_filter);executeAggregation(pipeline, expressions.before_aggregation, aggregate_overflow_row, aggregate_final);executeDistinct(pipeline, true, expressions.selected_columns); } 既然我们知道了执行计划AnalysisResult(即物理执行计划)接下来就需要从storage层中读取数据来执行对应的操作核心逻辑在 executeFetchColumns 中: 核心操作就是从storage层读取所要处理列的Block并组织成BlockStream。 void InterpreterSelectQuery::executeFetchColumns(QueryProcessingStage::Enum processing_stage, TPipeline pipeline,const SortingInfoPtr sorting_info, const PrewhereInfoPtr prewhere_info, const Names columns_to_remove_after_prewhere) { // 实例化Block Streamauto streams storage-read(required_columns, query_info, context, processing_stage, max_block_size, max_streams)// 读取列对应的Block,并且组织成Block Streamstreams {std::make_sharedNullBlockInputStream(storage-getSampleBlockForColumns(required_columns))};streams.back() std::make_sharedExpressionBlockInputStream(streams.back(), query_info.prewhere_info-remove_columns_actions); } 读取完Block Stream之后就是对其执行各种execute操作如 executeAggregation , executeWhere 操作详见 InterpreterSelectQuery::executeImpl 的代码。 因此Interpreter的处理过程可以总结为: • 对AST进行优化重写 • 解析重写后的AST并生成操作链(执行计划) • 从存储引擎中读取要处理的Block数据 • 对读取的Block数据应用操作链上的操作 那我们读取Block Stream并进行处理后生成的结果如何写回到storage层呢? 我们这里以insert语句的Interpreter来了解下: BlockIO InterpreterInsertQuery::execute() {// table为存储引擎接口StoragePtr table getTable(query);BlockOutputStreamPtr out;// 从存储引擎读取Block Streamauto query_sample_block getSampleBlock(query, table);out std::make_sharedAddingDefaultBlockOutputStream(out, query_sample_block, out-getHeader(), table-getColumns().getDefaults(), context);//执行结果封装成BlockIOBlockIO res;res.out std::move(out); } 上面代码中的StoragePtr实际上就是IStorage这个存储引擎的接口 using StoragePtr std::shared_ptrIStorage; 无论是写入还是读取操作都是依靠底层存储引擎(如MergeTree)的write和read接口来实现的关于存储引擎的细节实现这里暂时不赘述这里我们只需要知道我们从存储引擎接口中以流方式读取Block数据而结果组织成BlockIO流输出。Interpreter的流程总结如下: 返回请求结果 TCPHandler::runImpl 中执行完 executeQuery 之后需要调用各种processQuery的方法来给client返回执行SQL后的结果。 我们以 TCPHandler::processOrdinaryQuery 为例做简单分析: void TCPHandler::processOrdinaryQuery() {//把BlockStream封装成异步的Stream,那么从流中读取数据将会是异步操作AsynchronousBlockInputStream async_in(state.io.in);while(true){Block block;//从IO流读取block数据block async_in.read();//发送block数据sendData(block);} } Server负责在 sendData 函数中把输出结果写入到套接字输出缓冲区中,client只要从这个输出缓冲区读取就能够得到结果。 void TCPHandler::sendData(const Block block) {//初始化OutputStream的参数initBlockOutput(block);// 调用BlockOutputStream的write函数,把Block写到输出流state.block_out-write(block);state.maybe_compressed_out-next();out-next(); } 结语 了解ClickHouse背后SQL的查询整个流程不仅能让数据库使用者更清晰地认识到如何编写最优化的SQL也能够让数据库内核开发者加深对数据库体系结构的理解提高开发效率。本文并没有涉及到太深入的技术细节诸如向量化执行引擎SIMD基于llvm的动态代码生成类MergeTree存储引擎等CK的技术细节也没有提及只是从宏观角度给读者介绍了执行SQL背后内核到底发生了什么。后续我们会推出更多内核源码解读文章敬请关注。 写在最后 阿里云已经率先推出了ClickHouse的云托管产品产品首页地址云数据库ClickHouse目前正在免费公测中欢迎大家点击链接申请免费试用。 原文链接 本文为阿里云原创内容未经允许不得转载。
http://wiki.neutronadmin.com/news/119199/

相关文章:

  • 怎么做网站排版网站题头是什么
  • 茂名网站开发公司做网站 公司
  • 香奈儿网站建设目标大连哪个公司做网站好
  • 简单建站外贸网站建设是什么
  • 家政的网站怎么做怎么创建属于自己的平台
  • 大理高端网站建设免费企业网站系统源码
  • 网站开发及推广是怎么回事网站开发管理学什么
  • 中国城镇建设网站软文网站备案如何查询
  • 网站的设计步骤十个有创意的线上活动
  • 网站关键词搜不到佛山网页模板建站
  • 石家庄网站建设就找wordpress构建自定义设置页面
  • 网站明确内容做网站域名大概多少钱
  • 网站空间付款方式四川省建设部网站
  • php网站是什么数据库文件cp网站开发是什么
  • 淘宝客推广怎样做网站wordpress the_content();
  • 广州西樵网站制作网站开发实现总结
  • 苏州住房和城乡建设厅网站珠海专门做网站
  • 合肥最好的网站建设公司石家庄网站优化招聘
  • 2021年给我一个网站开发公司人事行政经理工作总结及计划
  • 网站建设题库含答案北京公司注册核名详细流程
  • 制作个人网站素材外贸公司英文网站
  • 建设银行春招网站做ppt素材网站哪个好
  • 做网站视频背景郑州做网站排名公司
  • 网站获取qq号码 代码淘客请人做网站
  • 网站被黑能查到是谁做的吗深圳网站建设 设计
  • 线上网站设计培训建立网站服务的公司网站
  • 装饰网站建设多少钱开平网站建设
  • 宜兴城乡建设局网站做网站项目需要多少钱
  • 淄博网站制作制作苏州手机社区网站建设
  • 秦皇岛做网站seo的百度指数明星搜索排名