当前位置: 首页 > 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;从远程仓库克隆一个本地副本…...

Linux vim 文本编辑 操作文本 三种模式

介绍 vi 是一个经典的行编辑器&#xff0c;支持模式编辑&#xff08;包括普通模式、插入模式和命令模式&#xff09;。 vim 保留vi核心功能的基础上&#xff0c;增加了多级撤销、语法高亮、插件支持等高级功能。 两者的最大区别&#xff0c;简单的来说vim就是vi的增强版 三…...

JavaFX DatePicker

JavaFX DatePicker允许从给定日历中选择一天。DatePicker控件包含一个带有日期字段和日期选择器的组合框。JavaFX DatePicker控件使用JDK8日期时间API。 import javafx.application.Application; import javafx.scene.Scene; import javafx.scene.control.DatePicker; import j…...

开展“安全生产月”活动向媒体投稿的好方法找到了

作为一名单位的信息宣传员,我的职责是确保每一次重要活动的声音都能准确无误地传达到社会的每一个角落。在这样的使命驱动下,我曾一度陷入了一种传统的投稿模式——依赖电子邮件,将精心准备的稿件一封封地发送给各大媒体。初入此行,我满心以为这便是信息传播的路径,却未料到,这…...

商讯杂志商讯杂志社商讯编辑部2024年第10期目录

案例分享 基于胜任素质的干部选拔和梯队建设体系探讨——以A区卫生健康系统为例 康文雁; 1-4 “家庭五险一金”对居民商业保险购买存在挤出效应——基于江苏省徐州、淮安、泰州三市的实证研究 李炳毅; 5-8 人口老龄化背景下促进徐州市经济高质量发展的探究 李艳秋;…...

在VS Code中快速生成Vue模板的技巧

配置vue.json: { "Print to console": {"prefix": "vue","body": ["<template>"," <div class\"\">\n"," </div>","</template>\n","<scri…...

新火种AI|Sora发布半年之后,AI视频生成领域风云再起

作者&#xff1a;一号 编辑&#xff1a;美美 AI视频最近有些疯狂&#xff0c;Sora可能要着急了。 自OpenAI的Sora发布以来&#xff0c;AI视频生成技术便成为了科技界的热门话题。尽管Sora以其卓越的性能赢得了广泛关注&#xff0c;但其迟迟未能面向公众开放&#xff0c;让人…...

《UNIX环境高级编程》第三版(电子工业出版社出品)——两年磨一剑的匠心译作

历时两年&#xff0c;《UNIX环境高级编程》的翻译工作终于落下帷幕。这一路走来&#xff0c;真可谓是如鱼饮水&#xff0c;冷暖自知。还记得最初看到招募译者消息的那一刻&#xff0c;内心的激动难以言表。我毫不犹豫地报名&#xff0c;而后经历了试译、海选等激烈的角逐&#…...

【RK3588/算能/Nvidia智能盒子】AI“值守”,规范新能源汽车充电站停车、烟火及充电乱象

近年来&#xff0c;中国新能源汽车高速发展&#xff0c;产量连续8年位居全球第一。根据中国充电联盟数据&#xff0c;截至2023年6月&#xff0c;新能源汽车保有量1620万辆&#xff0c;全国充电基础设施累计数量为665.2万台&#xff0c;车桩比约2.5:1。 虽然新能源汽车与充电桩供…...

使用ReentrantLock和ThreadPoolExecutor模拟抢课

这里主要是在场景下帮助理解ReentrantLock和线程池的使用。 import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock;public class GrabCourseTask implements Runnable {private final String studentName;private static int availableS…...

VirtFuzz:一款基于VirtIO的Linux内核模糊测试工具

关于VirtFuzz VirtFuzz是一款功能强大的Linux内核模糊测试工具&#xff0c;该工具使用LibAFL构建&#xff0c;可以利用VirtIO向目标设备的内核子系统提供输入测试用例&#xff0c;广大研究人员可以使用该工具测试Linux内核的安全性。 工具要求 1、Rust&#xff1b; 2、修补的Q…...

盐城网站建设培训/郑州建网站的公司

PHP框架CI CodeIgniter 的log_message開啟日志記錄方法第一步&#xff1a;index.php文件&#xff0c;修改環境為開發環境define(‘ENVIRONMENT’, ‘development’);第二步&#xff1a;application/config/config.php文件修改$config[‘log_threshold’] 4; //0表示關閉&#…...

室内设计师的网站/网络销售网站

反向代理简介----服务器的代理代理服务器是使用非常普遍的一种将局域网主机联入互联网的一种方式&#xff0c;使用代理上网可以节约紧缺的IP地址资源&#xff0c;而且可以阻断外部主机对内部主机的访问&#xff0c;使 内部网主机免受外部网主机的***。但是&#xff0c;如果想让…...

专业网专业网站建设/深圳seo网络推广

1 前言 最近在ASP.NET中做了一个AJAX调用 : Client端先从ASP.NET Server后台取到一个页面模板&#xff0c;然后在页面初始化时再从Server中取一些相关数据以实现页面模板的动态显示。具体实现为&#xff1a; 1) Client向 ASP.NET后台发送HTTP GET 请示 2) 后…...

甘肃省疫情最新消息/关键词优化策略

点击上方“Java基基”&#xff0c;选择“设为星标”做积极的人&#xff0c;而不是积极废人&#xff01;每天 14:00 更新文章&#xff0c;每天掉亿点点头发...源码精品专栏 原创 | Java 2021 超神之路&#xff0c;很肝~中文详细注释的开源项目RPC 框架 Dubbo 源码解析网络应用框…...

建设一个境外网站/百度竞价推广代运营

对于开发人员来说&#xff0c;正则表达式是一个非常有用的功能。它提供了查找&#xff0c;匹配&#xff0c;替换句子&#xff0c;单词&#xff0c;或者其他格式的字符串。在这篇文章里&#xff0c;我已经编写了15个贼有用的正则表达式&#xff0c;WEB开发人员都应该将它收藏到自…...

仿煎蛋 wordpress/企业查询官网

全局命令命令功能set创建键值对keys遍历查看所有键exists判断一个键是否存在&#xff0c;1存在&#xff0c;0不存在dbsize当前数据库中有多少个键del删除一个或多个键expire设置过期时间ttl查看过期时间type判断键的类型Redis有5种数据结构&#xff0c;它们是键值对中的值&…...