【PostgreSQL内核学习(七)—— 查询规划(生成路径)】

news2024/11/24 9:10:02

查询规划——预处理

  • 生成路径

声明:本文的部分内容参考了他人的文章。在编写过程中,我们尊重他人的知识产权和学术成果,力求遵循合理使用原则,并在适用的情况下注明引用来源。
本文主要参考了《PostgresSQL数据库内核分析》一书

生成路径

  对于SQL中的计划命令的处理,无非就是获取一个(或者一系列)元组,然后将这个元组返回给用户或者以其为基础进行插入、更新、删除操作。因此对于一个执行计划来说,最重要的部分就是告诉查询执行模块如何取到要操作的元组。执行计划要操作的元组可以来自于一个基本表或者由一系列基本表连接而成的“连接表”,当然一个基本表也可以看成是由它自身构成的连接表。这些基本表连接成连接表的关系可以从逻辑上表示成一个二叉树结构(连接树),由于表之间不同的连接方式和顺序,同一组基本表形成连接表的连接树会有多个,每一棵连接树在PostgreSQL中都称为一条路径。因此,路径在查询的规划和执行过程中表示了从一组基本表生成最终连接表的方式。而查询规划的工作就是从一系列等效的路径中选取效率最高的路径,并形成执行计划。
  生成路径的工作是由函数query_planner来完成的,query_planner 函数是 PostgreSQL 中生成简化查询计划的核心函数。该函数通过优化查询的 join 操作,但不涉及更高级的特性(例如分组、排序等),来为基本查询生成路径(simplified plan)。此函数的返回值是查询的顶层 join 连接(join relation)的 RelOptInfo
  该函数的执行过程如下:

  1. 首先,判断查询的 join 树是否为空。如果 join 树为空,则表明查询非常简单,例如 “SELECT 2+2;”“INSERT … VALUES();”,则直接构建一个空的 join 关系(build_empty_join_rel)表示没有任何基本关系(baserel),然后创建一个 Result 路径,生成查询计划。这里还会检查是否允许并行执行,如果允许则检查查询的限制条件是否适合并行执行。
  2. 初始化 PlannerInfo 结构体中的相关列表和数组。
  3. 为查询中的所有基本关系(base relation)构建 RelOptInfo 结构体,并为所有附加关系(appendrel)的成员关系(other rels)间接构建 RelOptInfo 结构体。这样,对于查询涉及的所有“简单”(非 join)关系,我们都有了 RelOptInfo
  4. 构建基本关系的目标列表(target list)和 join 树中的限制和连接条件,并为占位符变量(PlaceHolderVar)生成 PlaceHolderInfo 条目。同时,构建表达式的等价类(Equivalence Classes)以描述可以等价变换的表达式。还构建了 SpecialJoinInfo 列表,用于存储关于连接顺序的限制信息。最后,构建了一个目标 joinlist,用于进一步生成计划。
  5. 对于查询中所有占位符变量,确保所需的 Vars 被标记为相关联的连接层级(needed join level)。
  6. 去除无用的外连接,因为可能会导致不必要的计算。
  7. 将含有唯一内部关系的半连接转换为普通内连接,减少计算复杂度。
  8. 将“占位符”表达式(placeholders)分发到需要的基本关系中。
  9. 构建横向引用(lateral reference)集合,用于处理 lateral join
  10. 匹配外键(foreign keys)与等价类和连接条件。在构建完等价类之后进行,以便忽略涉及已移除关系的外键。
  11. 提取 join OR 条件中的单关系限制 OR 条件。
  12. 为所有实际表计算了大小估计,并计算 total_table_pages
  13. 最终开始执行主要的查询规划工作,通过 make_one_rel 函数生成 join 关系的 RelOptInfo
  14. 检查是否至少得到一个可用的路径,并返回查询顶层 join 关系的 RelOptInfo

  该函数的处理流程如图5-26所示。
在这里插入图片描述
  query_planner函数的源码如下:(路径:src/backend/optimizer/plan/planmain.c

该函数的参数说明如下:

  • rootPlannerInfo 结构体,用于描述要优化的查询
  • tlist:目标列表,即查询要输出的列
  • qp_callback:查询路径键回调函数,用于计算查询的路径键(查询结果的排序顺序)。
  • qp_extra:可选的额外数据,传递给查询路径键回调函数。
/*
 * query_planner
 *	  Generate a path (that is, a simplified plan) for a basic query,
 *	  which may involve joins but not any fancier features.
 *
 * Since query_planner does not handle the toplevel processing (grouping,
 * sorting, etc) it cannot select the best path by itself.  Instead, it
 * returns the RelOptInfo for the top level of joining, and the caller
 * (grouping_planner) can choose among the surviving paths for the rel.
 *
 * root describes the query to plan
 * tlist is the target list the query should produce
 *		(this is NOT necessarily root->parse->targetList!)
 * qp_callback is a function to compute query_pathkeys once it's safe to do so
 * qp_extra is optional extra data to pass to qp_callback
 *
 * Note: the PlannerInfo node also includes a query_pathkeys field, which
 * tells query_planner the sort order that is desired in the final output
 * plan.  This value is *not* available at call time, but is computed by
 * qp_callback once we have completed merging the query's equivalence classes.
 * (We cannot construct canonical pathkeys until that's done.)
 */
RelOptInfo *
query_planner(PlannerInfo *root, List *tlist,
			  query_pathkeys_callback qp_callback, void *qp_extra)
{
	Query	   *parse = root->parse;
	List	   *joinlist;
	RelOptInfo *final_rel;
	Index		rti;
	double		total_pages;

	/*
	 * If the query has an empty join tree, then it's something easy like
	 * "SELECT 2+2;" or "INSERT ... VALUES()".  Fall through quickly.
	 */
	if (parse->jointree->fromlist == NIL)
	{
		/* We need a dummy joinrel to describe the empty set of baserels */
		final_rel = build_empty_join_rel(root);

		/*
		 * If query allows parallelism in general, check whether the quals are
		 * parallel-restricted.  (We need not check final_rel->reltarget
		 * because it's empty at this point.  Anything parallel-restricted in
		 * the query tlist will be dealt with later.)
		 */
		if (root->glob->parallelModeOK)
			final_rel->consider_parallel =
				is_parallel_safe(root, parse->jointree->quals);

		/* The only path for it is a trivial Result path */
		add_path(final_rel, (Path *)
				 create_result_path(root, final_rel,
									final_rel->reltarget,
									(List *) parse->jointree->quals));

		/* Select cheapest path (pretty easy in this case...) */
		set_cheapest(final_rel);

		/*
		 * We still are required to call qp_callback, in case it's something
		 * like "SELECT 2+2 ORDER BY 1".
		 */
		root->canon_pathkeys = NIL;
		(*qp_callback) (root, qp_extra);

		return final_rel;
	}

	/*
	 * Init planner lists to empty.
	 *
	 * NOTE: append_rel_list was set up by subquery_planner, so do not touch
	 * here.
	 */
	root->join_rel_list = NIL;
	root->join_rel_hash = NULL;
	root->join_rel_level = NULL;
	root->join_cur_level = 0;
	root->canon_pathkeys = NIL;
	root->left_join_clauses = NIL;
	root->right_join_clauses = NIL;
	root->full_join_clauses = NIL;
	root->join_info_list = NIL;
	root->placeholder_list = NIL;
	root->fkey_list = NIL;
	root->initial_rels = NIL;

	/*
	 * Make a flattened version of the rangetable for faster access (this is
	 * OK because the rangetable won't change any more), and set up an empty
	 * array for indexing base relations.
	 */
	setup_simple_rel_arrays(root);

	/*
	 * Construct RelOptInfo nodes for all base relations in query, and
	 * indirectly for all appendrel member relations ("other rels").  This
	 * will give us a RelOptInfo for every "simple" (non-join) rel involved in
	 * the query.
	 *
	 * Note: the reason we find the rels by searching the jointree and
	 * appendrel list, rather than just scanning the rangetable, is that the
	 * rangetable may contain RTEs for rels not actively part of the query,
	 * for example views.  We don't want to make RelOptInfos for them.
	 */
	add_base_rels_to_query(root, (Node *) parse->jointree);

	/*
	 * Examine the targetlist and join tree, adding entries to baserel
	 * targetlists for all referenced Vars, and generating PlaceHolderInfo
	 * entries for all referenced PlaceHolderVars.  Restrict and join clauses
	 * are added to appropriate lists belonging to the mentioned relations. We
	 * also build EquivalenceClasses for provably equivalent expressions. The
	 * SpecialJoinInfo list is also built to hold information about join order
	 * restrictions.  Finally, we form a target joinlist for make_one_rel() to
	 * work from.
	 */
	build_base_rel_tlists(root, tlist);

	find_placeholders_in_jointree(root);

	find_lateral_references(root);

	joinlist = deconstruct_jointree(root);

	/*
	 * Reconsider any postponed outer-join quals now that we have built up
	 * equivalence classes.  (This could result in further additions or
	 * mergings of classes.)
	 */
	reconsider_outer_join_clauses(root);

	/*
	 * If we formed any equivalence classes, generate additional restriction
	 * clauses as appropriate.  (Implied join clauses are formed on-the-fly
	 * later.)
	 */
	generate_base_implied_equalities(root);

	/*
	 * We have completed merging equivalence sets, so it's now possible to
	 * generate pathkeys in canonical form; so compute query_pathkeys and
	 * other pathkeys fields in PlannerInfo.
	 */
	(*qp_callback) (root, qp_extra);

	/*
	 * Examine any "placeholder" expressions generated during subquery pullup.
	 * Make sure that the Vars they need are marked as needed at the relevant
	 * join level.  This must be done before join removal because it might
	 * cause Vars or placeholders to be needed above a join when they weren't
	 * so marked before.
	 */
	fix_placeholder_input_needed_levels(root);

	/*
	 * Remove any useless outer joins.  Ideally this would be done during
	 * jointree preprocessing, but the necessary information isn't available
	 * until we've built baserel data structures and classified qual clauses.
	 */
	joinlist = remove_useless_joins(root, joinlist);

	/*
	 * Also, reduce any semijoins with unique inner rels to plain inner joins.
	 * Likewise, this can't be done until now for lack of needed info.
	 */
	reduce_unique_semijoins(root);

	/*
	 * Now distribute "placeholders" to base rels as needed.  This has to be
	 * done after join removal because removal could change whether a
	 * placeholder is evaluable at a base rel.
	 */
	add_placeholders_to_base_rels(root);

	/*
	 * Construct the lateral reference sets now that we have finalized
	 * PlaceHolderVar eval levels.
	 */
	create_lateral_join_info(root);

	/*
	 * Match foreign keys to equivalence classes and join quals.  This must be
	 * done after finalizing equivalence classes, and it's useful to wait till
	 * after join removal so that we can skip processing foreign keys
	 * involving removed relations.
	 */
	match_foreign_keys_to_quals(root);

	/*
	 * Look for join OR clauses that we can extract single-relation
	 * restriction OR clauses from.
	 */
	extract_restriction_or_clauses(root);

	/*
	 * We should now have size estimates for every actual table involved in
	 * the query, and we also know which if any have been deleted from the
	 * query by join removal; so we can compute total_table_pages.
	 *
	 * Note that appendrels are not double-counted here, even though we don't
	 * bother to distinguish RelOptInfos for appendrel parents, because the
	 * parents will still have size zero.
	 *
	 * XXX if a table is self-joined, we will count it once per appearance,
	 * which perhaps is the wrong thing ... but that's not completely clear,
	 * and detecting self-joins here is difficult, so ignore it for now.
	 */
	total_pages = 0;
	for (rti = 1; rti < root->simple_rel_array_size; rti++)
	{
		RelOptInfo *brel = root->simple_rel_array[rti];

		if (brel == NULL)
			continue;

		Assert(brel->relid == rti); /* sanity check on array */

		if (IS_SIMPLE_REL(brel))
			total_pages += (double) brel->pages;
	}
	root->total_table_pages = total_pages;

	/*
	 * Ready to do the primary planning.
	 */
	final_rel = make_one_rel(root, joinlist);

	/* Check that we got at least one usable path */
	if (!final_rel || !final_rel->cheapest_total_path ||
		final_rel->cheapest_total_path->param_info != NULL)
		elog(ERROR, "failed to construct the join relation");

	return final_rel;
}

  由源代码可以看到,生成的路径是 RelOptInfo 结构体,其中包含了计划中要执行的操作和访问基本关系的路径信息。
  RelOptInfo结构(数据结构5.15)是贯穿整个路径生成过程的一个数据结构,生成路径的最终结果始终存放在其中,生成和选择路径所需的许多数据也存放在其中。路径生成和选择涉及的所有操作几乎都是针对这个结构进行的,因此搞清楚这个结构对于理解整个路径生成过程非常重要。
  先来看看书中所描述的 RelOptInfo 结构吧。
在这里插入图片描述
在这里插入图片描述
  RelOptInfo结构源码如下:(路径:src/include/nodes/relation.h)

typedef struct RelOptInfo
{
	NodeTag		type;

	RelOptKind	reloptkind;

	/* all relations included in this RelOptInfo */
	Relids		relids;			/* set of base relids (rangetable indexes) */

	/* size estimates generated by planner */
	double		rows;			/* estimated number of result tuples */

	/* per-relation planner control flags */
	bool		consider_startup;	/* keep cheap-startup-cost paths? */
	bool		consider_param_startup; /* ditto, for parameterized paths? */
	bool		consider_parallel;	/* consider parallel paths? */

	/* default result targetlist for Paths scanning this relation */
	struct PathTarget *reltarget;	/* list of Vars/Exprs, cost, width */

	/* materialization information */
	List	   *pathlist;		/* Path structures */
	List	   *ppilist;		/* ParamPathInfos used in pathlist */
	List	   *partial_pathlist;	/* partial Paths */
	struct Path *cheapest_startup_path;
	struct Path *cheapest_total_path;
	struct Path *cheapest_unique_path;
	List	   *cheapest_parameterized_paths;

	/* parameterization information needed for both base rels and join rels */
	/* (see also lateral_vars and lateral_referencers) */
	Relids		direct_lateral_relids;	/* rels directly laterally referenced */
	Relids		lateral_relids; /* minimum parameterization of rel */

	/* information about a base rel (not set for join rels!) */
	Index		relid;
	Oid			reltablespace;	/* containing tablespace */
	RTEKind		rtekind;		/* RELATION, SUBQUERY, or FUNCTION */
	AttrNumber	min_attr;		/* smallest attrno of rel (often <0) */
	AttrNumber	max_attr;		/* largest attrno of rel */
	Relids	   *attr_needed;	/* array indexed [min_attr .. max_attr] */
	int32	   *attr_widths;	/* array indexed [min_attr .. max_attr] */
	List	   *lateral_vars;	/* LATERAL Vars and PHVs referenced by rel */
	Relids		lateral_referencers;	/* rels that reference me laterally */
	List	   *indexlist;		/* list of IndexOptInfo */
	List	   *statlist;		/* list of StatisticExtInfo */
	BlockNumber pages;			/* size estimates derived from pg_class */
	double		tuples;
	double		allvisfrac;
	PlannerInfo *subroot;		/* if subquery */
	List	   *subplan_params; /* if subquery */
	int			rel_parallel_workers;	/* wanted number of parallel workers */

	/* Information about foreign tables and foreign joins */
	Oid			serverid;		/* identifies server for the table or join */
	Oid			userid;			/* identifies user to check access as */
	bool		useridiscurrent;	/* join is only valid for current user */
	/* use "struct FdwRoutine" to avoid including fdwapi.h here */
	struct FdwRoutine *fdwroutine;
	void	   *fdw_private;

	/* cache space for remembering if we have proven this relation unique */
	List	   *unique_for_rels;	/* known unique for these other relid
									 * set(s) */
	List	   *non_unique_for_rels;	/* known not unique for these set(s) */

	/* used by various scans and joins: */
	List	   *baserestrictinfo;	/* RestrictInfo structures (if base rel) */
	QualCost	baserestrictcost;	/* cost of evaluating the above */
	Index		baserestrict_min_security;	/* min security_level found in
											 * baserestrictinfo */
	List	   *joininfo;		/* RestrictInfo structures for join clauses
								 * involving this rel */
	bool		has_eclass_joins;	/* T means joininfo is incomplete */

	/* used by "other" relations */
	Relids		top_parent_relids;	/* Relids of topmost parents */
} RelOptInfo;

  RelOptInfo 结构体的存在意义是为了存储一个关系的优化信息,这样在查询优化过程中,可以方便地查找和管理每个关系的相关信息。RelOptInfo 结构体中的字段可以用于存储基本关系的属性、约束条件、可选的执行路径等信息,以便优化器可以根据这些信息选择最优的执行计划。同时,它还能记录关系之间的连接关系和关联条件,帮助优化器进行联接路径的选择和优化。
  在 RelOptInfo 中,pathlist 记录了生成该 RelOptInfo 在某方面较优的路径。每个节点都是一个 Path 结构的指针,用于描述扫描表的不同方法(例如顺序扫描、索引扫描)以及元组排序的不同结果Path 结构是路径的一个超类,可以根据 pathtype 字段表示的路径类型转换成具体的路径节点,如 T_IndexScan 对应 IndexPath 数据结构。pathlist 中的每个 Path 节点对应的具体路径节点中存放了构成该路径的具体信息。
  Path 结构体表示一个查询中的一个执行路径。执行路径是优化器生成的可执行计划的一部分,它描述了如何从关系中获取数据的具体方式,如扫描、聚合、排序等。每个 RelOptInfo 结构体都包含了多个 Path 结构体,这些 Path 结构体代表了不同的执行路径选择,优化器会通过比较这些路径的代价来选择最优的执行计划。
  还是先看看书中对Path结构体的描述吧
在这里插入图片描述

  Path结构体源码如下:(路径:src/include/nodes/relation.h

/*
 * Type "Path" is used as-is for sequential-scan paths, as well as some other
 * simple plan types that we don't need any extra information in the path for.
 * For other path types it is the first component of a larger struct.
 *
 * "pathtype" is the NodeTag of the Plan node we could build from this Path.
 * It is partially redundant with the Path's NodeTag, but allows us to use
 * the same Path type for multiple Plan types when there is no need to
 * distinguish the Plan type during path processing.
 *
 * "parent" identifies the relation this Path scans, and "pathtarget"
 * describes the precise set of output columns the Path would compute.
 * In simple cases all Paths for a given rel share the same targetlist,
 * which we represent by having path->pathtarget equal to parent->reltarget.
 *
 * "param_info", if not NULL, links to a ParamPathInfo that identifies outer
 * relation(s) that provide parameter values to each scan of this path.
 * That means this path can only be joined to those rels by means of nestloop
 * joins with this path on the inside.  Also note that a parameterized path
 * is responsible for testing all "movable" joinclauses involving this rel
 * and the specified outer rel(s).
 *
 * "rows" is the same as parent->rows in simple paths, but in parameterized
 * paths and UniquePaths it can be less than parent->rows, reflecting the
 * fact that we've filtered by extra join conditions or removed duplicates.
 *
 * "pathkeys" is a List of PathKey nodes (see above), describing the sort
 * ordering of the path's output rows.
 */
typedef struct Path
{
	NodeTag		type;

	NodeTag		pathtype;		/* tag identifying scan/join method */

	RelOptInfo *parent;			/* the relation this path can build */
	PathTarget *pathtarget;		/* list of Vars/Exprs, cost, width */

	ParamPathInfo *param_info;	/* parameterization info, or NULL if none */

	bool		parallel_aware; /* engage parallel-aware logic? */
	bool		parallel_safe;	/* OK to use as part of parallel plan? */
	int			parallel_workers;	/* desired # of workers; 0 = not parallel */

	/* estimated size/costs for path (see costsize.c for more info) */
	double		rows;			/* estimated number of result tuples */
	Cost		startup_cost;	/* cost expended before fetching any tuples */
	Cost		total_cost;		/* total cost (assuming all tuples fetched) */

	List	   *pathkeys;		/* sort ordering of path's output */
	/* pathkeys is a List of PathKey nodes; see above */
} Path;

  路径是用一个树结构表示的。叶子节点是对基本关系的扫描路径,内部节点是连接路径的节点。例如,图5-27中的树结构是图中SQL语句生成的路径之一。
在这里插入图片描述
  在 Path 生成过程中,在某一方面代价较优的路径(启动代价、总代价等)都存放在RelOptInfo结构的pathlist链表中,如图5-28所示。
在这里插入图片描述
  通过gdb调试可以查看Path结构体内容:
在这里插入图片描述

我们来解读以下这段结构体信息:
  这段代码描述了一个路径(Path)的结构信息,是查询规划器(Planner)生成的一个节点,用于表示查询执行过程中的某种执行方案。下面逐个字段解释其含义:

  • type = T_Path: 表示该节点的类型是 Path,即路径节点。
  • pathtype = T_SeqScan: 表示该路径节点的类型是 T_SeqScan,即顺序扫描的路径,这意味着在执行该路径时,系统将按照表中的物理存储顺序逐行扫描。
  • parent = 0x55ebb49db058: 表示该路径节点所属的父节点的指针,即该路径节点是哪个上级节点的一个子节点。在这里,parent 的值是一个内存地址(指针),指向上级节点的位置。
  • pathtarget = 0x55ebb49dbab0: 表示该路径节点要生成的目标(TargetEntry)的指针。pathtarget 指向一个目标条目,描述了查询执行后返回的结果的信息,如表达式、别名等。
  • param_info = 0x0: 表示该路径节点的参数信息,如果该路径节点没有需要传递的参数,则为 NULL
  • parallel_aware = 0 ‘\000’: 表示该路径是否支持并行执行。parallel_aware0 表示不支持,并行执行。
  • parallel_safe = 1 ‘\001’: 表示该路径是否是并行安全的。parallel_safe1 表示是,并行安全的路径可以被并行执行。
  • parallel_workers = 0: 表示在执行该路径时使用的并行工作者数量。
  • rows = 6: 表示该路径执行后返回的估计行数。
  • startup_cost = 0: 表示该路径的启动代价,即开始执行该路径所需要的额外开销。
  • total_cost = 25.875: 表示该路径的总代价,即执行该路径所需要的总开销。
  • pathkeys = 0x0: 表示该路径执行后返回的结果的排序方式,如果为 NULL,表示结果没有特定的排序要求。

  其中调试信息如下:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/783149.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

进阶C语言——动态内存管理

好久不见&#xff0c;今天我们学习一下C语言的动态内存管理&#xff0c;这是一个和指针一样重要的章节&#xff0c;所以大家一定要好好学这章。 1. 为什么存在动态内存分配 我们已经掌握的内存开辟方式有&#xff1a; int val 20;//在栈空间上开辟四个字节 char arr[10] {0};…

Obsidian同步到Notion

插件介绍 将Obsidian的内容同步到Notion需要使用一个第三方插件"Obsidian shared to Notion"EasyChris/obsidian-to-notion: Share obsidian markdown file to notion and generate notion share link 同步obsdian文件到notion&#xff0c;并生成notion分享链接&am…

数据仓库表设计理论

数据仓库表设计理论 数仓顾名思义是数据仓库&#xff0c;其数据来源大多来自于业务数据(例如:关系型数据库)&#xff0c;当设计数仓中表类型时(拉链表、增量表、全量表、流水表、切片表)时&#xff0c;应先观察业务数据的特点再设计数仓表结构 首先业务数据是会不断增长的-即…

flask介绍、快速使用、配置文件、路由系统

前言: Flask框架和Django框架的区别&#xff1a; Django框架&#xff1a; 大而全&#xff0c;内置的app的很多&#xff0c;第三方app也很多Flask框架&#xff1a; 小而精&#xff0c;没有过多的内置app&#xff0c;只能完成web框架的基本功能&#xff0c;很多功能都需要借助第三…

护城河理论

护城河理论 护城河理论|来自股神巴菲特&#xff0c;是指投资的企业在某一方面的核心竞争力。 模型介绍 在2000年的伯克希尔哈撒韦的年会上&#xff0c;巴菲特说&#xff1a;让我们来把护城河作为一个伟大企业的首要标准&#xff0c;保持它的宽度&#xff0c;保持它不被跨越。我…

【字符流】案例:集合到文件

案例&#xff1a;集合到文件 1.需求&#xff1a; 把ArrayList集合中的字符串数据写入到文本文件。要求&#xff1a;每一个字符串元素作为文件中的一行数据 2.思路 创建ArrayList集合往集合中存储字符串元素创建字符缓冲输出流对象遍历集合&#xff0c;得到每一个字符串数据调…

『Dubbo SPI源码分析』依赖注入机制分析

Dubbo Wrapper 依赖注入机制分析 基于 2.7.0 版本上一章&#xff1a;『Dubbo SPI源码分析』Wrapper 机制分析 创建测试 demo 首先创建一个接口&#xff0c;举例为 Car package com.luban.dubbo_spi.api;SPI public interface Car {public void getColor();public void getC…

数据结构(王道)——数据结构之 由遍历序列构造二叉树

结论&#xff1a;给出遍历序列当中的一种&#xff0c;不能唯一确定一颗二叉树。

[回馈]ASP.NET Core MVC开发实战之商城系统(一)

经过一段时间的准备&#xff0c;新的一期【ASP.NET Core MVC开发实战之商城系统】已经开始&#xff0c;今天着重讲解布局设计&#xff0c;环境搭建&#xff0c;系统配置&#xff0c;及首页商品类型&#xff0c;banner条&#xff0c;友情链接等功能的开发。 首页布局设计 首页是…

Android使用Shape画格子图和圆形

觉得画来玩玩&#xff0c;比较有趣&#xff0c;记录一下。 1格子。 <?xml version"1.0" encoding"utf-8"?> <layer-list xmlns:android"http://schemas.android.com/apk/res/android"><item ><shape><solid andro…

性能测试-Jmeter之Linux下压力测试

我们在做测试的时候&#xff0c;有时候要运行很久&#xff0c;公司用的测试服务器一般都是linux&#xff0c;就可以运行在linux下面&#xff0c;linux下面不能像windows一样有图形化界面&#xff0c;那怎么运行脚本呢&#xff0c;就先在windows上把脚本做好&#xff0c;然后在l…

解决ORACLE PLSQL查询速度慢问题

在表内已建有索引情况下&#xff0c;查询速度有时快&#xff0c;有时慢的问题。 数据库&#xff1a;Oracle&#xff0c; 工具&#xff1a;PlsqlDev 不走索引的原因通常有以下几种&#xff1a; 1.索引失效或丢失&#xff1a;当数据库中的索引被减少、删除或者失效时&#xff0…

浅析JAVA虚拟机结构与机制

本文旨在给所有希望了解 可以看出&#xff0c;JVM主要由类加载器子系统、运行时数据区&#xff08;内存空间&#xff09;、执行引擎以及与本地方法接口等组成。其中运行时数据区又由方法区、堆、Java栈、PC寄存器、本地方法栈组成。 从上图中还可以看出&#xff0c;在内存空间…

Flask 创建文件目录,删除文件目录

项目结构 app.py from flask import Flask, render_template, request, redirect, url_for import osapp Flask(__name__) BASE_DIR os.path.abspath(os.path.dirname(__file__)) FILE_DIR os.path.join(BASE_DIR, testfile)app.route(/, methods[GET, POST]) def index():…

心海舟楫、三一重工面试(部分)

心海舟楫 一道算法题&#xff1a; 我开始给出的是暴力解法&#xff0c;时间复杂度O(n^2)。 在面试官的提示下&#xff0c;实现了时间复杂度为O(n)的解法。 三一重工 没啥特别的

【VTK】VTK 让小球动起来,在 Windows 上使用 Visual Studio 配合 Qt 构建 VTK

知识不是单独的&#xff0c;一定是成体系的。更多我的个人总结和相关经验可查阅这个专栏&#xff1a;Visual Studio。 文章目录 版本环境A.uiA.hA.cppRef. 本文主要目的是在 Qt 界面中&#xff0c;显示出来使用 VTK 构建的小球&#xff0c;并让小球能够动起来。同时为了方便对比…

第2章 SparkSQL 核心编程

第2章 SparkSQL 核心编程 2.1 新的起点2.2 DataFrame2.2.1 创建 DataFrame2.2.2 SQL 语法2.2.3 DSL 语法2.2.4 RDD 转换为 DataFrame2.2.5 DataFrame 转换为 RDD 2.3 DataSet2.3.1 创建 DataSet2.3.2 RDD 转换为 DataSet2.3.3 DataSet 转换为 RDD 2.4 DataFrame 和 DataSet 转…

学习记录681@Gitlab升级实战

前言 我的Linux目前是centos8&#xff0c;目前使用的gitlab是从https://mirrors.tuna.tsinghua.edu.cn/ 下载下来的gitlab-ce-12.10.1-ce.0.el8.x86_64.rpm&#xff0c;然后安装的。 这里需要注意如果是centos8需要下载el8的gitlab&#xff0c;如果是centos7需要下载el7的git…

golang - 下载大文件,实时返回前端下载进度,实现下载进度条

示例&#xff1a; package mainimport ("fmt""io""net/http""os""path"//"github.com/kataras/iris""github.com/kataras/iris/v12""time" )func doSomething() {time.Sleep(time.Second * …

大数据学习04-Hbase分布式集群部署

系统环境&#xff1a;centos7 软件版本&#xff1a;jdk1.8、zookeeper3.4.8、hadoop2.8.5 一、下载 HBASE官网 cd /home/toolswget https://archive.apache.org/dist/hbase/2.2.4/hbase-2.2.4-bin.tar.gz二、解压 tar -zxvf hbase-2.2.4-bin.tar.gz -C /home/local/移动目…