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

PostgreSQL源码分析——索引扫描

这里,我们分析一下索引扫描的过程,以最简单的select * from t1 where a = 100;语句为例,分析一下查询的过程。

postgres@postgres=# \d t1;Table "public.t1"Column |  Type   | Collation | Nullable | Default 
--------+---------+-----------+----------+---------a      | integer |           | not null | b      | integer |           |          | 
Indexes:"t1_pkey" PRIMARY KEY, btree (a)-- 顺序扫描
postgres@postgres=# explain select * from t1;QUERY PLAN                      
------------------------------------------------------Seq Scan on t1  (cost=0.00..15.00 rows=1000 width=8)
(1 row)-- 索引扫描
postgres@postgres=# explain select * from t1 where a = 100;QUERY PLAN                            
------------------------------------------------------------------Index Scan using t1_pkey on t1  (cost=0.28..2.29 rows=1 width=8)Index Cond: (a = 100)
(2 rows)
语法解析以及语义分析

这部分前面已有分析过,这里简要描述:

解析层面流程如下:

exec_simple_query
--> pg_parse_query              // 生成抽象语法树--> raw_parser--> base_yyparse
--> pg_analyze_and_rewrite      // 语义分析,转为查询树Query

语法表示可查看gram.y中的定义。

simple_select:SELECT opt_all_clause opt_target_list   // select *into_clause from_clause where_clause    // from t1 where a = 100;group_clause having_clause window_clause{SelectStmt *n = makeNode(SelectStmt);// ......$$ = (Node *)n;}
from_clause:FROM from_list							{ $$ = $2; }| /*EMPTY*/								{ $$ = NIL; };from_list:table_ref								{ $$ = list_make1($1); }| from_list ',' table_ref				{ $$ = lappend($1, $3); };
// 表示t1
table_ref:	relation_expr opt_alias_clause{$1->alias = $2;$$ = (Node *) $1;}
where_clause:WHERE a_expr							{ $$ = $2; }	// 表示 where a = 100;| /*EMPTY*/								{ $$ = NULL; };

关键数据结构:

// select语句的抽象语法树表示
typedef struct SelectStmt
{NodeTag		type;// 对应select *List	   *targetList;		/* the target list (of ResTarget) */ // 对应         from t1List	   *fromClause;		/* the FROM clause */  // 保存RangeVar节点// ...... } SelectStmt;/** RangeVar - range variable, used in FROM clauses** Also used to represent table names in utility statements; there, the alias* field is not used, and inh tells whether to apply the operation* recursively to child tables.  In some contexts it is also useful to carry* a TEMP table indication here.*/
typedef struct RangeVar
{NodeTag		type;char	   *catalogname;	/* the catalog (database) name, or NULL */char	   *schemaname;		/* the schema name, or NULL */char	   *relname;		/* the relation/sequence name */bool		inh;			/* expand rel by inheritance? recursively act* on children? */char		relpersistence; /* see RELPERSISTENCE_* in pg_class.h */Alias	   *alias;			/* table alias & optional column aliases */int			location;		/* token location, or -1 if unknown */
} RangeVar;    // 存储表的所在库名,模式名,表名信息语义分析流程:
```c
pg_analyze_and_rewrite   // 语义分析,生成查询树Query
--> pg_analyze--> transformStmt--> transformSelectStmt--> transformFromClause  // 处理表, 将RangeVar转为RangeTblEntry--> transformFromClauseItem--> transformTableEntry     --> transformTargetList     // 处理 * ,展开为a, b--> transformWhereClause    // 处理 where a = 100
--> pg_rewrite_query
查询优化

主要是生成执行计划,前面已经通过EXPLAIN命令得知其执行计划为索引扫描,让我们具体看一下其是如何生成的。这里选择率以及代价估算先不进行分析,后续单独进行分析。

pg_plan_queries
--> pg_plan_query--> planner--> standard_planner--> subquery_planner--> grouping_planner--> query_planner 		// Generate a path (that is, a simplified plan) for a basic query--> setup_simple_rel_arrays--> add_base_rels_to_query--> build_simple_rel--> generate_base_implied_equalities(root);--> generate_base_implied_equalities_const--> distribute_restrictinfo_to_rels	// 选择下推, where a = 100 , 绑定到表t1中--> find_base_rel--> rel->baserestrictinfo = lappend(rel->baserestrictinfo, restrictinfo);--> extract_restriction_or_clauses--> make_one_rel	// Finds all possible access paths for executing a query--> set_base_rel_sizes--> set_rel_size--> set_plain_rel_size--> set_baserel_size_estimates	// 选择率计算,计算代价Cost要用--> clauselist_selectivity // 选择率计算, 会将表以及where a = 100限制条件传进去进行计算--> clauselist_selectivity_simple--> clause_selectivity--> restriction_selectivity /* Estimate selectivity for a restriction clause. */--> eqsel--> eqsel_internal--> var_eq_const--> get_variable_numdistinct--> set_base_rel_pathlists--> set_rel_pathlist // Build access paths for a base relation--> create_seqscan_path // 生成顺序扫描路径--> create_index_paths	 // 索引扫描路径--> get_index_paths--> build_index_paths--> check_index_only  // 检查是否可以IndexOnlyScan,仅索引扫描--> make_rel_from_joinlist--> apply_scanjoin_target_to_paths--> create_plan--> create_scan_plan--> create_indexscan_plan--> if indexonly--> make_indexonlyscanelse--> make_indexscan

这里有个函数要重点说明一下,在涉及多表路径规划时,单表路径是最基础的,单表路径可以是顺序扫描、索引扫描、TID扫描等,这个是最底层的。

/* Build access paths for a plain relation (no subquery, no inheritance) */
void set_plain_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, RangeTblEntry *rte)
{Relids		required_outer;/* We don't support pushing join clauses into the quals of a seqscan, but* it could still have required parameterization due to LATERAL refs in its tlist. */required_outer = rel->lateral_relids;/* Consider sequential scan */add_path(rel, create_seqscan_path(root, rel, required_outer, 0));	// 顺序扫描/* If appropriate, consider parallel sequential scan */if (rel->consider_parallel && required_outer == NULL)		// 尝试并行顺序扫描create_plain_partial_paths(root, rel);/* Consider index scans */create_index_paths(root, rel);			// 索引扫描/* Consider TID scans */create_tidscan_paths(root, rel);      // TID扫描
}

最后由最佳路径Path生成顺序扫描执行计划SeqScan

/* ==========* Scan nodes* ========== */
typedef struct Scan
{Plan		plan;// 指明扫描那个表,Index		scanrelid;		/* relid is index into the range table */
} Scan;/* ----------------*		sequential scan node* ---------------- */
typedef Scan SeqScan;

其中有个非常重要的函数, 这个函数有个细节,就是IndexOnlyScan,在这里会进行一个判断,是否可以进行IndexOnly扫描,判断条件时target列是否仅索引列,如果满足的话,则可以用IndexOnlyScan。当然前提是enable_indexonlyscan = true。比如下面的语句:

postgres@postgres=# show enable_indexonlyscan ;enable_indexonlyscan 
----------------------on
(1 row)-- Index Only Scan
postgres@postgres=# explain select a from t1 where a = 100;QUERY PLAN                               
-----------------------------------------------------------------------Index Only Scan using t1_pkey on t1  (cost=0.28..2.29 rows=1 width=4)Index Cond: (a = 100)
(2 rows)
-- 关闭 enable_indexonlyscan后,不能进行 IndexOnlyScan
postgres@postgres=# set enable_indexonlyscan = false;
SET
postgres@postgres=# explain select a from t1 where a = 100;QUERY PLAN                            
------------------------------------------------------------------Index Scan using t1_pkey on t1  (cost=0.28..2.29 rows=1 width=4)Index Cond: (a = 100)
(2 rows)

我们继续看一下如何构造IndexPath

/** build_index_paths*	  Given an index and a set of index clauses for it, construct zero*	  or more IndexPaths. It also constructs zero or more partial IndexPaths.** We return a list of paths because (1) this routine checks some cases* that should cause us to not generate any IndexPath, and (2) in some* cases we want to consider both a forward and a backward scan, so as* to obtain both sort orders.  Note that the paths are just returned* to the caller and not immediately fed to add_path().** At top level, useful_predicate should be exactly the index's predOK flag* (ie, true if it has a predicate that was proven from the restriction* clauses).  When working on an arm of an OR clause, useful_predicate* should be true if the predicate required the current OR list to be proven.* Note that this routine should never be called at all if the index has an* unprovable predicate.** scantype indicates whether we want to create plain indexscans, bitmap* indexscans, or both.  When it's ST_BITMAPSCAN, we will not consider* index ordering while deciding if a Path is worth generating.** If skip_nonnative_saop is non-NULL, we ignore ScalarArrayOpExpr clauses* unless the index AM supports them directly, and we set *skip_nonnative_saop* to true if we found any such clauses (caller must initialize the variable* to false).  If it's NULL, we do not ignore ScalarArrayOpExpr clauses.** If skip_lower_saop is non-NULL, we ignore ScalarArrayOpExpr clauses for* non-first index columns, and we set *skip_lower_saop to true if we found* any such clauses (caller must initialize the variable to false).  If it's* NULL, we do not ignore non-first ScalarArrayOpExpr clauses, but they will* result in considering the scan's output to be unordered.** 'rel' is the index's heap relation* 'index' is the index for which we want to generate paths* 'clauses' is the collection of indexable clauses (IndexClause nodes)* 'useful_predicate' indicates whether the index has a useful predicate* 'scantype' indicates whether we need plain or bitmap scan support* 'skip_nonnative_saop' indicates whether to accept SAOP if index AM doesn't* 'skip_lower_saop' indicates whether to accept non-first-column SAOP*/
static List *
build_index_paths(PlannerInfo *root, RelOptInfo *rel,IndexOptInfo *index, IndexClauseSet *clauses,bool useful_predicate,ScanTypeControl scantype,bool *skip_nonnative_saop,bool *skip_lower_saop)
{List	   *result = NIL;IndexPath  *ipath;List	   *index_clauses;Relids		outer_relids;double		loop_count;List	   *orderbyclauses;List	   *orderbyclausecols;List	   *index_pathkeys;List	   *useful_pathkeys;bool		found_lower_saop_clause;bool		pathkeys_possibly_useful;bool		index_is_ordered;bool		index_only_scan;int			indexcol;/** Check that index supports the desired scan type(s)*/switch (scantype){case ST_INDEXSCAN:if (!index->amhasgettuple)return NIL;break;case ST_BITMAPSCAN:if (!index->amhasgetbitmap)return NIL;break;case ST_ANYSCAN:/* either or both are OK */break;}/** 1. Combine the per-column IndexClause lists into an overall list.** In the resulting list, clauses are ordered by index key, so that the* column numbers form a nondecreasing sequence.  (This order is depended* on by btree and possibly other places.)  The list can be empty, if the* index AM allows that.** found_lower_saop_clause is set true if we accept a ScalarArrayOpExpr* index clause for a non-first index column.  This prevents us from* assuming that the scan result is ordered.  (Actually, the result is* still ordered if there are equality constraints for all earlier* columns, but it seems too expensive and non-modular for this code to be* aware of that refinement.)** We also build a Relids set showing which outer rels are required by the* selected clauses.  Any lateral_relids are included in that, but not* otherwise accounted for.*/index_clauses = NIL;found_lower_saop_clause = false;outer_relids = bms_copy(rel->lateral_relids);for (indexcol = 0; indexcol < index->nkeycolumns; indexcol++){ListCell   *lc;foreach(lc, clauses->indexclauses[indexcol]){IndexClause *iclause = (IndexClause *) lfirst(lc);RestrictInfo *rinfo = iclause->rinfo;/* We might need to omit ScalarArrayOpExpr clauses */if (IsA(rinfo->clause, ScalarArrayOpExpr)){if (!index->amsearcharray){if (skip_nonnative_saop){/* Ignore because not supported by index */*skip_nonnative_saop = true;continue;}/* Caller had better intend this only for bitmap scan */Assert(scantype == ST_BITMAPSCAN);}if (indexcol > 0){if (skip_lower_saop){/* Caller doesn't want to lose index ordering */*skip_lower_saop = true;continue;}found_lower_saop_clause = true;}}/* OK to include this clause */index_clauses = lappend(index_clauses, iclause);outer_relids = bms_add_members(outer_relids,rinfo->clause_relids);}/** If no clauses match the first index column, check for amoptionalkey* restriction.  We can't generate a scan over an index with* amoptionalkey = false unless there's at least one index clause.* (When working on columns after the first, this test cannot fail. It* is always okay for columns after the first to not have any* clauses.)*/if (index_clauses == NIL && !index->amoptionalkey)return NIL;}/* We do not want the index's rel itself listed in outer_relids */outer_relids = bms_del_member(outer_relids, rel->relid);/* Enforce convention that outer_relids is exactly NULL if empty */if (bms_is_empty(outer_relids))outer_relids = NULL;/* Compute loop_count for cost estimation purposes */loop_count = get_loop_count(root, rel->relid, outer_relids);/** 2. Compute pathkeys describing index's ordering, if any, then see how* many of them are actually useful for this query.  This is not relevant* if we are only trying to build bitmap indexscans, nor if we have to* assume the scan is unordered.*/pathkeys_possibly_useful = (scantype != ST_BITMAPSCAN &&!found_lower_saop_clause &&has_useful_pathkeys(root, rel));index_is_ordered = (index->sortopfamily != NULL);if (index_is_ordered && pathkeys_possibly_useful){index_pathkeys = build_index_pathkeys(root, index,ForwardScanDirection);useful_pathkeys = truncate_useless_pathkeys(root, rel,index_pathkeys);orderbyclauses = NIL;orderbyclausecols = NIL;}else if (index->amcanorderbyop && pathkeys_possibly_useful){/* see if we can generate ordering operators for query_pathkeys */match_pathkeys_to_index(index, root->query_pathkeys,&orderbyclauses,&orderbyclausecols);if (orderbyclauses)useful_pathkeys = root->query_pathkeys;elseuseful_pathkeys = NIL;}else{useful_pathkeys = NIL;orderbyclauses = NIL;orderbyclausecols = NIL;}// 这里非常重要,检查是否可以进行index-only scan/** 3. Check if an index-only scan is possible.  If we're not building* plain indexscans, this isn't relevant since bitmap scans don't support* index data retrieval anyway.*/index_only_scan = (scantype != ST_BITMAPSCAN &&check_index_only(rel, index));/** 4. Generate an indexscan path if there are relevant restriction clauses* in the current clauses, OR the index ordering is potentially useful for* later merging or final output ordering, OR the index has a useful* predicate, OR an index-only scan is possible.*/if (index_clauses != NIL || useful_pathkeys != NIL || useful_predicate ||index_only_scan){ipath = create_index_path(root, index,index_clauses,orderbyclauses,orderbyclausecols,useful_pathkeys,index_is_ordered ?ForwardScanDirection :NoMovementScanDirection,index_only_scan,outer_relids,loop_count,false);result = lappend(result, ipath);/** If appropriate, consider parallel index scan.  We don't allow* parallel index scan for bitmap index scans.*/if (index->amcanparallel &&rel->consider_parallel && outer_relids == NULL &&scantype != ST_BITMAPSCAN){ipath = create_index_path(root, index,index_clauses,orderbyclauses,orderbyclausecols,useful_pathkeys,index_is_ordered ?ForwardScanDirection :NoMovementScanDirection,index_only_scan,outer_relids,loop_count,true);/** if, after costing the path, we find that it's not worth using* parallel workers, just free it.*/if (ipath->path.parallel_workers > 0)add_partial_path(rel, (Path *) ipath);elsepfree(ipath);}}/** 5. If the index is ordered, a backwards scan might be interesting.*/if (index_is_ordered && pathkeys_possibly_useful){index_pathkeys = build_index_pathkeys(root, index,BackwardScanDirection);useful_pathkeys = truncate_useless_pathkeys(root, rel,index_pathkeys);if (useful_pathkeys != NIL){ipath = create_index_path(root, index,index_clauses,NIL,NIL,useful_pathkeys,BackwardScanDirection,index_only_scan,outer_relids,loop_count,false);result = lappend(result, ipath);/* If appropriate, consider parallel index scan */if (index->amcanparallel &&rel->consider_parallel && outer_relids == NULL &&scantype != ST_BITMAPSCAN){ipath = create_index_path(root, index,index_clauses,NIL,NIL,useful_pathkeys,BackwardScanDirection,index_only_scan,outer_relids,loop_count,true);/** if, after costing the path, we find that it's not worth* using parallel workers, just free it.*/if (ipath->path.parallel_workers > 0)add_partial_path(rel, (Path *) ipath);elsepfree(ipath);}}}return result;
}
执行器

输入执行计划,输出最终结果。执行索引扫描。

主流程
PortalStart
--> ExecutorStart--> InitPlan--> ExecInitIndexScan
PortalRun
--> ExecutorRun--> ExecutePlan--> ExecIndexScan--> ExecScan--> ExecScanFetch--> IndexNext--> index_beginscan  --> index_beginscan_internal  --> btbeginscan	//start a scan on a btree index--> table_index_fetch_begin--> heapam_index_fetch_begin--> index_getnext_slot--> index_getnext_tid--> btgettuple
PortalDrop

具体实现代码如下:

/* ----------------------------------------------------------------*		ExecIndexScan(node)* ----------------------------------------------------------------*/
static TupleTableSlot *
ExecIndexScan(PlanState *pstate)
{IndexScanState *node = castNode(IndexScanState, pstate);/** If we have runtime keys and they've not already been set up, do it now.*/if (node->iss_NumRuntimeKeys != 0 && !node->iss_RuntimeKeysReady)ExecReScan((PlanState *) node);if (node->iss_NumOrderByKeys > 0)return ExecScan(&node->ss,(ExecScanAccessMtd) IndexNextWithReorder,(ExecScanRecheckMtd) IndexRecheck);elsereturn ExecScan(&node->ss,(ExecScanAccessMtd) IndexNext,(ExecScanRecheckMtd) IndexRecheck);
}/** ExecScanFetch -- check interrupts & fetch next potential tuple** This routine is concerned with substituting a test tuple if we are* inside an EvalPlanQual recheck.  If we aren't, just execute* the access method's next-tuple routine.*/
static inline TupleTableSlot *ExecScanFetch(ScanState *node, ExecScanAccessMtd accessMtd, ExecScanRecheckMtd recheckMtd)
{EState	   *estate = node->ps.state;if (estate->es_epq_active != NULL){// ...}/* Run the node-type-specific access method function to get the next tuple */return (*accessMtd) (node);
}/* ----------------------------------------------------------------*		IndexNext**		Retrieve a tuple from the IndexScan node's currentRelation*		using the index specified in the IndexScanState information.* ---------------------------------------------------------------- */
static TupleTableSlot *IndexNext(IndexScanState *node)
{EState	   *estate;ExprContext *econtext;ScanDirection direction;IndexScanDesc scandesc;TupleTableSlot *slot;/* extract necessary information from index scan node */estate = node->ss.ps.state;direction = estate->es_direction;/* flip direction if this is an overall backward scan */if (ScanDirectionIsBackward(((IndexScan *) node->ss.ps.plan)->indexorderdir)){if (ScanDirectionIsForward(direction))direction = BackwardScanDirection;else if (ScanDirectionIsBackward(direction))direction = ForwardScanDirection;}scandesc = node->iss_ScanDesc;econtext = node->ss.ps.ps_ExprContext;slot = node->ss.ss_ScanTupleSlot;if (scandesc == NULL){/** We reach here if the index scan is not parallel, or if we're* serially executing an index scan that was planned to be parallel.*/scandesc = index_beginscan(node->ss.ss_currentRelation,node->iss_RelationDesc,estate->es_snapshot,node->iss_NumScanKeys,node->iss_NumOrderByKeys);node->iss_ScanDesc = scandesc;/** If no run-time keys to calculate or they are ready, go ahead and* pass the scankeys to the index AM.*/if (node->iss_NumRuntimeKeys == 0 || node->iss_RuntimeKeysReady)index_rescan(scandesc,node->iss_ScanKeys, node->iss_NumScanKeys,node->iss_OrderByKeys, node->iss_NumOrderByKeys);}/* ok, now that we have what we need, fetch the next tuple. */while (index_getnext_slot(scandesc, direction, slot)){CHECK_FOR_INTERRUPTS();/* If the index was lossy, we have to recheck the index quals using the fetched tuple. */if (scandesc->xs_recheck){econtext->ecxt_scantuple = slot;if (!ExecQualAndReset(node->indexqualorig, econtext)){/* Fails recheck, so drop it and loop back for another */InstrCountFiltered2(node, 1);continue;}}return slot;}/* if we get here it means the index scan failed so we are at the end of the scan.. */node->iss_ReachedEnd = true;return ExecClearTuple(slot);
}
索引扫描

索引与表一样,单独存储,同样是被切分成很多页,索引扫描,先访问索引(访问索引所在页,在索引页中继续找,找到匹配的键,获取被查找的表的元组的TID,再根据该TID去访问表的某页,根据页偏移找到指定元组返回)

/* Descriptor for heap table scans.*/
typedef struct HeapScanDescData
{TableScanDescData rs_base;	/* AM independent part of the descriptor *//* state set up at initscan time */BlockNumber rs_nblocks;		/* total number of blocks in rel */BlockNumber rs_startblock;	/* block # to start at */BlockNumber rs_numblocks;	/* max number of blocks to scan *//* rs_numblocks is usually InvalidBlockNumber, meaning "scan whole rel" *//* scan current state */bool		rs_inited;		/* false = scan not init'd yet */BlockNumber rs_cblock;		/* current block # in scan, if any */Buffer		rs_cbuf;		/* current buffer in scan, if any *//* NB: if rs_cbuf is not InvalidBuffer, we hold a pin on that buffer *//* rs_numblocks is usually InvalidBlockNumber, meaning "scan whole rel" */BufferAccessStrategy rs_strategy;	/* access strategy for reads */HeapTupleData rs_ctup;		/* current tuple in scan, if any *//* these fields only used in page-at-a-time mode and for bitmap scans */int			rs_cindex;		/* current tuple's index in vistuples */int			rs_ntuples;		/* number of visible tuples on page */OffsetNumber rs_vistuples[MaxHeapTuplesPerPage];	/* their offsets */
}			HeapScanDescData;
typedef struct HeapScanDescData *HeapScanDesc;

更详细的代码要阅读nbtree.c中的实现。

/* btgettuple() -- Get the next tuple in the scan. */
bool btgettuple(IndexScanDesc scan, ScanDirection dir)
{BTScanOpaque so = (BTScanOpaque) scan->opaque;bool		res;/* btree indexes are never lossy */scan->xs_recheck = false;/** If we have any array keys, initialize them during first call for a* scan.  We can't do this in btrescan because we don't know the scan* direction at that time.*/if (so->numArrayKeys && !BTScanPosIsValid(so->currPos)){/* punt if we have any unsatisfiable array keys */if (so->numArrayKeys < 0)return false;_bt_start_array_keys(scan, dir);}/* This loop handles advancing to the next array elements, if any */do{/** If we've already initialized this scan, we can just advance it in* the appropriate direction.  If we haven't done so yet, we call* _bt_first() to get the first item in the scan.*/if (!BTScanPosIsValid(so->currPos))res = _bt_first(scan, dir);else{/** Check to see if we should kill the previously-fetched tuple.*/if (scan->kill_prior_tuple){/** Yes, remember it for later. (We'll deal with all such* tuples at once right before leaving the index page.)  The* test for numKilled overrun is not just paranoia: if the* caller reverses direction in the indexscan then the same* item might get entered multiple times. It's not worth* trying to optimize that, so we don't detect it, but instead* just forget any excess entries.*/if (so->killedItems == NULL)so->killedItems = (int *)palloc(MaxTIDsPerBTreePage * sizeof(int));if (so->numKilled < MaxTIDsPerBTreePage)so->killedItems[so->numKilled++] = so->currPos.itemIndex;}/** Now continue the scan.*/res = _bt_next(scan, dir);}/* If we have a tuple, return it ... */if (res)break;/* ... otherwise see if we have more array keys to deal with */} while (so->numArrayKeys && _bt_advance_array_keys(scan, dir));return res;
}

相关文章:

PostgreSQL源码分析——索引扫描

这里&#xff0c;我们分析一下索引扫描的过程&#xff0c;以最简单的select * from t1 where a 100;语句为例&#xff0c;分析一下查询的过程。 postgrespostgres# \d t1;Table "public.t1"Column | Type | Collation | Nullable | Default ------------------…...

零基础入门学用Arduino 第四部分(一)

重要的内容写在前面&#xff1a; 该系列是以up主太极创客的零基础入门学用Arduino教程为基础制作的学习笔记。个人把这个教程学完之后&#xff0c;整体感觉是很好的&#xff0c;如果有条件的可以先学习一些相关课程&#xff0c;学起来会更加轻松&#xff0c;相关课程有数字电路…...

x-anylabelimg如何标识人脸

软件地址&#xff0c;下载CPU版本就好 https://github.com/CVHub520/X-AnyLabeling/releases/tag/v2.0.0 一、打开软件选择的一个按钮&#xff0c;选择文件夹 二、选择模型运行 未下载的模型需要安全上网下载 选用Yolov6Lite_l-Face MeiTuan生成的文件格式&#xff0c;略作调…...

Element-ui中Table表格无法显示

Element-ui中Table表格无法显示 在使用过程中发现样式正常显示但是table就是不显示&#xff0c;研究了一段时间后&#xff0c;发现问题是项目结构的问题 当你创建vue和安装el的时候&#xff0c;一定要注意进入到正确的项目文件夹&#xff0c;如果在外面也出现一个package.jso…...

电信网关配置管理系统 del_file.php 前台RCE漏洞复现

0x01 产品简介 中国电信集团有限公司(英文名称“China Telecom”、简称“中国电信”)成立于2000年9月,是中国特大型国有通信企业、上海世博会全球合作伙伴。电信网关配置管理系统是一个用于管理和配置电信网络中网关设备的软件系统。它可以帮助网络管理员实现对网关设备的远…...

游戏心理学Day18

游戏玩家心理 在游戏世界中&#xff0c;设计师的工作总是围绕尽可能留住玩家要展开。在游戏创作时&#xff0c;设计师会假设目标诉讼的特点并激励迎合他们的需求&#xff0c;如果这种假设是经过实际调研之后做出的&#xff0c;那么就会比较接近实际情况而。如果这种假设是设计…...

发文章不违规的5种解决方案,非常适用,记得收藏!

之前以为使用AI写出来的文章&#xff0c;只要检测通过就不会违规&#xff0c;结果却还是让我有些失望。最近测试几款AI工具&#xff0c;测试结果都还是会存在违规情况&#xff0c;无法全文发布。 AI是听从人的指令&#xff0c;只能说是如何下指令&#xff0c;这个非常重要。至…...

【ARMv8/ARMv9 硬件加速系列 2.2 -- ARM NEON 的加减乘除(左移右移)运算】

文章目录 NEON 加减乘除NEON 加减乘除 下面代码是使用ARMv8汇编语言对向量寄存器v0-v31执行加、减、乘以及左移和右移操作的示例。 ARMv8的SIMD指令集允许对向量寄存器中的多个数据进行并行操作。v0和v1加载数据,对它们进行加、减和乘,左移和右移操作。最后,我们会将结果存储…...

[2024-06]-[大模型]-[Ollama]- WebUI

主要涉及要部署的前端webui是来源于:https://github.com/open-webui/open-webui 正常就使用: docker run -d -p 3000:8080 --add-host=host.docker.internal:host-gateway -v open-webui:/app/backend/data --name open-webui --restart always ghcr.io/open-webui/open-web…...

AI智能盒子助力中钢天源设备工厂升级安全防护

中钢集团安徽天源科技股份有限公司成立于2002年3月27日,是中央企业中国中钢股份有限公司控股的上市公司&#xff0c;主导产品为永磁铁氧体器件、钕铁硼器件、四氧化三锰、锶铁氧体预烧料及各类磁选机等。 在中钢天源智能化升级过程中&#xff0c;采用并定制开发一系列厂区安全…...

RNN的变种们:GRULSTM双向RNN

上篇笔记记录到RNN的一个缺点&#xff1a;训练时会出现梯度消失&#xff0c;解决的办法是找到一个更优的计算单元。这里也有GRU和LSTM。 GRU&#xff08;Gated Recurrent Unit&#xff09;门控训练网络 什么是门控机制&#xff1f;就是对当前的输入进行一个筛选。门打开&…...

Linux网络-HttpServer的实现

文章目录 前言一、请求报文的解析URL的解析 二、响应报文的发送Content-LenthConten-TypeCookie和Set-CookieCookie的风险 三、尝试发送一个HTML网页404网页Location 重定向 四、浏览器的多次请求行为总结 前言 之前我们简单理解了一下Http协议&#xff0c;本章我们将在LInux下…...

GPT-4o的综合评估与前景展望

如何评价GPT-4o? GPT-4o作为OpenAI推出的最新一代大型语言模型&#xff0c;其性能、功能和应用前景都备受关注。以下是对GPT-4o的综合评估与前景展望&#xff1a; 一、技术性能评估 响应速度&#xff1a;GPT-4o在响应速度上有了显著提升&#xff0c;能够在极短的时间内对输入…...

私人云盘(自动云同步)

一、项目简介 模仿小米的云服务&#xff0c;实现一个通过TCP实现的私人云盘&#xff0c;因为能力有限&#xff0c;所以只实现自动云同步这一个功能&#xff0c;具体可以分为三个小功能&#xff0c;即保持云端和终端数据一致、实现文件的上传与下载以及手动同步 二、涉及到的知…...

【CMake】Linux 下权限丢失与软链接失效问题

【CMake】Linux 下权限丢失与软链接失效问题 文章目录 【CMake】Linux 下权限丢失与软链接失效问题问题概述解决方法1 - 安装目录2 - 安装文件3 - 手动指定 使用 Linux 下原生命令行注意事项参考链接 问题概述 一般使用 CMake 安装&#xff0c;在 Windows 平台不会出问题&…...

内部类介绍

内部类&#xff08;Inner Class&#xff09;是在另一个类的内部定义的类。它可以访问外部类的所有成员&#xff0c;包括私有成员。内部类有两种主要形式&#xff1a;局部内部类&#xff08;定义在方法内部&#xff09;和成员内部类&#xff08;定义在类的内部&#xff0c;但不在…...

【CVPR2021】LoFTR:基于Transformers的无探测器的局部特征匹配方法

LoFTR&#xff1a;基于Transformers的局部检测器 0. 摘要 我们提出了一种新的局部图像特征匹配方法。我们建议先在粗略级别建立像素级密集匹配&#xff0c;然后再在精细级别细化良好匹配&#xff0c;而不是按顺序进行图像特征检测、描述和匹配。与使用成本体积搜索对应关系的密…...

总结一下 C# 如何自定义特性 Attribute 并进行应用

前言 Attribute&#xff08;特性&#xff09;是一种用于为程序元素&#xff08;如类、方法、属性等&#xff09;提供元数据信息的方法。 特性是一种声明式的信息&#xff0c;附加到程序元素上&#xff0c;提供额外的数据用于描述和控制这些元素的行为。 在编译和运行时&…...

三种暴露方法和引入方式

1.分别暴露 export ...export...用于按需导出一个或多个模块&#xff0c;在导入时需要使用花括号指定导入的模块名称&#xff0c;例如import { a, b } from module module.js中 export let a 1 export function b(){console.log(hello,vue) } 2.统一暴露 export { ...}用于统…...

“Git之道:掌握常用命令,轻松管理代码“

目录 1. 初始化和配置 2. 提交和更新 3. 分支和合并 4. 查看和比较 5. 远程仓库 6. 文件操作命令 1. 初始化和配置 git init&#xff1a;在当前目录初始化一个新的Git仓库git config&#xff1a;配置Git的全局或局部选项git clone&#xff1a;从远程仓库克隆一个本地副本…...

css实现圆环展示百分比,根据值动态展示所占比例

代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...

2024年赣州旅游投资集团社会招聘笔试真

2024年赣州旅游投资集团社会招聘笔试真 题 ( 满 分 1 0 0 分 时 间 1 2 0 分 钟 ) 一、单选题(每题只有一个正确答案,答错、不答或多答均不得分) 1.纪要的特点不包括()。 A.概括重点 B.指导传达 C. 客观纪实 D.有言必录 【答案】: D 2.1864年,()预言了电磁波的存在,并指出…...

生成 Git SSH 证书

&#x1f511; 1. ​​生成 SSH 密钥对​​ 在终端&#xff08;Windows 使用 Git Bash&#xff0c;Mac/Linux 使用 Terminal&#xff09;执行命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" ​​参数说明​​&#xff1a; -t rsa&#x…...

【决胜公务员考试】求职OMG——见面课测验1

2025最新版&#xff01;&#xff01;&#xff01;6.8截至答题&#xff0c;大家注意呀&#xff01; 博主码字不易点个关注吧,祝期末顺利~~ 1.单选题(2分) 下列说法错误的是:&#xff08; B &#xff09; A.选调生属于公务员系统 B.公务员属于事业编 C.选调生有基层锻炼的要求 D…...

DBAPI如何优雅的获取单条数据

API如何优雅的获取单条数据 案例一 对于查询类API&#xff0c;查询的是单条数据&#xff0c;比如根据主键ID查询用户信息&#xff0c;sql如下&#xff1a; select id, name, age from user where id #{id}API默认返回的数据格式是多条的&#xff0c;如下&#xff1a; {&qu…...

NLP学习路线图(二十三):长短期记忆网络(LSTM)

在自然语言处理(NLP)领域,我们时刻面临着处理序列数据的核心挑战。无论是理解句子的结构、分析文本的情感,还是实现语言的翻译,都需要模型能够捕捉词语之间依时序产生的复杂依赖关系。传统的神经网络结构在处理这种序列依赖时显得力不从心,而循环神经网络(RNN) 曾被视为…...

回溯算法学习

一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

C/C++ 中附加包含目录、附加库目录与附加依赖项详解

在 C/C 编程的编译和链接过程中&#xff0c;附加包含目录、附加库目录和附加依赖项是三个至关重要的设置&#xff0c;它们相互配合&#xff0c;确保程序能够正确引用外部资源并顺利构建。虽然在学习过程中&#xff0c;这些概念容易让人混淆&#xff0c;但深入理解它们的作用和联…...

云原生安全实战:API网关Kong的鉴权与限流详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关&#xff08;API Gateway&#xff09; API网关是微服务架构中的核心组件&#xff0c;负责统一管理所有API的流量入口。它像一座…...