戰(zhàn):從PostgreSQL日志中快速定位慢查詢與性能瓶頸)
簡(jiǎn)介pgBadger是一款用純Perl語(yǔ)言編寫(xiě)的PostgreSQL日志分析器以解析速度見(jiàn)長(zhǎng)能夠自動(dòng)識(shí)別syslog、stderr、csvlog等常見(jiàn)日志格式并借助JavaScript圖表庫(kù)生成可視化報(bào)告適合DBA、運(yùn)維工程師及需要深度排查數(shù)據(jù)庫(kù)性能的開(kāi)發(fā)者在日常巡檢和故障分析中使用。資源為pgBadger 11.5的開(kāi)源發(fā)行版共54個(gè)文件、約2.2MB除核心Perl腳本外還包含jQuery、jqplot等前端圖表資源、測(cè)試腳本、文檔與許可證說(shuō)明整體結(jié)構(gòu)緊湊便于直接部署或二次開(kāi)發(fā)。目前已有440人學(xué)習(xí)查看。壓縮包內(nèi)附有完整源碼、構(gòu)建配置如Makefile.PL與MANIFEST、單元測(cè)試樣例及詳細(xì)文檔讀者既可快速上手生成日志分析報(bào)表也能參考其內(nèi)部實(shí)現(xiàn)擴(kuò)展自定義功能同時(shí)自帶對(duì)gzip壓縮日志的支持有助于在日志量大、存儲(chǔ)受限的環(huán)境下高效完成分析任務(wù)。1. pgBadger 是什么一份 PostgreSQL 日志能榨出多少信息有一次線上慢查詢排查手里擺著 2GB 的 PostgreSQL 日志我 grep 了大半個(gè)下午才湊出幾條 5 秒以上的語(yǔ)句那滋味不想再試第二次。pgBadger 就是一個(gè)為提高速度而構(gòu)建的 PostgreSQL 日志分析器Perl 寫(xiě)的命令行工具喂給它 PostgreSQL 的文本日志它吐出一份可交互的 HTML 報(bào)告慢查詢 Top、鎖等待、Checkpoint、臨時(shí)文件、全表掃描在這些區(qū)塊里一次看全。它不裝數(shù)據(jù)庫(kù)插件、不寫(xiě)回?cái)?shù)據(jù)庫(kù)、不需要改業(yè)務(wù)代碼唯一的前提是你把 PostgreSQL 的日志開(kāi)關(guān)按格式打開(kāi)。適合經(jīng)常要處理慢查詢的 DBA、后端負(fù)責(zé)人和 SRE日志越亂越有價(jià)值這份工具就是把“出了什么問(wèn)題”從黑匣子里翻出來(lái)的那雙手。它是開(kāi)源項(xiàng)目系統(tǒng)包里一條命令就能裝后面我會(huì)把配置、命令和踩過(guò)的坑一起展開(kāi)。2. 先搞清它分析什么pgBadger 的輸入、報(bào)告與提速原理2.1 它吃的是原生日志不是 pg_stat_statements上手 pgBadger 之前必須先分清兩個(gè)概念。pg_stat_statements 是 PostgreSQL 內(nèi)置插件長(zhǎng)期累計(jì)每類 SQL 的總耗時(shí)、執(zhí)行次數(shù)適合看趨勢(shì)而 pgBadger 解析的是數(shù)據(jù)庫(kù)進(jìn)程寫(xiě)出來(lái)的原始日志文件回答的是“這段時(shí)間里到底發(fā)生了什么”。常見(jiàn)誤解是我已經(jīng)開(kāi)了 pg_stat_statements還需要日志分析器嗎需要。pg_stat_statements 只記錄執(zhí)行過(guò)的語(yǔ)句聚合不記錄鎖等待過(guò)程、checkpoint 行為、臨時(shí)文件寫(xiě)入、連接斷開(kāi)這類旁路信息而這些往往是數(shù)據(jù)庫(kù)抖動(dòng)的大頭。還有一層容易忽視的區(qū)別MySQL 的慢日志有固定表結(jié)構(gòu)、自帶查詢接口把這個(gè)習(xí)慣帶過(guò)來(lái)會(huì)發(fā)現(xiàn) PostgreSQL 完全不同——它的默認(rèn)日志是純文本沒(méi)有內(nèi)置慢日志表最多靠 log_line_prefix 控制格式。這就是 pgBadger 這類開(kāi)源項(xiàng)目存在的意義把一條條裸日志變成可讀的指標(biāo)。能讓 pgBadger 出內(nèi)容的日志開(kāi)關(guān)大致有五個(gè)我一般這樣開(kāi)log_min_duration_statement 記錄超過(guò)閾值的慢語(yǔ)句log_checkpoints 記錄檢查點(diǎn)事件log_lock_waits 記錄鎖等待log_temp_files 記錄臨時(shí)文件log_autovacuum_min_duration 記錄自動(dòng) vacuum。每開(kāi)一個(gè)報(bào)告里就多一個(gè)區(qū)塊開(kāi)少了報(bào)告就瘦開(kāi)多了文件漲得快第 3 章會(huì)給出完整配置。2.2 原生日志里有什么一行日志能被拆出哪些字段pgBadger 的解析邏輯本質(zhì)上就是把你日志里每個(gè)字段映射到報(bào)告維度。拿一條真實(shí)的日志為例2024-06-01 10:00:00.123 CST [23456]: [123-1] userdba,dbapp,apppsql,client127.0.0.1 LOG: duration: 2500.123 ms statement: SELECT * FROM orders WHERE user_id 123;拆開(kāi)看這一行值多少錢開(kāi)頭時(shí)間戳給報(bào)告時(shí)間軸[23456] 是進(jìn)程號(hào)用于區(qū)分連接[123-1] 是會(huì)話行號(hào)pgBadger 靠它把一條跨行消息拼回同一事件user、db、app、client 分別是用戶、數(shù)據(jù)庫(kù)、客戶端應(yīng)用、來(lái)源地址報(bào)告按庫(kù)按用戶分組全得靠它們duration 是慢查詢耗時(shí)statement 是語(yǔ)句正文。pgBadger 還會(huì)把這條語(yǔ)句里的數(shù)字參數(shù)替換成 ?做參數(shù)歸一化所以同一條 SQL 的不同參數(shù)值會(huì)聚合在一起排名不會(huì)因?yàn)?where 條件里數(shù)字不同就被拆成幾百行。這里面最容易被忽略的是 [123-1] 的會(huì)話行號(hào)。PostgreSQL 的日志里一條 ERROR 常常帶出 detail 和 context 多行內(nèi)容而只有第一行才有前綴里的完整字段沒(méi)有序號(hào)多行消息會(huì)被當(dāng)成獨(dú)立事件報(bào)告里的語(yǔ)句會(huì)被攔腰截?cái)?。所?log_line_prefix 里有沒(méi)有 %l直接決定報(bào)告能不能拼全語(yǔ)句。這也是為什么很多人換了 log_line_prefix 之后報(bào)告突然“少了一半數(shù)據(jù)”的根本原因。2.3 單遍流式解析快在哪和自寫(xiě)腳本的差距標(biāo)題里“為提高速度而構(gòu)建”不是宣傳話術(shù)是它的實(shí)現(xiàn)方式?jīng)Q定的。pgBadger 處理日志是單遍流式一行一行讀正則提取出時(shí)間、進(jìn)程號(hào)、數(shù)據(jù)庫(kù)、用戶、語(yǔ)句耗時(shí)在內(nèi)存里做累加全程不把日志導(dǎo)入數(shù)據(jù)庫(kù)也不需要二次 ETL。這樣做最大的收益是內(nèi)存可控處理 10GB 級(jí)別的日志時(shí)內(nèi)存占用通常維持在幾百 MB 以內(nèi)而不是像導(dǎo)入數(shù)據(jù)庫(kù)統(tǒng)計(jì)那樣先把數(shù)據(jù)搬一遍。對(duì)比一下常見(jiàn)的替代方案就能看出差別。用 grep 加 awk 自己統(tǒng)計(jì)grep 本身很快但你要自己處理多行消息、自己按分鐘聚合、自己做 Top 排序?qū)懗鰜?lái)能用維護(hù)成本不小把日志灌進(jìn) PostgreSQL 再 SQL 分析要先清洗格式再建表導(dǎo)入日志一多大半時(shí)間花在導(dǎo)入而不是分析上。pgBadger 把這幾步都省了一條命令出 HTML還能直接讀 .gz、.bz2、.xz 壓縮日志不用先解壓占磁盤(pán)。新版本還支持多線程并行解析不同日志文件適合日志被按天切成很多小文件的場(chǎng)景。有一點(diǎn)要提醒它快不代表它對(duì)日志格式不挑。輸入格式越規(guī)整解析越快如果 log_line_prefix 配得亂七八糟它會(huì)先花時(shí)間做格式猜測(cè)甚至直接拒絕。所以想享受速度紅利前提是第 3 章的配置一步都不能省。2.4 開(kāi)源分發(fā)與版本差異從 GitHub 拿源碼還是走系統(tǒng)包pgBadger 是開(kāi)源項(xiàng)目用的 PostgreSQL License屬于寬松類許可證改完再分發(fā)也沒(méi)有像 GPL 那樣強(qiáng)的傳染性公司內(nèi)部二次開(kāi)發(fā)負(fù)擔(dān)小。你直接搜 GitHub 的 darold/pgbadger 就能看到倉(cāng)庫(kù)和 release 包很多 postgresql 安裝教程的末尾也會(huì)順手提它一句。獲取方式取決于你的操作系統(tǒng)。Debian/Ubuntu 的 apt 倉(cāng)庫(kù)、RHEL/CentOS 的 EPEL 倉(cāng)庫(kù)里基本都有 pgbadger 包一條命令裝完好處是依賴有人管。但系統(tǒng)倉(cāng)庫(kù)里的版本可能落后 GitHub 一個(gè)大版本而新版本往往意味著支持更新的 PostgreSQL 日志格式比如 PostgreSQL 15 之后新增的 jsonlog 日志格式老版本 pgBadger 就不認(rèn)。我的習(xí)慣是生產(chǎn)環(huán)境優(yōu)先用系統(tǒng)包遇到“日志格式識(shí)別不了”這類問(wèn)題時(shí)再去 GitHub 拉新 release 裝源碼版。裝完先跑一句 pgbadger --version確認(rèn)版本再往下走。3. 從 PostgreSQL 開(kāi)日志到跑出第一份 HTML完整配置與命令3.1 PostgreSQL 日志參數(shù)log_line_prefix 必須按這個(gè)格式配pgBadger 解析依賴兩個(gè)東西日志里有內(nèi)容格式它能認(rèn)。PostgreSQL 側(cè)需要改 postgresql.conf 里的這幾項(xiàng)改完執(zhí)行 SELECT pg_reload_conf(); 即可不用重啟實(shí)例logging_collector on log_destination stderr log_line_prefix %t [%p]: [%l-1] user%u,db%d,app%a,client%h log_min_duration_statement 1000 log_checkpoints on log_lock_waits on log_temp_files 0 log_autovacuum_min_duration 0log_line_prefix 是 pgBadger 最看重的格式。%t 是帶毫秒的時(shí)間戳%p 是進(jìn)程號(hào)%l 是會(huì)話行號(hào)前面說(shuō)過(guò)它負(fù)責(zé)把跨行日志拼回同一事件%u、%d、%a、%h 分別是用戶、數(shù)據(jù)庫(kù)、應(yīng)用名、客戶端地址報(bào)告里按庫(kù)按用戶分組就靠它們。注意行尾我留了一個(gè)空格這是為了讓消息正文與前綴隔開(kāi)pgBadger 匹配前綴時(shí)會(huì)少很多邊界問(wèn)題。log_min_duration_statement 設(shè) 1000 表示只記錄執(zhí)行超過(guò) 1 秒的語(yǔ)句如果你懷疑慢查詢被漏了先往小調(diào)比如 100代價(jià)是日志文件明顯變大。log_temp_files 設(shè) 0 表示記錄所有落盤(pán)的臨時(shí)文件不設(shè)這個(gè)值報(bào)告里的 Temp files 區(qū)塊就是空的。改完在 psql 里驗(yàn)證SHOW log_line_prefix; SELECT pg_reload_conf();SHOW 輸出的字符串要和配置文件完全一致如果這里帶轉(zhuǎn)義字符或者換行pgBadger 那邊大概率要踩坑。注意 reload 只對(duì)新產(chǎn)生的日志生效已經(jīng)寫(xiě)出去的舊日志格式不會(huì)變所以改完配置最好等幾分鐘再跑分析或者直接把舊日志移走。我在 5.1 里講的那個(gè)翻車案例就是沒(méi)等新日志直接排了 cron。3.2 安裝 pgBadgerapt、yum、源碼三選一安裝這一步?jīng)]有難度三個(gè)常見(jiàn)渠道如下# Debian / Ubuntu sudo apt install pgbadger -y # RHEL / CentOS 需要先開(kāi) EPEL sudo dnf install epel-release -y sudo dnf install pgbadger -y # 從 GitHub release 拿源碼包安裝 tar xzf pgbadger-*.tar.gz cd pgbadger-* perl Makefile.PL make sudo make installapt 和 dnf 適合圖省事的場(chǎng)景裝完直接有 pgbadger 命令。源碼安裝的好處是版本新perl Makefile.PL 階段會(huì)提示缺哪些 Perl 模塊常見(jiàn)的有 Text::CSV_XS 這類純文本解析加速模塊缺什么用 cpan 裝上即可。裝完驗(yàn)證一下pgbadger --version命令能輸出版本就說(shuō)明可執(zhí)行文件在 PATH 里。如果日志文件是 postgres 用戶寫(xiě)的而你用 root 跑 pgbadger記得文件要能讀、目錄要有執(zhí)行權(quán)限否則后面會(huì)遇到 Permission denied。還可以順手看一眼 pgbadger --help 里的支持列表確認(rèn)你本地的版本支持哪些日志格式尤其是 jsonlog。3.3 第一條命令與報(bào)告驗(yàn)證最小命令就一行指定輸出文件再把日志文件或通配符丟進(jìn)去pgbadger -o /tmp/pg_report.html /var/log/postgresql/postgresql-*.log-o 指定 HTML 輸出路徑。stderr 是最常見(jiàn)的日志格式pgBadger 會(huì)自動(dòng)識(shí)別所以不用顯式寫(xiě) -f stderr如果你用的是 csvlog 或 jsonlog才需要在 -f 參數(shù)里指定。日志多、內(nèi)存少時(shí)可以加 -q 關(guān)閉進(jìn)度條輸出。跑完屏幕上會(huì)出現(xiàn)一行類似 “Html report written to /tmp/pg_report.html” 的提示然后用瀏覽器打開(kāi)該文件。這里有個(gè)新手常踩的點(diǎn)日志文件名帶不帶日期后綴取決于你部署 PostgreSQL 時(shí)是否開(kāi)了日志輪轉(zhuǎn)通配符寫(xiě)得不對(duì)就會(huì)“找不到文件”。先 ls /var/log/postgresql/ 看真實(shí)文件名再寫(xiě)命令比盲猜靠譜。如果命令報(bào) “Wrong log_line_prefix” 或者解析完報(bào)告里全是 0回到 3.1 檢查 SHOW log_line_prefix 的輸出。第一次打開(kāi)報(bào)告先看總請(qǐng)求量有沒(méi)有數(shù)字再翻到 Top Queries 看有沒(méi)有語(yǔ)句這兩處都有內(nèi)容才說(shuō)明日志喂對(duì)了。3.4 新報(bào)告先看哪五個(gè)區(qū)塊第一次打開(kāi) HTML 報(bào)告很多人會(huì)被滿屏圖表晃到我建議只看五個(gè)區(qū)塊它們信息密度最高報(bào)告區(qū)塊能看出什么對(duì)應(yīng)日志開(kāi)關(guān)General Statistics總請(qǐng)求數(shù)、每分鐘請(qǐng)求數(shù)、平均耗時(shí)先看量級(jí)是否異常log_min_duration_statementTop Queries慢語(yǔ)句排行按總耗時(shí)、平均耗時(shí)、最大耗時(shí)排序log_min_duration_statementCheckpoints檢查點(diǎn)頻率和耗時(shí)看是否寫(xiě)盤(pán)壓力大log_checkpointsLock Waits鎖等待次數(shù)、等待時(shí)間定位鎖競(jìng)爭(zhēng)log_lock_waitsTemp Files每次查詢落盤(pán)的臨時(shí)文件大小判斷 work_mem 夠不夠log_temp_filesTop Queries 里的語(yǔ)句是做了參數(shù)歸一化的同一條 SQL 不同參數(shù)值會(huì)被聚合成一類字面量被替換成 ?所以排名更公平不會(huì)出現(xiàn)同一邏輯的 SQL 因?yàn)閰?shù)不同被拆成幾百行。看的時(shí)候先按“總耗時(shí)”排再按“平均耗時(shí)”排前者找總量?jī)词趾笳哒覇未萎惓!A硗鈭?bào)告里有按數(shù)據(jù)庫(kù)、按用戶的篩選器排查某個(gè)業(yè)務(wù)模塊的問(wèn)題時(shí)先把范圍縮到一個(gè)庫(kù)再看 Top比全局列表直觀得多。4. 把 pgBadger 變成日常巡檢時(shí)間窗、增量解析與定時(shí)任務(wù)4.1 常用參數(shù)怎么設(shè)-b、-e、-t、-d 的組合用法單次跑報(bào)告只是開(kāi)始要把 pgBadger 用成日常巡檢工具必須學(xué)會(huì)限定時(shí)間窗。下面幾個(gè)參數(shù)是我每次寫(xiě)腳本都要用到的參數(shù)作用我的常用值-b / --begin只解析該時(shí)間之后的行“2024-06-01 00:00:00”-e / --end只解析該時(shí)間之前的行“2024-06-02 00:00:00”-t / --topTop 查詢顯示條數(shù)30-d / --dbname只看某個(gè)數(shù)據(jù)庫(kù)業(yè)務(wù)庫(kù)名-s / --size最多讀取多少日志量視日志大小而定組合起來(lái)是這樣pgbadger -b 2024-06-01 00:00:00 -e 2024-06-02 00:00:00 \ -t 30 -o /tmp/report_day.html /var/log/postgresql/postgresql-*.log-b 和 -e 解析的是日志里的時(shí)間戳不是文件修改時(shí)間所以即使日志文件混著好幾天的內(nèi)容也能精確切出一天。時(shí)間格式要帶空格命令行里必須加引號(hào)這個(gè)細(xì)節(jié)在 cron 里最容易翻車少一對(duì)引號(hào) shell 就把時(shí)間拆成了兩個(gè)參數(shù)。如果你只要最近一小時(shí)-b 也可以用 date 命令動(dòng)態(tài)生成但注意時(shí)區(qū)和第 5 章那個(gè)坑。-s我一般用在探測(cè)場(chǎng)景不確定日志有多大、機(jī)器內(nèi)存緊的時(shí)候先限制只讀最后 2GB 跑一份小報(bào)告觀察資源消耗再?zèng)Q定要不要全量。4.2 增量分析不要讓報(bào)告一次比一次慢全量日志只有幾 GB 時(shí)多久跑一次無(wú)所謂跑了一個(gè)月后還每次從頭解析就是在浪費(fèi)時(shí)間。增量分析的目的很直接只處理上次跑完之后新產(chǎn)生的日志。常見(jiàn)做法有兩種。第一種是時(shí)間窗法配合日志輪轉(zhuǎn)每天用 -b 限定昨天零點(diǎn)、-e 限定今天零點(diǎn)只解析前一天。第二種是偏移記錄法pgBadger 支持記錄上次解析到的日志偏移下次從偏移處繼續(xù)參數(shù)名在不同版本里略有差別裝好后先跑一下 pgbadger --help 確認(rèn)你本地版本的寫(xiě)法。我自己的生產(chǎn)環(huán)境更常用時(shí)間窗法因?yàn)檫壿嬛卑?、排障容易偏移法省解析時(shí)間但日志被輪轉(zhuǎn)刪除后會(huì)丟失中間段反而不適合日志保留期短的環(huán)境。# 每天只解析 24 小時(shí)內(nèi)的日志 pgbadger -b $(date -d yesterday 00:00 %Y-%m-%d %H:%M:%S) \ -e $(date %Y-%m-%d %H:%M:%S) \ -o /var/www/pgbadger/daily.html \ /var/log/postgresql/postgresql-*.log這段腳本用 date 動(dòng)態(tài)生成起止時(shí)間配合 crontab 每天執(zhí)行就是一套最樸素的增量日?qǐng)?bào)。注意 date -d 語(yǔ)法在 Linux 和 macOS 上不一樣Linux 用 -dmacOS 要寫(xiě) -v-1d服務(wù)器場(chǎng)景基本都是 Linux不會(huì)有歧義。切換增量方式或者日志目錄變化之后先跑一次全量報(bào)告作為新基線免得新舊報(bào)告口徑對(duì)不上。4.3 用 cron 定時(shí)生成日?qǐng)?bào)并自動(dòng)清理舊報(bào)告定時(shí)任務(wù)我放在 /opt/scripts/pgbadger_daily.sh內(nèi)容比單條命令多兩件事輸出目錄按日期命名、超過(guò) 90 天的舊報(bào)告自動(dòng)刪除。日志權(quán)限記得給執(zhí)行用戶可讀權(quán)限我是用一個(gè)專門的 postgres 系統(tǒng)賬戶跑的避免和業(yè)務(wù)權(quán)限攪在一起。#!/bin/bash LOG_DIR/var/log/postgresql OUT_DIR/var/www/pgbadger YESTERDAY$(date -d yesterday %Y-%m-%d) TODAY$(date %Y-%m-%d) pgbadger -b $YESTERDAY 00:00:00 -e $TODAY 00:00:00 \ -o $OUT_DIR/report-$YESTERDAY.html \ $LOG_DIR/postgresql-*.log /dev/null find $OUT_DIR -name report-*.html -mtime 90 -deletecrontab 里加一行30 2 * * * /opt/scripts/pgbadger_daily.sh /var/log/pgbadger_cron.log 21每天凌晨 2 點(diǎn)半執(zhí)行正好避開(kāi)業(yè)務(wù)高峰早上上班打開(kāi)瀏覽器就能看到昨天全天的報(bào)告。重定向到日志文件是為了排障腳本里 date、通配符出錯(cuò)時(shí)能看到痕跡。跑失敗了別只盯著 pgbadger 報(bào)錯(cuò)先看這個(gè)日志文件血淚經(jīng)驗(yàn)是八成問(wèn)題出在引號(hào)或文件權(quán)限而不是 pgBadger 本身。4.4 巡檢閾值哪些數(shù)字一報(bào)警就該處理報(bào)告生成只是第一步會(huì)讀才有效果。我按踩過(guò)坑的經(jīng)驗(yàn)給一檔初始閾值你可以根據(jù)業(yè)務(wù)調(diào)整指標(biāo)注意閾值說(shuō)明每分鐘請(qǐng)求數(shù)較前一日驟降 50% 以上連接可能被鎖或連接池耗盡慢查詢最大耗時(shí)超過(guò) 5 秒單語(yǔ)句異常重點(diǎn)看 Top Queries鎖等待次數(shù)大于 0 且平均等待超 1 秒業(yè)務(wù)側(cè)要查鎖來(lái)源臨時(shí)文件總量單日累計(jì)超 1GB優(yōu)先調(diào)大 work_mem 再觀察Checkpoint 間隔頻繁且耗時(shí)高檢查 max_wal_size 和磁盤(pán)寫(xiě)入這些閾值不是真理是一份讓你有據(jù)可依的起點(diǎn)。每種業(yè)務(wù)對(duì)耗時(shí)的容忍度不同運(yùn)行一個(gè)月后把報(bào)告里的數(shù)字和線上故障時(shí)間對(duì)上再回改閾值比拍腦袋準(zhǔn)得多。特別是臨時(shí)文件這個(gè)指標(biāo)它和你業(yè)務(wù)里報(bào)表查詢的頻度強(qiáng)相關(guān)同一套閾值放到 OLTP 和 OLAP 集群上完全是兩個(gè)世界。5. 避坑pgBadger 出真報(bào)告前最容易翻車的五個(gè)地方5.1 log_line_prefix 不匹配一上來(lái)就報(bào)錯(cuò)現(xiàn)象命令執(zhí)行后立刻報(bào) “Wrong log_line_prefix” 或 “cant parse”報(bào)告文件沒(méi)生成。原因postgresql.conf 里的 log_line_prefix 不是 pgBadger 認(rèn)識(shí)的格式常見(jiàn)是少了 %t 或 %l或者手寫(xiě)的格式和實(shí)際配置有出入。解決先在 psql 里執(zhí)行 SHOW log_line_prefix;拿真實(shí)輸出檢查。最簡(jiǎn)單的做法是把 3.1 的配置原樣粘貼并 reload再等新日志產(chǎn)生后重跑。如果你的前綴必須帶業(yè)務(wù)標(biāo)識(shí)把 SHOW 輸出的字符串原樣用 -p 參數(shù)傳給 pgBadger兩邊對(duì)齊就不再報(bào)錯(cuò)。這屬于格式對(duì)不上就老老實(shí)實(shí)改配置的問(wèn)題沒(méi)有玄學(xué)空間。5.2 報(bào)告空白但日志明明有內(nèi)容現(xiàn)象報(bào)告生成成功打開(kāi) General 頁(yè)面也有連接數(shù)但 Top Queries 和慢查詢區(qū)塊是空的。原因日志里只有連接、斷開(kāi)的記錄沒(méi)有語(yǔ)句執(zhí)行時(shí)長(zhǎng)。log_min_duration_statement 沒(méi)開(kāi)或者設(shè)得太大示例里設(shè) 1000 意味著只有超過(guò) 1 秒的語(yǔ)句才帶 duration 字段小于 1 秒的語(yǔ)句完全不進(jìn)日志。解決SHOW log_min_duration_statement; 確認(rèn)值。想分析所有語(yǔ)句臨時(shí)設(shè)成 0日志量和報(bào)告體積會(huì)明顯變大不要一直開(kāi)著想找相對(duì)慢的設(shè) 100 到 300 更實(shí)用。另外注意 log_statement 和 log_min_duration_statement 不要同時(shí)開(kāi)兩者疊加會(huì)產(chǎn)生重復(fù)記錄報(bào)告里的數(shù)字是虛胖的。5.3 報(bào)告時(shí)間和本地時(shí)間對(duì)不上現(xiàn)象報(bào)告的橫軸是 UTC 時(shí)間和業(yè)務(wù)日志、告警平臺(tái)差 8 小時(shí)對(duì)不上號(hào)。原因PostgreSQL 的 log_timezone 參數(shù)控制日志時(shí)間戳?xí)r區(qū)默認(rèn)可能跟服務(wù)器本地時(shí)區(qū)不一致pgBadger 按日志內(nèi)的 %t 解析不換算所以報(bào)告時(shí)間和日志本身一致但和你 date 看到的本地時(shí)間不一致。解決在 postgresql.conf 里把 log_timezone 設(shè)成業(yè)務(wù)所在時(shí)區(qū)比如log_timezone Asia/Shanghaireload 后重新生成的日志就對(duì)齊了。歷史日志沒(méi)法補(bǔ)救只能按差值換算。寫(xiě) -b/-e 時(shí)也帶上時(shí)區(qū)偏移比如 “2024-06-01 00:00:0008”讓 pgBadger 明確邊界避免整點(diǎn)對(duì)不上。5.4 增量數(shù)字重復(fù)越堆越大現(xiàn)象每天的日?qǐng)?bào)里某個(gè)慢查詢的總耗時(shí)比昨天翻倍明明線上沒(méi)有新慢查詢。原因時(shí)間窗沒(méi)生效cron 命令里的引號(hào)丟了date 命令實(shí)際執(zhí)行成了兩個(gè)參數(shù)-b 被 shell 拆掉pgBadger 退化成解析全部日志或者日志文件沒(méi)輪轉(zhuǎn)天天解析同一批文件。解決先手動(dòng)跑一遍 4.3 的腳本看生成報(bào)告的 General 頁(yè)時(shí)間范圍是不是只有昨天一天再把日志輪轉(zhuǎn)配好建議 PostgreSQL 側(cè)按天產(chǎn)生新文件已歸檔的壓縮移走。清理一次全量報(bào)告作為新基線之后增量才會(huì)準(zhǔn)。增量功能依賴穩(wěn)定的文件列表日志文件改個(gè)名、目錄換位置都會(huì)打破它變更后先跑一次全量。5.5 日志太大把磁盤(pán)和內(nèi)存打爆現(xiàn)象解析 30GB 日志時(shí)/tmp 寫(xiě)滿或內(nèi)存飆升機(jī)器卡死。原因pgBadger 雖然流式讀日志但最后生成 HTML 需要把所有聚合結(jié)果寫(xiě)進(jìn)一個(gè)文件報(bào)告本身可能幾百 MB開(kāi)啟多線程并行解析時(shí)每個(gè)線程都有自己的緩沖內(nèi)存占用成倍上漲輸出路徑默認(rèn)在 /tmp小分區(qū)很快被撐爆。解決先用 -s 限制日志讀取量探測(cè)性能比如 pgbadger -s 2G -o /tmp/test.html 先跑一小段把 -O 指向有足夠空間的目錄別用 /tmp日志按天拆開(kāi)、分批解析成多個(gè)報(bào)告再配合歸檔。壓縮日志能減少 I/OpgBadger 直接支持 .gz 文件建議輪轉(zhuǎn)時(shí)順手 gzip既省磁盤(pán)又省命令行通配符的長(zhǎng)度。6. 接進(jìn)告警與抽查驗(yàn)證用 pg_stat_statements 給報(bào)告背書(shū)pgBadger 報(bào)告的價(jià)值在于快速定位但別把唯一一份報(bào)告當(dāng)真相。我現(xiàn)在的習(xí)慣是報(bào)告出了 Top 慢查詢先去數(shù)據(jù)庫(kù)里用 pg_stat_statements 交叉驗(yàn)證一遍。SELECT query, calls, total_exec_time, mean_exec_time FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5;注意 PostgreSQL 11 之前的字段叫 total_time之后改成了 total_exec_time版本不同查詢也要跟著改。對(duì)照方式很簡(jiǎn)單報(bào)告里的 Top 語(yǔ)句應(yīng)該能在上面結(jié)果里找到同款數(shù)值量級(jí)大致一致。如果報(bào)告里某條語(yǔ)句總耗時(shí)很高、pg_stat_statements 里卻幾乎沒(méi)有先別急著下結(jié)論檢查是不是日志采樣窗口和統(tǒng)計(jì)窗口不一致或者語(yǔ)句在日志里被拆成了多行沒(méi)拼全。報(bào)告負(fù)責(zé)“懷疑”pg_stat_statements 負(fù)責(zé)“確認(rèn)”兩個(gè)工具對(duì)著看就不會(huì)被單個(gè)視角帶偏。關(guān)于歸檔我還會(huì)把每天的 HTML 報(bào)告按周打包保留 90 天方便追溯“上周五那次抖動(dòng)是不是同一批慢查詢”。打包用 tar 就行不展開(kāi)。真要接告警我會(huì)在 cron 腳本里加一行 grep從當(dāng)天日志里撈出耗時(shí)最大的那條語(yǔ)句拼成一行文本告警不要直接解析 HTML那東西改版一次壞一次。最后分享一個(gè)血淚教訓(xùn)我剛開(kāi)始用 pgBadger 時(shí)改完配置直接排了 cron結(jié)果第二天報(bào)告全空查了一圈發(fā)現(xiàn)是 reload 之后忘記等新日志產(chǎn)生歷史日志又是舊前綴白白浪費(fèi)一天?,F(xiàn)在我的固定動(dòng)作是改完配置先手動(dòng)跑一條 10 分鐘的小日志確認(rèn)報(bào)告有數(shù)據(jù)、時(shí)間正確再上 cron。先小后大、先時(shí)間窗后全量、先單文件后通配符這三條順序能擋住絕大部分翻車。希望幫到你。本文還有配套的精品資源點(diǎn)擊獲取