Files
ewoooc/n8n-workflows/13-slow-query-monitor.json
OoO d6d8777e41
All checks were successful
CD Pipeline / deploy (push) Successful in 1m12s
V10.601 收斂 Gemini 與密鑰治理
2026-06-06 14:52:46 +08:00

101 lines
7.0 KiB
JSON
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"name": "PostgreSQL 慢查詢監控 (含自動修復)",
"nodes": [
{
"parameters": { "rule": { "interval": [{ "field": "minutes", "minutesInterval": 15 }] } },
"id": "schedule", "name": "每 15 分鐘", "type": "n8n-nodes-base.scheduleTrigger", "typeVersion": 1.1, "position": [0, 0]
},
{
"parameters": {
"url": "http://192.168.0.110:5001/api/system/db/slow_queries",
"options": { "timeout": 30000 }
},
"id": "http-slow", "name": "取得慢查詢", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.1, "position": [220, 0],
"onError": "continueRegularOutput"
},
{
"parameters": {
"jsCode": "const response = $input.first().json;\nconst queries = response.slow_queries || response.data || [];\nconst threshold = 5; // 秒\n\n// 過濾超過閾值的查詢\nconst slowQueries = queries.filter(q => (q.duration || q.total_time || 0) > threshold * 1000);\n\nlet needsOptimize = false;\nlet optimizeReason = '';\n\n// 檢查是否需要優化\nif (slowQueries.length >= 3) {\n needsOptimize = true;\n optimizeReason = `發現 ${slowQueries.length} 個慢查詢`;\n}\n\n// 檢查是否有重複的慢查詢(可能需要索引)\nconst queryPatterns = {};\nslowQueries.forEach(q => {\n const pattern = (q.query || '').substring(0, 50);\n queryPatterns[pattern] = (queryPatterns[pattern] || 0) + 1;\n});\n\nconst repeatedSlowQueries = Object.entries(queryPatterns).filter(([k, v]) => v >= 2);\nif (repeatedSlowQueries.length > 0) {\n needsOptimize = true;\n optimizeReason += `, 重複慢查詢: ${repeatedSlowQueries.length} 種`;\n}\n\nreturn [{ json: { \n hasSlowQueries: slowQueries.length > 0,\n slowQueryCount: slowQueries.length,\n needsOptimize,\n optimizeReason,\n topSlowQueries: slowQueries.slice(0, 5).map(q => ({\n query: (q.query || '').substring(0, 100),\n duration: ((q.duration || q.total_time || 0) / 1000).toFixed(2) + 's',\n calls: q.calls || 1\n }))\n} }];"
},
"id": "analyze", "name": "分析慢查詢", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [440, 0]
},
{
"parameters": {
"conditions": {
"options": { "caseSensitive": true },
"conditions": [
{ "id": "c1", "leftValue": "={{ $json.hasSlowQueries }}", "rightValue": true, "operator": { "type": "boolean", "operation": "equals" } }
],
"combinator": "and"
}
},
"id": "if-slow", "name": "有慢查詢?", "type": "n8n-nodes-base.if", "typeVersion": 2, "position": [660, 0]
},
{
"parameters": {
"jsCode": "const data = $input.first().json;\nconst time = new Date().toLocaleString('zh-TW', {timeZone: 'Asia/Taipei'});\n\nlet queryList = '';\nif (data.topSlowQueries && data.topSlowQueries.length > 0) {\n queryList = data.topSlowQueries.map((q, i) => \n `${i+1}. ${q.duration} - ${q.query.substring(0, 60)}...`\n ).join('\\n');\n}\n\nconst text = '🐢 *PostgreSQL 慢查詢告警*\\n\\n' +\n '⏰ 時間: ' + time + '\\n' +\n '📊 慢查詢數: ' + data.slowQueryCount + '\\n\\n' +\n '🔍 *Top 5 慢查詢:*\\n```\\n' + queryList + '\\n```\\n\\n' +\n (data.needsOptimize ? '🔧 *建議執行優化*: ' + data.optimizeReason : '');\n\nreturn [{ json: { \n telegramBody: { chat_id: '5619078117', text: text, parse_mode: 'Markdown' },\n needsOptimize: data.needsOptimize\n} }];"
},
"id": "code-alert", "name": "組合告警", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [880, 100]
},
{
"parameters": {
"method": "POST", "url": "https://api.telegram.org/bot<TELEGRAM_BOT_TOKEN>/sendMessage",
"sendBody": true, "specifyBody": "json",
"jsonBody": "={{ JSON.stringify($json.telegramBody) }}"
},
"id": "telegram", "name": "發送告警", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.1, "position": [1100, 100]
},
{
"parameters": {
"conditions": {
"options": { "caseSensitive": true },
"conditions": [
{ "id": "c1", "leftValue": "={{ $json.needsOptimize }}", "rightValue": true, "operator": { "type": "boolean", "operation": "equals" } }
],
"combinator": "and"
}
},
"id": "if-optimize", "name": "需要優化?", "type": "n8n-nodes-base.if", "typeVersion": 2, "position": [1320, 100]
},
{
"parameters": {
"method": "POST",
"url": "http://192.168.0.110:5001/api/system/db/optimize",
"sendBody": true,
"specifyBody": "json",
"jsonBody": "{\"action\": \"vacuum_analyze\", \"auto_triggered\": true}",
"options": { "timeout": 120000 }
},
"id": "auto-optimize", "name": "自動優化", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.1, "position": [1540, 200]
},
{
"parameters": {
"jsCode": "const result = $input.first().json;\nconst success = result.success || result.status === 'ok';\nconst text = success \n ? '✅ *資料庫自動優化完成*\\n\\n執行了 VACUUM ANALYZE效能應該有所改善。'\n : '❌ *資料庫自動優化失敗*\\n\\n' + (result.error || result.message || '未知錯誤');\n\nreturn [{ json: { telegramBody: { chat_id: '5619078117', text: text, parse_mode: 'Markdown' } } }];"
},
"id": "code-result", "name": "組合結果", "type": "n8n-nodes-base.code", "typeVersion": 2, "position": [1760, 200]
},
{
"parameters": {
"method": "POST", "url": "https://api.telegram.org/bot<TELEGRAM_BOT_TOKEN>/sendMessage",
"sendBody": true, "specifyBody": "json",
"jsonBody": "={{ JSON.stringify($json.telegramBody) }}"
},
"id": "telegram-result", "name": "通知結果", "type": "n8n-nodes-base.httpRequest", "typeVersion": 4.1, "position": [1980, 200]
},
{ "parameters": {}, "id": "no-op", "name": "無慢查詢", "type": "n8n-nodes-base.noOp", "typeVersion": 1, "position": [880, -100] },
{ "parameters": {}, "id": "no-op2", "name": "不需優化", "type": "n8n-nodes-base.noOp", "typeVersion": 1, "position": [1540, 0] }
],
"connections": {
"每 15 分鐘": { "main": [[{ "node": "取得慢查詢", "type": "main", "index": 0 }]] },
"取得慢查詢": { "main": [[{ "node": "分析慢查詢", "type": "main", "index": 0 }]] },
"分析慢查詢": { "main": [[{ "node": "有慢查詢?", "type": "main", "index": 0 }]] },
"有慢查詢?": { "main": [[{ "node": "組合告警", "type": "main", "index": 0 }], [{ "node": "無慢查詢", "type": "main", "index": 0 }]] },
"組合告警": { "main": [[{ "node": "發送告警", "type": "main", "index": 0 }]] },
"發送告警": { "main": [[{ "node": "需要優化?", "type": "main", "index": 0 }]] },
"需要優化?": { "main": [[{ "node": "自動優化", "type": "main", "index": 0 }], [{ "node": "不需優化", "type": "main", "index": 0 }]] },
"自動優化": { "main": [[{ "node": "組合結果", "type": "main", "index": 0 }]] },
"組合結果": { "main": [[{ "node": "通知結果", "type": "main", "index": 0 }]] }
},
"settings": { "executionOrder": "v1" }
}