PostgreSQL 提供支援資料庫伺服器動態追蹤的功能。這允許在程式碼中的特定點呼叫外部實用程式,從而追蹤執行。
許多探針或追蹤點已經插入到原始程式碼中。這些探針旨在供資料庫開發人員和管理員使用。預設情況下,探針不會編譯到 PostgreSQL 中;使用者需要明確地告訴 configure 腳本使探針可用。
目前,支援 DTrace 實用程式,在撰寫本文時,該實用程式可在 Solaris、macOS、FreeBSD、NetBSD 和 Oracle Linux 上使用。 Linux 的 SystemTap 專案提供了一個 DTrace 等效程式,也可以使用。 理論上,可以透過更改 src/include/utils/probes.h
中巨集的定義來支援其他動態追蹤實用程式。
預設情況下,探針不可用,因此您需要明確地告訴 configure 腳本使探針在 PostgreSQL 中可用。 若要包含 DTrace 支援,請指定 --enable-dtrace
來設定。 有關更多資訊,請參閱第 17.3.3.6 節。
原始程式碼中提供了許多標準探針,如表 27.49 所示;表 27.50 顯示了探針中使用的類型。 當然,可以添加更多探針來增強 PostgreSQL 的可觀察性。
表 27.49. 內建 DTrace 探針
名稱 | 參數 | 描述 |
---|---|---|
transaction-start |
(LocalTransactionId) |
在新的交易開始時觸發的探針。 arg0 是交易 ID。 |
transaction-commit |
(LocalTransactionId) |
當交易成功完成時觸發的探針。 arg0 是交易 ID。 |
transaction-abort |
(LocalTransactionId) |
當交易未成功完成時觸發的探針。 arg0 是交易 ID。 |
query-start |
(const char *) |
當開始處理查詢時觸發的探針。 arg0 是查詢字串。 |
query-done |
(const char *) |
當查詢的處理完成時觸發的探針。 arg0 是查詢字串。 |
query-parse-start |
(const char *) |
當開始解析查詢時觸發的探針。 arg0 是查詢字串。 |
query-parse-done |
(const char *) |
當查詢的解析完成時觸發的探針。 arg0 是查詢字串。 |
query-rewrite-start |
(const char *) |
當開始重寫查詢時觸發的探針。 arg0 是查詢字串。 |
query-rewrite-done |
(const char *) |
當查詢的重寫完成時觸發的探針。 arg0 是查詢字串。 |
query-plan-start |
() |
當開始規劃查詢時觸發的探針。 |
query-plan-done |
() |
當查詢的規劃完成時觸發的探針。 |
query-execute-start |
() |
當開始執行查詢時觸發的探針。 |
query-execute-done |
() |
當查詢的執行完成時觸發的探針。 |
statement-status |
(const char *) |
每當伺服器程序更新其 pg_stat_activity .status 時觸發的探針。 arg0 是新的狀態字串。 |
checkpoint-start |
(int) |
當啟動檢查點時觸發的探針。 arg0 保留用於區分不同檢查點類型的位元旗標,例如關機、立即或強制。 |
checkpoint-done |
(int, int, int, int, int) |
當檢查點完成時觸發的探針。(接下來列出的探針在檢查點處理期間按順序觸發。)arg0 是寫入的緩衝區數。 arg1 是緩衝區總數。 arg2、arg3 和 arg4 分別包含新增、移除和回收的 WAL 檔案數。 |
clog-checkpoint-start |
(bool) |
當啟動檢查點的 CLOG 部分時觸發的探針。 對於正常檢查點,arg0 為 true;對於關機檢查點,則為 false。 |
clog-checkpoint-done |
(bool) |
當檢查點的 CLOG 部分完成時觸發的探針。 arg0 的含義與 clog-checkpoint-start 相同。 |
subtrans-checkpoint-start |
(bool) |
當啟動檢查點的 SUBTRANS 部分時觸發的探針。 對於正常檢查點,arg0 為 true;對於關機檢查點,則為 false。 |
subtrans-checkpoint-done |
(bool) |
當檢查點的 SUBTRANS 部分完成時觸發的探針。 arg0 的含義與 subtrans-checkpoint-start 相同。 |
multixact-checkpoint-start |
(bool) |
在檢查點的 MultiXact 部分開始時觸發的探針。arg0 對於正常檢查點為 true,對於關閉檢查點為 false。 |
multixact-checkpoint-done |
(bool) |
在檢查點的 MultiXact 部分完成時觸發的探針。arg0 的含義與 multixact-checkpoint-start 相同。 |
buffer-checkpoint-start |
(int) |
在檢查點的緩衝區寫入部分開始時觸發的探針。arg0 包含用於區分不同檢查點類型(例如關閉、立即或強制)的位元旗標。 |
buffer-sync-start |
(int, int) |
在我們開始在檢查點期間寫入髒緩衝區時(在識別哪些緩衝區必須寫入之後)觸發的探針。arg0 是緩衝區總數。arg1 是目前為髒且需要寫入的數量。 |
buffer-sync-written |
(int) |
在檢查點期間寫入每個緩衝區後觸發的探針。arg0 是緩衝區的 ID 編號。 |
buffer-sync-done |
(int, int, int) |
在所有髒緩衝區都已寫入時觸發的探針。arg0 是緩衝區總數。arg1 是實際由檢查點進程寫入的緩衝區數量。arg2 是預期要寫入的數量 (buffer-sync-start 的 arg1);任何差異都反映了在檢查點期間刷新緩衝區的其他進程。 |
buffer-checkpoint-sync-start |
() |
在髒緩衝區已寫入核心後,以及在開始發出 fsync 請求之前觸發的探針。 |
buffer-checkpoint-done |
() |
在將緩衝區同步到磁碟完成時觸發的探針。 |
twophase-checkpoint-start |
() |
在檢查點的兩階段部分開始時觸發的探針。 |
twophase-checkpoint-done |
() |
在檢查點的兩階段部分完成時觸發的探針。 |
buffer-extend-start |
(ForkNumber, BlockNumber, Oid, Oid, Oid, int, unsigned int) |
在關係擴展開始時觸發的探針。arg0 包含要擴展的 fork。arg1、arg2 和 arg3 包含識別關係的表空間、資料庫和關係 OID。arg4 是為本地緩衝區建立臨時關係的後端 ID,或者對於共享緩衝區,則為 INVALID_PROC_NUMBER (-1)。arg5 是呼叫者想要擴展的區塊數量。 |
buffer-extend-done |
(ForkNumber, BlockNumber, Oid, Oid, Oid, int, unsigned int, BlockNumber) |
在關係擴展完成時觸發的探針。arg0 包含要擴展的 fork。arg1、arg2 和 arg3 包含識別關係的表空間、資料庫和關係 OID。arg4 是為本地緩衝區建立臨時關係的後端 ID,或者對於共享緩衝區,則為 INVALID_PROC_NUMBER (-1)。arg5 是關係擴展的區塊數量,由於資源限制,這可以小於 buffer-extend-start 中的數量。arg6 包含第一個新區塊的 BlockNumber。 |
buffer-read-start |
(ForkNumber, BlockNumber, Oid, Oid, Oid, int) |
在緩衝區讀取開始時觸發的探針。arg0 和 arg1 包含頁面的 fork 和區塊編號。arg2、arg3 和 arg4 包含識別關係的表空間、資料庫和關係 OID。arg5 是為本地緩衝區建立臨時關係的後端 ID,或者對於共享緩衝區,則為 INVALID_PROC_NUMBER (-1)。 |
buffer-read-done |
(ForkNumber, BlockNumber, Oid, Oid, Oid, int, bool) |
在緩衝區讀取完成時觸發的探針。arg0 和 arg1 包含頁面的 fork 和區塊編號。arg2、arg3 和 arg4 包含識別關係的表空間、資料庫和關係 OID。arg5 是為本地緩衝區建立臨時關係的後端 ID,或者對於共享緩衝區,則為 INVALID_PROC_NUMBER (-1)。arg6 如果在池中找到緩衝區,則為 true,如果沒有找到,則為 false。 |
buffer-flush-start |
(ForkNumber, BlockNumber, Oid, Oid, Oid) |
在發出共享緩衝區的任何寫入請求之前觸發的探針。arg0 和 arg1 包含頁面的 fork 和區塊編號。arg2、arg3 和 arg4 包含識別關係的表空間、資料庫和關係 OID。 |
buffer-flush-done |
(ForkNumber, BlockNumber, Oid, Oid, Oid) |
在寫入請求完成時觸發的探針。(請注意,這僅反映將資料傳遞到核心的時間;通常實際上尚未寫入到磁碟。)參數與 buffer-flush-start 相同。 |
wal-buffer-write-dirty-start |
() |
在伺服器進程由於沒有更多 WAL 緩衝區空間可用而開始寫入髒 WAL 緩衝區時觸發的探針。(如果經常發生這種情況,則表示 wal_buffers 太小。) |
wal-buffer-write-dirty-done |
() |
在髒 WAL 緩衝區寫入完成時觸發的探針。 |
wal-insert |
(unsigned char, unsigned char) |
在插入 WAL 記錄時觸發的探針。arg0 是記錄的資源管理器 (rmid)。arg1 包含 info 旗標。 |
wal-switch |
() |
在請求 WAL 段切換時觸發的探針。 |
smgr-md-read-start |
(ForkNumber, BlockNumber, Oid, Oid, Oid, int) |
在開始從關係讀取區塊時觸發的探針。arg0 和 arg1 包含頁面的 fork 和區塊編號。arg2、arg3 和 arg4 包含識別關係的表空間、資料庫和關係 OID。arg5 是為本地緩衝區建立臨時關係的後端 ID,或者對於共享緩衝區,則為 INVALID_PROC_NUMBER (-1)。 |
smgr-md-read-done |
(ForkNumber, BlockNumber, Oid, Oid, Oid, int, int, int) |
在區塊讀取完成時觸發的探針。arg0 和 arg1 包含頁面的 fork 和區塊編號。arg2、arg3 和 arg4 包含識別關係的表空間、資料庫和關係 OID。arg5 是為本地緩衝區建立臨時關係的後端 ID,或者對於共享緩衝區,則為 INVALID_PROC_NUMBER (-1)。arg6 是實際讀取的位元組數,而 arg7 是請求的數量(如果這些不同,則表示短讀取)。 |
smgr-md-write-start |
(ForkNumber, BlockNumber, Oid, Oid, Oid, int) |
在開始將區塊寫入關係時觸發的探針。arg0 和 arg1 包含頁面的 fork 和區塊編號。arg2、arg3 和 arg4 包含識別關係的表空間、資料庫和關係 OID。arg5 是為本地緩衝區建立臨時關係的後端 ID,或者對於共享緩衝區,則為 INVALID_PROC_NUMBER (-1)。 |
smgr-md-write-done |
(ForkNumber, BlockNumber, Oid, Oid, Oid, int, int, int) |
在區塊寫入完成時觸發的探針。arg0 和 arg1 包含頁面的 fork 和區塊編號。arg2、arg3 和 arg4 包含識別關係的表空間、資料庫和關係 OID。arg5 是為本地緩衝區建立臨時關係的後端 ID,或者對於共享緩衝區,則為 INVALID_PROC_NUMBER (-1)。arg6 是實際寫入的位元組數,而 arg7 是請求的數量(如果這些不同,則表示短寫入)。 |
sort-start |
(int, bool, int, int, bool, int) |
在排序操作開始時觸發的探針。arg0 指示堆積、索引或資料排序。arg1 對於唯一值強制為 true。arg2 是金鑰欄的數量。arg3 是允許的工作記憶體的千位元組數。arg4 如果需要對排序結果進行隨機存取,則為 true。arg5 在 0 時指示序列,在 1 時指示平行工作器,在 2 時指示平行領導者。 |
sort-done |
(bool, long) |
在排序完成時觸發的探針。arg0 對於外部排序為 true,對於內部排序為 false。arg1 是用於外部排序的磁碟區塊數量,或用於內部排序的記憶體千位元組數。 |
lwlock-acquire |
(char *, LWLockMode) |
在取得 LWLock 時觸發的探針。arg0 是 LWLock 的區塊。arg1 是請求的鎖定模式,可以是獨佔或共享。 |
lwlock-release |
(char *) |
在釋放 LWLock 時觸發的探針(但請注意,尚未喚醒任何已釋放的等待者)。arg0 是 LWLock 的區塊。 |
lwlock-wait-start |
(char *, LWLockMode) |
當 LWLock 無法立即取得且伺服器程序已開始等待鎖定變成可用時,觸發的探針。arg0 是 LWLock 的 tranche。arg1 是請求的鎖定模式,可以是獨佔或共享。 |
lwlock-wait-done |
(char *, LWLockMode) |
當伺服器程序已從等待 LWLock 中釋放(實際上尚未擁有鎖定)時,觸發的探針。arg0 是 LWLock 的 tranche。arg1 是請求的鎖定模式,可以是獨佔或共享。 |
lwlock-condacquire |
(char *, LWLockMode) |
當呼叫者指定不等待時,成功取得 LWLock 時觸發的探針。arg0 是 LWLock 的 tranche。arg1 是請求的鎖定模式,可以是獨佔或共享。 |
lwlock-condacquire-fail |
(char *, LWLockMode) |
當呼叫者指定不等待時,未能成功取得 LWLock 時觸發的探針。arg0 是 LWLock 的 tranche。arg1 是請求的鎖定模式,可以是獨佔或共享。 |
lock-wait-start |
(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, LOCKMODE) |
當請求一個重量級鎖定(lmgr 鎖定)由於鎖定不可用而開始等待時,觸發的探針。arg0 到 arg3 是標籤欄位,用於識別被鎖定的物件。arg4 指示被鎖定的物件類型。arg5 指示被請求的鎖定類型。 |
lock-wait-done |
(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, LOCKMODE) |
當請求一個重量級鎖定(lmgr 鎖定)完成等待(即,已取得鎖定)時,觸發的探針。參數與 lock-wait-start 相同。 |
deadlock-found |
() |
當死鎖檢測器發現死鎖時,觸發的探針。 |
表 27.50. 探針參數中使用的定義類型
類型 | 定義 |
---|---|
LocalTransactionId |
unsigned int |
LWLockMode |
int |
LOCKMODE |
int |
BlockNumber |
unsigned int |
Oid |
unsigned int |
ForkNumber |
int |
bool |
unsigned char |
下面的例子展示了一個 DTrace 腳本,用於分析系統中的事務計數,作為在性能測試前後快照 pg_stat_database
的替代方案
#!/usr/sbin/dtrace -qs postgresql$1:::transaction-start { @start["Start"] = count(); self->ts = timestamp; } postgresql$1:::transaction-abort { @abort["Abort"] = count(); } postgresql$1:::transaction-commit /self->ts/ { @commit["Commit"] = count(); @time["Total time (ns)"] = sum(timestamp - self->ts); self->ts=0; }
執行時,範例 D 腳本會給出如下輸出
# ./txn_count.d `pgrep -n postgres` or ./txn_count.d <PID> ^C Start 71 Commit 70 Total time (ns) 2312105013
SystemTap 對於追蹤腳本使用與 DTrace 不同的表示法,即使底層的追蹤點是相容的。一個值得注意的點是,在撰寫本文時,SystemTap 腳本必須使用雙底線來代替連字符來引用探針名稱。 預計這將在未來的 SystemTap 版本中修復。
您應該記住,DTrace 腳本需要仔細編寫和調試,否則收集的追蹤訊息可能毫無意義。 在大多數發現問題的情況下,都是檢測工具的錯誤,而不是底層系統。 在討論使用動態追蹤找到的訊息時,請務必附上使用的腳本,以便也可以檢查和討論它。
可以在開發人員想要的程式碼中定義新的探針,儘管這需要重新編譯。 以下是插入新探針的步驟
確定探針名稱和通過探針提供的資料
將探針定義新增到 src/backend/utils/probes.d
如果包含探針點的模組中尚未存在,請包含 pg_trace.h
,並在原始碼中的所需位置插入 TRACE_POSTGRESQL
探針巨集
重新編譯並驗證新的探針是否可用
範例: 以下是如何新增探針來追蹤所有新事務(依事務 ID)的範例。
確定探針將被命名為 transaction-start
,並且需要一個 LocalTransactionId
類型的參數
將探針定義新增到 src/backend/utils/probes.d
probe transaction__start(LocalTransactionId);
請注意在探針名稱中使用雙底線。 在使用探針的 DTrace 腳本中,需要將雙底線替換為連字符,因此 transaction-start
是要記錄給使用者的名稱。
在編譯時,transaction__start
會轉換為名為 TRACE_POSTGRESQL_TRANSACTION_START
的巨集(請注意此處的底線是單個),該巨集可通過包含 pg_trace.h
來取得。 將巨集呼叫新增到原始碼中的適當位置。 在這種情況下,它看起來像這樣
TRACE_POSTGRESQL_TRANSACTION_START(vxid.localTransactionId);
重新編譯並執行新的二進位檔後,通過執行以下 DTrace 命令來檢查新新增的探針是否可用。 你應該看到類似的輸出
# dtrace -ln transaction-start ID PROVIDER MODULE FUNCTION NAME 18705 postgresql49878 postgres StartTransactionCommand transaction-start 18755 postgresql49877 postgres StartTransactionCommand transaction-start 18805 postgresql49876 postgres StartTransactionCommand transaction-start 18855 postgresql49875 postgres StartTransactionCommand transaction-start 18986 postgresql49873 postgres StartTransactionCommand transaction-start
將追蹤巨集新增到 C 程式碼時,需要注意一些事項
您應該注意為探針的參數指定的資料類型是否與巨集中使用的變數的資料類型相符。 否則,您將會收到編譯錯誤。
在大多數平台上,如果 PostgreSQL 是使用 --enable-dtrace
建構的,則每當控制通過巨集時,都會評估追蹤巨集的參數,即使沒有進行追蹤。 如果您只是報告一些區域變數的值,通常不必擔心這一點。 但請注意不要將昂貴的函式呼叫放入參數中。 如果您需要這樣做,請考慮使用檢查來保護巨集,以查看追蹤是否實際上已啟用
if (TRACE_POSTGRESQL_TRANSACTION_START_ENABLED()) TRACE_POSTGRESQL_TRANSACTION_START(some_function(...));
每個追蹤巨集都有一個對應的 ENABLED
巨集。
如果您在文件中發現任何不正確、與您對特定功能的經驗不符或需要進一步澄清的地方,請使用此表單報告文件問題。