PGSQL運維優化:提升vacuum執行時間觀測能力

本文是 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&…