本文是 IvorySQL 2025 生态大会暨 PostgreSQL 高峰论坛上的演讲内容,作者:NKYoung。

6 月底济南召开的 HOW2025 IvorySQL 生态大会上,我在内核论坛分享了“提升 vacuum 时间观测能力”的主题,提出了新增统计信息的方法,增强 vacuum 执行时长的观测能力。同时也给大家分享了一些 DBA 上手内核的感悟。

vacuum 在 PG 中是一个无法绕开的话题,甚至有些用户因为 vacuum 的特性而放弃 PG。老杨认为只要做好 vacuum 的观测以及优化,这都不是事。

监控 vacuum 的重要性

我们先来看vacuum 的主要工作:

  1. 移除死元组
  • 移除每一页中的死元组,并对每一页内的元组进行碎片整理。
  • 移除指向死元组的索引元组。
  1. 冻结旧的事务标识(txid)
  • 如有必要,冻结旧元组的事务标识(txid)。
  • 更新与冻结事务标识相关的系统视图(pg_database 与 pg_class)。
  • 如果可能,移除非必需的提交日志(clog)。
  1. 其他
  • 更新已处理表的空闲空间映射(FSM)和可见性映射(VM)。
  • 更新一些统计信息(pg_stat_all_tables 等)

vacuum 伪代码:

// Phase 1: initializing(1)    FOR each table
(2)      Acquire a ShareUpdateExclusiveLock lock for the target table/* The first block */// Phase 2: Scan Heap
(3)      Scan all pages to get all dead tuples, and freeze old tuples if necessary// Phase 3: Vacuuming Indexes
(4)      Remove the index tuples that point to the respective dead tuples if exists/* The second block */// Phase 4: Vacuuming Heap
(5)      FOR each page of the table
(6)         Remove the dead tuples, and Reallocate the live tuples in the page
(7)         Update FSM and VMEND FOR/* The third block */// Phase 5: Cleaning up indexes
(8)      Clean up indexes// Phase 6: Truncating heap
(9)      Truncate the last page ifpossible
(10)     Update both the statistics and system catalogs of the target tableRelease the ShareUpdateExclusiveLock lockEND FOR/* Post-processing */// Phase 7: Final Cleaning
(11)  Update statistics and system catalogs
(12)  Remove both unnecessary files and pages of the clogif possible

如果 vacuum 不合理,可能会导致以下问题:

  • 空间“膨胀”
  • 统计信息偏差,SQL 性能劣化
  • transaction ID wraparound or multixact ID wraparound

因此对 vacuum 的监控,是 PG 运维环节的重中之重。

监控执行时长的痛点

目前监控 vacuum 的手段:

  • 查看统计信息视图:pg_stat_all(user)_tables,获取上次 vacuum 完成时间,vacuum 次数等
  • 查看进度报告视图:pg_stat_progress_vacuum(analyze)
  • 手动执行 vacuum command 使用 verbose option

我们也可以根据现有的统计信息视图,创建自定义视图来观测 vacuum,具体可以参考灿神的书中对应的章节。

如何监控(auto)vacuum 的时长?

  1. vacuum:
  • 使用 verbose option,这样命令行就会返回 vacuum 过程信息
  • 配置 log_min_duration_statement 参数,超过阈值的 vacuum 会记录到日志中
  1. Autovacuum:
  • 配置 log_autovacuum_min_duration 参数,超过阈值的 autovacuum 会记录到日志中。

因此要查看表(auto)vacuum 的时长,几乎只能在 server log 中查看:

  • log 阈值参数取值,需要在是否记录大部分表 vacuum 和产生的日志条目占用的存储空间中权衡
  • 不是所有人员都有主机权限去访问日志
  • 即使将 server log 做成日志服务,检索和分析起来也不是很方便

大多时候表的 vacuum 历史执行时长对于我们精细化 vacuum 调优来说是很必要的。比如结合表的 SQL 性能表现,确定 vacuum 的执行频率,是否调参加速。

所以,为什么不做到统计信息里呢?老杨一直认为对于数据库而言,SQL 是提供给外界访问的最好的 API。这一点 Oracle 就做的很不错,提供了各种各样的统计信息视图,相对丰富全面的统计信息,直接查询系统视图就可以定制大部分监控指标,并不是吹嘘,实事求是。

当然 PG 的可观测性也是一直在发展的。如果搭建监控有痛点的话,认准 pigsty。

提升执行时长观测能力

方案:

PgStatStatTabEntry 结构体新增几个成员,直观来说就是对 pg_stat_all_tables 进行改造,新增 last(auto)vacuumduration,last(auto)analyze_duration 字段,记录上次 vacuum 和 analyze 的时长。

这里特别感谢我的好友神医(邱文辉),感谢他的帮助,同时我能不能成为贡献者就看他了。不报什么期望,主打自嗨图片。

效果:

file

比如可以精细化统计关键表的 Autovacuum 时长,定制监控项来预警。

file

伪代码: vacuum/analyze 开始时记录一个时间戳,结束时记录一个时间戳。求 duration,并赋值给新增的统计信息。

heap_vacuum_rel:begintime=GetCurrentTimestamp();...do vacuum:...endtime=GetCurrentTimestamp();duration = endtime - begintime;last_vacuum_duration = duration;
do_analyze_rel:begintime=GetCurrentTimestamp();...do analyze:...endtime=GetCurrentTimestamp();duration = endtime - begintime;last_analyze_duration = duration;

代码:

From caeddbf1ead4078d3dd6af36c0bab27f747409a9 Mon Sep 17 00:00:002001
From: Nickyoung0 <yxbshare@163.com>
Date: Fri, 13 Jun 202516:29:55 +0800
Subject: [PATCH] add the duration for last_vacuum/analyze orlast_autovacuum/autoanalyze---.../src/backend/access/heap/vacuumlazy.c      | 38 ++++++++++++++++++-.../src/backend/catalog/system_views.sql      |  6 ++-.../src/backend/commands/analyze.c            | 38 ++++++++++++++++++-.../src/backend/utils/adt/pgstatfuncs.c       | 30 +++++++++++++++.../src/include/catalog/pg_proc.dat           | 16 ++++++++postgresql-17.4/src/include/pgstat.h          |  4 ++
6 files changed, 127 insertions(+), 5 deletions(-)diff --git a/postgresql-17.4/src/backend/access/heap/vacuumlazy.c b/postgresql-17.4/src/backend/access/heap/vacuumlazy.c
index f2d2598..9c78200 100644
--- a/postgresql-17.4/src/backend/access/heap/vacuumlazy.c
+++ b/postgresql-17.4/src/backend/access/heap/vacuumlazy.c
@@ -59,7 +59,7 @@
#include "utils/memutils.h"
#include "utils/pg_rusage.h"
#include "utils/timestamp.h"
-
+#include "utils/pgstat_internal.h"/** Space/time tradeoff parameters: do these need to be user-tunable?
@@ -305,7 +305,14 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,new_rel_pages,new_rel_allvisible;PGRUsage ru0;
- TimestampTz starttime = 0;
+ TimestampTz starttime = 0,
+    begintime = 0;
+        long            secs;
+        int             usecs;
+        int             msecs;
+        PgStat_EntryRef *entry_ref;
+        PgStatShared_Relation *shtabentry;
+        PgStat_StatTabEntry *tabentry;PgStat_Counter startreadtime = 0,startwritetime = 0;WalUsage startwalusage = pgWalUsage;
@@ -327,6 +334,8 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,}}+        begintime = GetCurrentTimestamp();
+pgstat_progress_start_command(PROGRESS_COMMAND_VACUUM,RelationGetRelid(rel));@@ -591,6 +600,31 @@ heap_vacuum_rel(Relation rel, VacuumParams *params,vacrel->missed_dead_tuples);pgstat_progress_end_command();+ TimestampDifference(begintime,
+                                        GetCurrentTimestamp(),
+                                        &secs, &usecs);
+        msecs = usecs / 1000;
+        /* Add commentMore actions
+         * Store the data in the table's hash table entry.
+         * block acquiring lock for the same reason as pgstat_report_autovac()
+         */
+        entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION,
+                                                MyDatabaseId, RelationGetRelid(rel), false);
+
+        shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+        tabentry = &shtabentry->stats;
+
+        if (AmAutoVacuumWorkerProcess())
+
+        {
+                snprintf(tabentry->last_autovacuum_duration, 32, "%ld.%03d",
+                                secs * 1000 + msecs, usecs % 1000);
+        }
+        else
+                snprintf(tabentry->last_vacuum_duration, 32, "%ld.%03d",
+                                secs * 1000 + msecs, usecs % 1000);
+        pgstat_unlock_entry(entry_ref);
+
if (instrument){TimestampTz endtime = GetCurrentTimestamp();
diff --git a/postgresql-17.4/src/backend/catalog/system_views.sql b/postgresql-17.4/src/backend/catalog/system_views.sql
index efb29ad..411609f 100644
--- a/postgresql-17.4/src/backend/catalog/system_views.sql
+++ b/postgresql-17.4/src/backend/catalog/system_views.sql
@@ -695,7 +695,11 @@ CREATE VIEW pg_stat_all_tables ASpg_stat_get_vacuum_count(C.oid) AS vacuum_count,pg_stat_get_autovacuum_count(C.oid) AS autovacuum_count,pg_stat_get_analyze_count(C.oid) AS analyze_count,
-            pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count
+            pg_stat_get_autoanalyze_count(C.oid) AS autoanalyze_count,
+            pg_stat_get_last_vacuum_duration(c.oid) AS last_vacuum_duration,
+            pg_stat_get_last_autovacuum_duration(c.oid) AS last_autovacuum_duration,
+            pg_stat_get_last_analyze_duration(c.oid) AS last_analyze_duration,
+            pg_stat_get_last_autoanalyze_duration(c.oid) AS last_autoanalyze_durationFROM pg_class C LEFT JOINpg_index I ON C.oid = I.indrelidLEFT JOIN pg_namespace N ON(N.oid = C.relnamespace)
diff --git a/postgresql-17.4/src/backend/commands/analyze.c b/postgresql-17.4/src/backend/commands/analyze.c
index c590a2a..a98da59 100644
--- a/postgresql-17.4/src/backend/commands/analyze.c
+++ b/postgresql-17.4/src/backend/commands/analyze.c
@@ -57,7 +57,7 @@
#include "utils/spccache.h"
#include "utils/syscache.h"
#include "utils/timestamp.h"
-
+#include "utils/pgstat_internal.h"/* Per-index data for ANALYZE */
typedef struct AnlIndexData
@@ -298,7 +298,14 @@ do_analyze_rel(Relation onerel, VacuumParams *params,totaldeadrows;HeapTuple  *rows;PGRUsage ru0;
- TimestampTz starttime = 0;
+ TimestampTz starttime = 0,
+    begintime = 0;
+        long            secs;
+        int             usecs;
+        int             msecs;
+        PgStat_EntryRef *entry_ref;
+        PgStatShared_Relation *shtabentry;
+        PgStat_StatTabEntry *tabentry;MemoryContext caller_context;Oid   save_userid;
int   save_sec_context;
@@ -353,6 +360,8 @@ do_analyze_rel(Relation onerel, VacuumParams *params,starttime = GetCurrentTimestamp();}+        begintime = GetCurrentTimestamp();
+
/** Determine which columns to analyze*
@@ -722,6 +731,31 @@ do_analyze_rel(Relation onerel, VacuumParams *params,/* Done with indexes */vac_close_indexes(nindexes, Irel, NoLock);
+
+ TimestampDifference(begintime,
+                                        GetCurrentTimestamp(),
+                                        &secs, &usecs);
+        msecs = usecs / 1000;
+        /* Add commentMore actions
+         * Store the data in the table's hash table entry.
+         * block acquiring lock for the same reason as pgstat_report_autovac()
+         */
+        entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_RELATION,
+                                                MyDatabaseId, RelationGetRelid(onerel), false);
+
+        shtabentry = (PgStatShared_Relation *) entry_ref->shared_stats;
+        tabentry = &shtabentry->stats;
+
+        if (AmAutoVacuumWorkerProcess())
+
+        {
+                snprintf(tabentry->last_autoanalyze_duration, 32, "%ld.%03d",
+                                secs * 1000 + msecs, usecs % 1000);
+        }
+        else
+                snprintf(tabentry->last_analyze_duration, 32, "%ld.%03d",
+                                secs * 1000 + msecs, usecs % 1000);
+        pgstat_unlock_entry(entry_ref);/* Log the action if appropriate */
if (AmAutoVacuumWorkerProcess() && params->log_min_duration >= 0)
diff --git a/postgresql-17.4/src/backend/utils/adt/pgstatfuncs.c b/postgresql-17.4/src/backend/utils/adt/pgstatfuncs.c
index 2575dba..7aaab84 100644
--- a/postgresql-17.4/src/backend/utils/adt/pgstatfuncs.c
+++ b/postgresql-17.4/src/backend/utils/adt/pgstatfuncs.c
@@ -140,6 +140,36 @@ PG_STAT_GET_RELENTRY_TIMESTAMPTZ(last_vacuum_time)
/* pg_stat_get_lastscan */PG_STAT_GET_RELENTRY_TIMESTAMPTZ(lastscan)+#define PG_STAT_GET_RELENTRY_STRING(stat)                                  \
+Datum                                                                                                                   \
+CppConcat(pg_stat_get_,stat)(PG_FUNCTION_ARGS)                                  \
+{                                                                                                                               \
+        Oid                     relid = PG_GETARG_OID(0);                                               \
+        char    *result;                                                                                     \
+        PgStat_StatTabEntry *tabentry;                                                          \
+                                                                                                                                \
+        if ((tabentry = pgstat_fetch_stat_tabentry(relid)) == NULL)     \
+                PG_RETURN_NULL();                                                                                             \
+        else                                                                                                            \
+                result = tabentry->stat;                                                                \
+                                                                                                                                \
+        if (result == NULL || result[0] == '\0')                                                                                        \
+                PG_RETURN_NULL();                                                                               \
+        else                                                                                                            \
+                PG_RETURN_TEXT_P(cstring_to_text(result));                                                 \
+}
+/* pg_stat_get_last_vacuum_duration */
+PG_STAT_GET_RELENTRY_STRING(last_vacuum_duration)
+
+/* pg_stat_get_last_autovacuum_duration */
+PG_STAT_GET_RELENTRY_STRING(last_autovacuum_duration)
+
+/* pg_stat_get_last_analyze_duration */
+PG_STAT_GET_RELENTRY_STRING(last_analyze_duration)
+
+/* pg_stat_get_last_autoanalyze_duration */
+PG_STAT_GET_RELENTRY_STRING(last_autoanalyze_duration)
+Datumpg_stat_get_function_calls(PG_FUNCTION_ARGS){
diff --git a/postgresql-17.4/src/include/catalog/pg_proc.dat b/postgresql-17.4/src/include/catalog/pg_proc.dat
index 6a5476d..2809c39 100644
--- a/postgresql-17.4/src/include/catalog/pg_proc.dat
+++ b/postgresql-17.4/src/include/catalog/pg_proc.dat
@@ -5445,6 +5445,22 @@proname => 'pg_stat_get_autoanalyze_count', provolatile => 's',proparallel => 'r', prorettype => 'int8', proargtypes => 'oid',prosrc => 'pg_stat_get_autoanalyze_count' },
+{ oid => '6347', descr => 'statistics: last manual vacuum duration for a table',
+  proname => 'pg_stat_get_last_vacuum_duration', provolatile => 's',
+  proparallel => 'r', prorettype => 'text', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_last_vacuum_duration' },
+{ oid => '6348', descr => 'statistics: last auto vacuum duration for a table',
+  proname => 'pg_stat_get_last_autovacuum_duration', provolatile => 's',
+  proparallel => 'r', prorettype => 'text', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_last_autovacuum_duration' },
+{ oid => '6349', descr => 'statistics: last manual analyze duration for a table',
+  proname => 'pg_stat_get_last_analyze_duration', provolatile => 's',
+  proparallel => 'r', prorettype => 'text', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_last_analyze_duration' },
+{ oid => '6350', descr => 'statistics: last auto analyze duration for a table',
+  proname => 'pg_stat_get_last_autoanalyze_duration', provolatile => 's',
+  proparallel => 'r', prorettype => 'text', proargtypes => 'oid',
+  prosrc => 'pg_stat_get_last_autoanalyze_duration' },{ oid => '1936', descr => 'statistics: currently active backend IDs',proname => 'pg_stat_get_backend_idset', prorows => '100', proretset => 't',provolatile => 's', proparallel => 'r', prorettype => 'int4',
diff --git a/postgresql-17.4/src/include/pgstat.h b/postgresql-17.4/src/include/pgstat.h
index 2136239..f3f3423 100644
--- a/postgresql-17.4/src/include/pgstat.h
+++ b/postgresql-17.4/src/include/pgstat.h
@@ -426,6 +426,10 @@ typedef struct PgStat_StatTabEntryPgStat_Counter analyze_count;TimestampTz last_autoanalyze_time; /* autovacuum initiated */PgStat_Counter autoanalyze_count;
+        char            last_vacuum_duration[32];
+        char            last_autovacuum_duration[32];
+        char            last_analyze_duration[32];
+        char            last_autoanalyze_duration[32];} PgStat_StatTabEntry;typedef struct PgStat_WalStats
--
2.39.5 (Apple Git-154)

DBA 上手内核的感悟

虽然老杨一直提倡 DBA 向内核精进,但是否搞内核,还是因人而异。

先说说我自己为什么会坚持玩内核:

说直白点,其实就是自嗨,给自己提供情绪价值,自我取悦,自我肯定。

工作是存在隐形鄙视链的,运维被视为“打杂的”再正常不过了。

所有的脏活累活运维来扛,却没什么成果可以呈现。

再加目前重应用,轻数据库的玩法,甚至有些项目中数据库被当做存储来使用,DBA 的价值就更得不到体现了。另外就是本身 DBA 的某些能力也不如前辈了,比如我自己,老实说我的 SQL 能力就不怎么样。

在晋升汇报时,时不时被拷打,没有量化的成果去展示。当你表达客户满意度提升,收入增长,成本缩减时可能被质疑,这是研发和产品所负责的事情。

所以,运维拿着最低的工资,没有价值体现,还被 PUA,你不自嗨,这工作干着得多心累?

多年前在某家公司,全组就我一个人能通过看代码分析报错,这期间我看过 PG、MySQL、Redis、MongoDB 等。虽然都是简单问题,但这种装 X 的感觉是真的好,人需要装 X。

不过,我现在的团队挺 nice 的,老板鼓励大家深入技术,多参加技术活动。

当然不是说 DBA 非得去搞内核,看个人的兴趣点在哪吧,这个节点更适合卷 AI。 工作的同时能够发展自己的兴趣,也是一件美事。

假如你也是非专业人士,想搞内核,可以参考下老杨的几点感悟:

  • 前期积累:对 PG 代码的熟悉,对每个模块逻辑的理解。

  • 从何入手:“临摹”已有的逻辑,比如实现一个新的 GUC 产生不同的控制逻辑

  • 借助 AI :可以使用 AI 去分析代码逻辑,并实现功能,对比 AI 和自己思路的差异

  • 勇于尝试:只要有想法就可以尝试实现,不要过于在乎“正确性”、“专业性”

  • 过程>结果:能不能实现不重要,最有收益的是对于原理的深入理解

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

神奇的数据跳变

目的 上周遇上了一个非常奇怪的问题,就是软件的数据在跳变,本来数据应该是158吧,数据一会变成10,一会又变成158,数据在不断地跳变,那是怎么回事?? 这个问题非常非常的神奇,让人感觉太不可思议了。 这是这段时间,我遇上的最神奇的事了,没有之一,最神奇的事,下面…

【跨国数仓迁移最佳实践3】资源消耗减少50%!解析跨国数仓迁移至MaxCompute背后的性能优化技术

本系列文章将围绕东南亚头部科技集团的真实迁移历程展开&#xff0c;逐步拆解 BigQuery 迁移至 MaxCompute 过程中的关键挑战与技术创新。本篇为第3篇&#xff0c;解析跨国数仓迁移背后的性能优化技术。注&#xff1a;客户背景为东南亚头部科技集团&#xff0c;文中用 GoTerra …

【MySQL集群架构与实践3】使用Dcoker实现读写分离

目录 一. 在Docker中安装ShardingSphere 二 实践&#xff1a;读写分离 2.1 应用场景 2.2 架构图 2.3 服务器规划 2.4 启动数据库服务器 2.5. 配置读写分离 2.6 日志配置 2.7 重启ShardingSphere 2.8 测试 2.9. 负载均衡 2.9.1. 随机负载均衡算法示例 2.9.2. 轮询负…

maven的阿里云镜像地址

在 Maven 中配置阿里云镜像可以加速依赖包的下载&#xff0c;尤其是国内环境下效果明显。以下是阿里云 Maven 镜像的配置方式&#xff1a; 配置步骤&#xff1a;找到 Maven 的配置文件 settings.xml 全局配置&#xff1a;位于 Maven 安装目录的 conf/settings.xml用户级配置&am…

大语言模型信息抽取系统解析

这段代码实现了一个基于大语言模型的信息抽取系统&#xff0c;能够从金融和新闻类文本中提取结构化信息。下面我将详细解析整个代码的结构和功能。1. 代码整体结构代码主要分为以下几个部分&#xff1a;模式定义&#xff1a;定义不同领域(金融、新闻)需要抽取的实体类型示例数据…

Next实习项目总结串联讲解(一)

下面是一些 Next.js 前端面试中常见且具深度的问题&#xff0c;按照逻辑模块整理&#xff0c;同时提供示范回答建议&#xff0c;便于你条理清晰地展示理解与实践经验。 ✅ 面试讲述结构建议 先讲 Next.js 是什么&#xff0c;它为什么比 React 更高级。(支持 SSR/SSG/ISR,提升S…

React开发依赖分析

1. React小案例&#xff1a; 在界面显示一个文本&#xff1a;Hello World点击按钮后&#xff0c;文本改为为&#xff1a;Hello React 2. React开发依赖 2.1. 开发React必须依赖三个库&#xff1a; 2.1.1. react: 包含react所必须的核心代码2.1.2. react-dom: react渲染在不同平…

工具(一)Cursor

目录 一、介绍 二、如何打开文件 1、从idea跳转文件 2、单独打开项目 三、常见使用 1、Chat 窗口 Ask 对话模式 1.1、使用技巧 1.2 发送和使用 codebase 发送区别 1.3、问题快速修复 2、Chat 窗口 Agent 对话模式 2.1、agent模式功能 2.2、Chat 窗口回滚&撤销 2.3…

Prompt编写规范指引

1、&#x1f4d6; 引言 随着人工智能生成内容&#xff08;AIGC&#xff09;技术的快速发展&#xff0c;越来越多的开发者开始利用AIGC工具来辅助代码编写。然而&#xff0c;如何编写有效的提示词&#xff08;Prompt&#xff09;以引导AIGC生成高质量的代码&#xff0c;成为了许…

自我学习----绘制Mark点

在PCB的Layout过程中我们需在光板上放置Mark点以方便生产时的光学定位&#xff08;三点定位&#xff09;&#xff1b;我个人Mark点绘制步骤如下&#xff1a; layer层&#xff1a;1.放置直径1mm的焊盘&#xff08;无网络连接&#xff09; 2.放置一个圆直径2mm&#xff0c;圆心与…

2025年财税行业拓客破局:小蓝本财税版AI拓客系统助力高效拓客

2025年&#xff0c;在"金税四期"全面实施的背景下&#xff0c;中国财税服务市场迎来爆发式增长&#xff0c;根据最新的市场研究报告&#xff0c;2025年中国财税服务行业产值将达2725.7亿元。然而&#xff0c;行业高速发展的背后&#xff0c;80%的财税公司却陷入获客成…

双向链表,对其实现头插入,尾插入以及遍历倒序输出

1.创建一个节点&#xff0c;并将链表的首节点返回创建一个独立节点&#xff0c;没有和原链表产生任何关系#include "head.h"typedef struct Node { int num; struct Node*pNext; struct Node*pPer; }NODE;后续代码&#xff1a;NODE*createNode(int value) {NODE*new …

2025年自动化工程与计算机网络国际会议(ICAECN 2025)

2025年自动化工程与计算机网络国际会议&#xff08;ICAECN 2025&#xff09; 2025 International Conference on Automation Engineering and Computer Networks一、大会信息会议简称&#xff1a;ICAECN 2025 大会地点&#xff1a;中国柳州 审稿通知&#xff1a;投稿后2-3日内通…

12.Origin2021如何绘制误差带图?

12.Origin2021如何绘制误差带图?选中Y3列→点击统计→选择描述统计→选择行统计→选择打开对话框输入范围选择B列到D列点击输出量→勾选均值和标准差Control选择下面三列点击绘图→选择基础2D图→选择误差带图双击图像→选择符号和颜色点击第二个Sheet1→点击误差棒→连接选择…

如何使用API接口获取淘宝店铺订单信息

要获取淘宝店铺的订单信息&#xff0c;您需要通过淘宝开放平台(Taobao Open Platform, TOP)提供的API接口来实现。以下是详细步骤&#xff1a;1. 注册淘宝开放平台账号访问淘宝开放平台注册开发者账号并完成实名认证创建应用获取App Key和App Secret2. 申请API权限在"我的…

【Kiro Code 从入门到精通】重要的功能

一、Kiro 是什么&#xff1f; Kiro 是一款智能型集成开发环境&#xff08;IDE&#xff09;&#xff0c;借助规格说明&#xff08;specs&#xff09;、向导&#xff08;steer&#xff09;、钩子&#xff08;hooks&#xff09;帮助你高效完成工作。 二、Specs 规格说明 规范&…

直播间里的酒旅新故事:内容正在重构消费链路

文/李乐编辑/子夜今年暑期&#xff0c;旅游的热浪席卷全国。机场、火车站人潮涌动&#xff0c;电子屏上滚动的航班信息与检票口前的长队交织成繁忙的出行图景&#xff0c;酒店预订量也在这股热潮中节节攀升。连线 Insight关注到&#xff0c;今年的暑期游有了一些新变化&#xf…

50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | VerifyAccountUi(验证码组件)

&#x1f4c5; 我们继续 50 个小项目挑战&#xff01;—— VerifyAccountUi组件 仓库地址&#xff1a;https://github.com/SunACong/50-vue-projects 项目预览地址&#xff1a;https://50-vue-projects.vercel.app/ 使用 Vue 3 的 <script setup> 语法结合 Tailwind CS…

AbstractAuthenticationToken 认证流程中​​认证令牌的核心抽象类详解

AbstractAuthenticationToken 认证流程中​​认证令牌的核心抽象类详解在 Spring Security 中&#xff0c;AbstractAuthenticationToken 是 Authentication 接口的​​抽象实现类​​&#xff0c;其核心作用是为具体的认证令牌&#xff08;如用户名密码令牌、JWT 令牌等&#x…

小程序视频播放,与父视图一致等样式设置

初始设置的代码&#xff1a;WXML的代码<view class"card-wrapper"> <!-- 视频播放容器&#xff08;默认隐藏&#xff09; --> <view class"video-container" wx:if"{{isPlaying}}"> <video id"cardVideo" class&…