未记录事实核验 · 状态:维护中

记录仅说明核验时间与维护状态,不保证适用于所有环境;使用前请留意正文中的版本、适用条件与风险提示。

warning 适用边界与维护说明

本文适合具备Presto与系统运维基础的读者学习跨日志、查询指标、内存趋势、网络连接和故障响应的综合排障设计,仅作结构审阅,未作外部事实核验或运行验证,不能作为可直接部署的生产运维方案。诊断SQL需要适配实际集群的运行时表、字段和时间类型,并自行提供日志采集表;执行计划函数是概念占位,示例阈值与关键词规则不能证明根因。REST调用假定明文HTTP和特定节点响应结构,使用前须适配认证、TLS及接口格式。当前Worker健康计数未区分活跃与预期节点,紧急处理仍可能受历史错误触发,实时错误告警缺少时间窗口、重置与去重;查询改写会生成缺少列清单的SQL,且近似聚合改变精确计数语义;内存使用率未处理零分母,带宽方法也不能提供可靠测速结果。RSS增长只提示进一步调查,TCP连接与Ping只反映已执行检查的结果;清理系统页缓存不等于释放JVM堆。清缓存、终止进程、覆盖配置及删除文件只可在明确授权、限定实例、查询排空并具备备份与回滚条件的受控环境中评估,不得直接接入生产Cron,也不能把脚本输出当作修复成功的证明。

Presto故障诊断与调试 - 企业级问题排查与性能调优指南

abstract 📋 本节概览

  • 🎯 学习目标:掌握Presto生产环境的故障诊断方法和调试技能
  • 📚 前置知识:监控与告警 和 系统运维基础
  • ⏱️ 预计用时:160分钟

🏗️ 故障诊断架构

📊 诊断体系概览

%%{init: {"theme":"base","themeVariables":{"fontFamily":"PingFang SC, -apple-system, Source Han Serif SC, serif","fontSize":"16px","background":"#FDFBF5","primaryColor":"#FDFBF5","primaryTextColor":"#2D2A26","primaryBorderColor":"#6E6A60","secondaryColor":"#E8EFE8","secondaryBorderColor":"#8FA890","secondaryTextColor":"#2D2A26","tertiaryColor":"#FDFBF5","tertiaryBorderColor":"#9A9488","tertiaryTextColor":"#6E6A60","lineColor":"#6E6A60","textColor":"#2D2A26","mainBkg":"#FDFBF5","nodeBorder":"#2D5A3D","clusterBkg":"#F3EEDF","clusterBorder":"#9A9488","edgeLabelBackground":"#FDFBF5"},"flowchart":{"htmlLabels":true,"curve":"basis","padding":20,"nodeSpacing":60,"rankSpacing":70,"useMaxWidth":false},"themeCSS":".node rect, .node polygon, .node circle { stroke-width: 1.5px; } .edgePath .path { stroke-width: 1.5px; } .cluster-label .nodeLabel, .cluster-label foreignObject { color: #2D2A26 !important; } .edgeLabel, .edgeLabel foreignObject { color: #2D2A26 !important; background-color: #FDFBF5; } .edgeLabel rect { fill: #FDFBF5; }"}}%%
flowchart TB
    subgraph "Presto故障诊断体系"
        A[问题识别<br/>🔍 Problem Detection]
        B[信息收集<br/>📊 Data Collection]
        C[根因分析<br/>🔬 Root Cause Analysis]
        D[解决方案<br/>🔧 Solution Implementation]
        E[验证修复<br/>✅ Fix Verification]
        F[预防措施<br/>🛡️ Prevention]
    end
    subgraph "诊断工具链"
        G[日志分析<br/>📝 Log Analysis]
        H[指标监控<br/>📈 Metrics Monitoring]
        I[查询分析<br/>🔍 Query Analysis]
        J[系统状态<br/>💻 System Status]
        K[网络诊断<br/>🌐 Network Diagnosis]
    end
    subgraph "常见问题类型"
        L[性能问题<br/>⚡ Performance Issues]
        M[连接问题<br/>🔗 Connection Issues]
        N[内存问题<br/>💾 Memory Issues]
        O[查询失败<br/>❌ Query Failures]
        P[集群问题<br/>🏗️ Cluster Issues]
    end
    A --> B --> C --> D --> E --> F
    B --> G
    B --> H
    B --> I
    B --> J
    B --> K
    C --> L
    C --> M
    C --> N
    C --> O
    C --> P
    style A fill:#ffebee
    style L fill:#e3f2fd
    style P fill:#f3e5f5

🔍 日志分析与诊断

📝 日志系统架构

-- 创建日志分析视图
CREATE VIEW catalog.schema.presto_log_analysis AS
WITH parsed_logs AS (
    SELECT 
        timestamp,
        level,
        thread,
        logger,
        message,
        -- 解析查询ID
        REGEXP_EXTRACT(message, 'Query ([a-f0-9_]+)', 1) AS query_id,
        -- 解析错误代码
        REGEXP_EXTRACT(message, 'Error code: ([0-9]+)', 1) AS error_code,
        -- 解析执行时间
        REGEXP_EXTRACT(message, 'elapsed ([0-9.]+)s', 1) AS elapsed_seconds,
        -- 解析内存使用
        REGEXP_EXTRACT(message, 'memory: ([0-9.]+[KMGT]?B)', 1) AS memory_usage
    FROM catalog.schema.presto_logs
),
log_categories AS (
    SELECT 
        *,
        CASE 
            WHEN logger LIKE '%QueryManager%' THEN 'query_management'
            WHEN logger LIKE '%TaskManager%' THEN 'task_management'
            WHEN logger LIKE '%MemoryManager%' THEN 'memory_management'
            WHEN logger LIKE '%ConnectorManager%' THEN 'connector_management'
            WHEN logger LIKE '%SecurityManager%' THEN 'security'
            ELSE 'general'
        END AS log_category,
        CASE 
            WHEN level = 'ERROR' THEN 4
            WHEN level = 'WARN' THEN 3
            WHEN level = 'INFO' THEN 2
            WHEN level = 'DEBUG' THEN 1
            ELSE 0
        END AS severity_level
    FROM parsed_logs
)
SELECT * FROM log_categories;

🚨 错误日志分析

#!/bin/bash
# 错误日志分析脚本

LOG_DIR="/var/presto/logs"
ANALYSIS_DIR="/var/presto/analysis"
DATE=$(date +%Y%m%d)

# 创建分析目录
mkdir -p $ANALYSIS_DIR

# 分析错误模式
analyze_error_patterns() {
    echo "=== Error Pattern Analysis - $(date) ===" > $ANALYSIS_DIR/error_analysis_$DATE.txt
    
    # TOP 10 错误类型
    echo "Top 10 Error Types:" >> $ANALYSIS_DIR/error_analysis_$DATE.txt
    grep -h "ERROR" $LOG_DIR/*.log | \
    sed 's/.*ERROR.*\] //' | \
    awk '{print $1}' | \
    sort | uniq -c | sort -rn | head -10 >> $ANALYSIS_DIR/error_analysis_$DATE.txt
    
    # 查询失败分析
    echo -e "\nQuery Failure Analysis:" >> $ANALYSIS_DIR/error_analysis_$DATE.txt
    grep -h "Query.*failed" $LOG_DIR/*.log | \
    grep -o "Query [a-f0-9_]*" | \
    sort | uniq -c | sort -rn | head -20 >> $ANALYSIS_DIR/error_analysis_$DATE.txt
    
    # 内存相关错误
    echo -e "\nMemory Related Errors:" >> $ANALYSIS_DIR/error_analysis_$DATE.txt
    grep -h -i "memory\|oom\|heap" $LOG_DIR/*.log | \
    grep -i "error\|exception" | \
    wc -l >> $ANALYSIS_DIR/error_analysis_$DATE.txt
    
    # 连接错误
    echo -e "\nConnection Errors:" >> $ANALYSIS_DIR/error_analysis_$DATE.txt
    grep -h -i "connection\|timeout\|refused" $LOG_DIR/*.log | \
    grep -i "error\|exception" | \
    wc -l >> $ANALYSIS_DIR/error_analysis_$DATE.txt
}

# 性能日志分析
analyze_performance_logs() {
    echo "=== Performance Log Analysis - $(date) ===" > $ANALYSIS_DIR/performance_analysis_$DATE.txt
    
    # 慢查询分析
    echo "Slow Queries (>10s):" >> $ANALYSIS_DIR/performance_analysis_$DATE.txt
    grep -h "elapsed.*s" $LOG_DIR/*.log | \
    awk '{
        match($0, /elapsed ([0-9.]+)s/, arr)
        if (arr[1] > 10) {
            match($0, /Query ([a-f0-9_]+)/, query)
            print arr[1] "s", query[1]
        }
    }' | sort -rn | head -20 >> $ANALYSIS_DIR/performance_analysis_$DATE.txt
    
    # 资源使用分析
    echo -e "\nResource Usage Analysis:" >> $ANALYSIS_DIR/performance_analysis_$DATE.txt
    grep -h "memory:" $LOG_DIR/*.log | \
    awk '{
        match($0, /memory: ([0-9.]+[KMGT]?B)/, arr)
        print arr[1]
    }' | sort | uniq -c | sort -rn | head -10 >> $ANALYSIS_DIR/performance_analysis_$DATE.txt
}

# 执行分析
analyze_error_patterns
analyze_performance_logs

echo "Analysis completed. Results saved to $ANALYSIS_DIR/"

📊 实时日志监控

#!/usr/bin/env python3
# 实时日志监控脚本

import re
import time
import json
from collections import defaultdict
from datetime import datetime

class PrestoLogMonitor:
    def __init__(self, log_file, alert_threshold=10):
        self.log_file = log_file
        self.alert_threshold = alert_threshold
        self.error_counts = defaultdict(int)
        self.query_stats = defaultdict(dict)
        
    def parse_log_line(self, line):
        """解析日志行"""
        patterns = {
            'timestamp': r'(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z)',
            'level': r'\[(ERROR|WARN|INFO|DEBUG)\]',
            'query_id': r'Query ([a-f0-9_]+)',
            'elapsed_time': r'elapsed ([0-9.]+)s',
            'memory_usage': r'memory: ([0-9.]+[KMGT]?B)',
            'error_code': r'Error code: ([0-9]+)',
            'stage': r'Stage-([0-9]+)',
            'task': r'Task ([a-f0-9_.-]+)'
        }
        
        result = {'message': line}
        for key, pattern in patterns.items():
            match = re.search(pattern, line)
            if match:
                result[key] = match.group(1)
        
        return result
    
    def analyze_error(self, parsed_line):
        """分析错误"""
        if parsed_line.get('level') == 'ERROR':
            error_type = self._extract_error_type(parsed_line)
            self.error_counts[error_type] += 1
            
            # 错误阈值告警
            if self.error_counts[error_type] >= self.alert_threshold:
                self._send_alert(error_type, self.error_counts[error_type])
    
    def analyze_performance(self, parsed_line):
        """分析性能"""
        query_id = parsed_line.get('query_id')
        elapsed_time = parsed_line.get('elapsed_time')
        memory_usage = parsed_line.get('memory_usage')
        
        if query_id:
            if elapsed_time:
                self.query_stats[query_id]['elapsed_time'] = float(elapsed_time)
            if memory_usage:
                self.query_stats[query_id]['memory_usage'] = memory_usage
            
            # 性能告警
            if elapsed_time and float(elapsed_time) > 300:  # 5分钟
                self._send_performance_alert(query_id, elapsed_time)
    
    def _extract_error_type(self, parsed_line):
        """提取错误类型"""
        message = parsed_line.get('message', '')
        if 'OutOfMemoryError' in message:
            return 'memory_error'
        elif 'TimeoutException' in message:
            return 'timeout_error'
        elif 'ConnectException' in message:
            return 'connection_error'
        elif 'SQLException' in message:
            return 'sql_error'
        else:
            return 'general_error'
    
    def _send_alert(self, error_type, count):
        """发送告警"""
        alert_message = {
            'timestamp': datetime.now().isoformat(),
            'type': 'error_threshold',
            'error_type': error_type,
            'count': count,
            'threshold': self.alert_threshold
        }
        print(f"ALERT: {json.dumps(alert_message)}")
    
    def _send_performance_alert(self, query_id, elapsed_time):
        """发送性能告警"""
        alert_message = {
            'timestamp': datetime.now().isoformat(),
            'type': 'performance_alert',
            'query_id': query_id,
            'elapsed_time': elapsed_time,
            'threshold': '300s'
        }
        print(f"PERFORMANCE ALERT: {json.dumps(alert_message)}")
    
    def monitor(self):
        """监控日志"""
        try:
            with open(self.log_file, 'r') as f:
                # 移动到文件末尾
                f.seek(0, 2)
                
                while True:
                    line = f.readline()
                    if line:
                        parsed = self.parse_log_line(line.strip())
                        self.analyze_error(parsed)
                        self.analyze_performance(parsed)
                    else:
                        time.sleep(1)
        except KeyboardInterrupt:
            print("Monitoring stopped.")
        except Exception as e:
            print(f"Monitoring error: {e}")

# 使用示例
if __name__ == "__main__":
    monitor = PrestoLogMonitor('/var/presto/logs/server.log')
    monitor.monitor()

🔧 查询诊断与优化

📈 查询性能分析

-- 查询性能诊断视图
CREATE VIEW catalog.schema.query_performance_diagnosis AS
WITH query_metrics AS (
    SELECT 
        query_id,
        session_user,
        catalog,
        schema,
        query,
        state,
        created,
        started,
        "end",
        elapsed_time,
        execution_time,
        analysis_time,
        distributed_planning_time,
        total_planning_time,
        finishing_time,
        total_cpu_time,
        total_scheduled_time,
        total_blocked_time,
        peak_user_memory_bytes,
        peak_task_user_memory,
        peak_task_total_memory,
        physical_input_bytes,
        physical_input_rows,
        internal_network_bytes,
        internal_network_rows,
        total_bytes,
        total_rows,
        output_bytes,
        output_rows,
        written_bytes,
        written_rows
    FROM system.runtime.queries
    WHERE created >= current_timestamp - interval '1' hour
),
performance_metrics AS (
    SELECT 
        *,
        -- 计算各种比率
        CASE 
            WHEN total_rows > 0 THEN physical_input_bytes / total_rows 
            ELSE 0 
        END AS bytes_per_row,
        CASE 
            WHEN elapsed_time.seconds > 0 THEN physical_input_bytes / elapsed_time.seconds 
            ELSE 0 
        END AS bytes_per_second,
        CASE 
            WHEN elapsed_time.seconds > 0 THEN total_rows / elapsed_time.seconds 
            ELSE 0 
        END AS rows_per_second,
        -- 内存效率
        CASE 
            WHEN physical_input_bytes > 0 THEN peak_user_memory_bytes / physical_input_bytes 
            ELSE 0 
        END AS memory_efficiency,
        -- CPU效率
        CASE 
            WHEN elapsed_time.seconds > 0 THEN total_cpu_time.seconds / elapsed_time.seconds 
            ELSE 0 
        END AS cpu_efficiency,
        -- 网络效率
        CASE 
            WHEN total_bytes > 0 THEN internal_network_bytes / total_bytes 
            ELSE 0 
        END AS network_efficiency
    FROM query_metrics
),
performance_classification AS (
    SELECT 
        *,
        CASE 
            WHEN elapsed_time.seconds > 300 THEN 'very_slow'
            WHEN elapsed_time.seconds > 60 THEN 'slow'
            WHEN elapsed_time.seconds > 10 THEN 'moderate'
            ELSE 'fast'
        END AS performance_class,
        CASE 
            WHEN peak_user_memory_bytes > 10 * 1024 * 1024 * 1024 THEN 'high_memory'  -- 10GB
            WHEN peak_user_memory_bytes > 1 * 1024 * 1024 * 1024 THEN 'medium_memory'  -- 1GB
            ELSE 'low_memory'
        END AS memory_class,
        CASE 
            WHEN cpu_efficiency > 0.8 THEN 'cpu_intensive'
            WHEN cpu_efficiency > 0.3 THEN 'cpu_moderate'
            ELSE 'cpu_light'
        END AS cpu_class
    FROM performance_metrics
)
SELECT * FROM performance_classification;

🔍 查询执行计划分析

-- 执行计划诊断工具
WITH FUNCTION analyze_execution_plan(query_text VARCHAR) 
RETURNS TABLE(
    stage_id VARCHAR,
    operation VARCHAR,
    estimated_rows BIGINT,
    estimated_bytes BIGINT,
    join_type VARCHAR,
    join_strategy VARCHAR,
    partition_count INTEGER,
    bottleneck_probability DOUBLE
) AS (
    -- 这是一个概念性的函数,实际实现需要访问执行计划API
    SELECT 
        'stage_' || CAST(ROW_NUMBER() OVER() AS VARCHAR),
        'scan',
        1000000,
        100000000,
        NULL,
        NULL,
        100,
        0.2
);

-- 查询瓶颈识别
CREATE VIEW catalog.schema.query_bottleneck_analysis AS
SELECT 
    q.query_id,
    q.query,
    q.elapsed_time,
    q.total_cpu_time,
    q.peak_user_memory_bytes,
    q.physical_input_bytes,
    -- 瓶颈分析
    CASE 
        WHEN q.total_cpu_time.seconds / q.elapsed_time.seconds > 0.8 THEN 'CPU_BOUND'
        WHEN q.peak_user_memory_bytes > 8 * 1024 * 1024 * 1024 THEN 'MEMORY_BOUND'
        WHEN q.physical_input_bytes / q.elapsed_time.seconds < 10 * 1024 * 1024 THEN 'IO_BOUND'
        WHEN q.internal_network_bytes > q.physical_input_bytes * 2 THEN 'NETWORK_BOUND'
        ELSE 'BALANCED'
    END AS bottleneck_type,
    -- 优化建议
    CASE 
        WHEN q.total_cpu_time.seconds / q.elapsed_time.seconds > 0.8 THEN 
            'Consider adding more CPU resources or optimizing complex operations'
        WHEN q.peak_user_memory_bytes > 8 * 1024 * 1024 * 1024 THEN 
            'Increase memory limits or optimize memory usage'
        WHEN q.physical_input_bytes / q.elapsed_time.seconds < 10 * 1024 * 1024 THEN 
            'Check storage performance or add more parallel workers'
        WHEN q.internal_network_bytes > q.physical_input_bytes * 2 THEN 
            'Optimize JOIN operations or network configuration'
        ELSE 'Query is well balanced'
    END AS optimization_recommendation
FROM catalog.schema.query_performance_diagnosis q
WHERE q.state = 'FINISHED' 
  AND q.elapsed_time.seconds > 30;

⚡ 自动化查询优化

#!/usr/bin/env python3
# 自动查询优化建议工具

import re
import sqlparse
from collections import defaultdict

class PrestoQueryOptimizer:
    def __init__(self):
        self.optimization_rules = {
            'join_optimization': self._analyze_joins,
            'predicate_pushdown': self._analyze_predicates,
            'column_pruning': self._analyze_columns,
            'partition_optimization': self._analyze_partitions,
            'aggregation_optimization': self._analyze_aggregations
        }
    
    def analyze_query(self, query):
        """分析查询并提供优化建议"""
        parsed = sqlparse.parse(query)[0]
        recommendations = []
        
        for rule_name, rule_func in self.optimization_rules.items():
            suggestions = rule_func(parsed, query)
            if suggestions:
                recommendations.extend(suggestions)
        
        return recommendations
    
    def _analyze_joins(self, parsed, query):
        """分析JOIN操作"""
        suggestions = []
        
        # 检查JOIN顺序
        join_pattern = re.findall(r'JOIN\s+(\w+)', query, re.IGNORECASE)
        if len(join_pattern) > 2:
            suggestions.append({
                'type': 'join_optimization',
                'severity': 'medium',
                'message': 'Consider reordering JOINs with smaller tables first',
                'recommendation': 'Place smaller tables on the right side of JOIN operations'
            })
        
        # 检查JOIN条件
        if 'ON ' not in query.upper() and 'WHERE ' in query.upper():
            suggestions.append({
                'type': 'join_optimization',
                'severity': 'high',
                'message': 'Possible Cartesian product detected',
                'recommendation': 'Ensure all JOINs have proper ON conditions'
            })
        
        return suggestions
    
    def _analyze_predicates(self, parsed, query):
        """分析谓词下推"""
        suggestions = []
        
        # 检查WHERE子句位置
        if re.search(r'SELECT.*FROM.*JOIN.*WHERE', query, re.IGNORECASE | re.DOTALL):
            where_conditions = re.findall(r'WHERE\s+(.+?)(?:GROUP|ORDER|LIMIT|$)', query, re.IGNORECASE | re.DOTALL)
            if where_conditions:
                suggestions.append({
                    'type': 'predicate_pushdown',
                    'severity': 'medium',
                    'message': 'WHERE conditions could be pushed down to individual tables',
                    'recommendation': 'Move table-specific filters to subqueries or use explicit filtering'
                })
        
        return suggestions
    
    def _analyze_columns(self, parsed, query):
        """分析列剪枝"""
        suggestions = []
        
        # 检查SELECT *
        if re.search(r'SELECT\s+\*', query, re.IGNORECASE):
            suggestions.append({
                'type': 'column_pruning',
                'severity': 'medium',
                'message': 'Using SELECT * may read unnecessary columns',
                'recommendation': 'Specify only the columns you need'
            })
        
        return suggestions
    
    def _analyze_partitions(self, parsed, query):
        """分析分区优化"""
        suggestions = []
        
        # 检查日期过滤
        date_columns = ['date', 'created_at', 'updated_at', 'timestamp']
        has_date_filter = any(col in query.lower() for col in date_columns)
        
        if not has_date_filter and ('FROM' in query.upper()):
            suggestions.append({
                'type': 'partition_optimization',
                'severity': 'high',
                'message': 'No date-based filtering detected',
                'recommendation': 'Add date range filters to leverage partitioning'
            })
        
        return suggestions
    
    def _analyze_aggregations(self, parsed, query):
        """分析聚合优化"""
        suggestions = []
        
        # 检查DISTINCT
        distinct_count = len(re.findall(r'DISTINCT', query, re.IGNORECASE))
        if distinct_count > 1:
            suggestions.append({
                'type': 'aggregation_optimization',
                'severity': 'medium',
                'message': 'Multiple DISTINCT operations detected',
                'recommendation': 'Consider using approximation functions like approx_distinct()'
            })
        
        # 检查GROUP BY
        if 'GROUP BY' in query.upper() and 'ORDER BY' in query.upper():
            suggestions.append({
                'type': 'aggregation_optimization',
                'severity': 'low',
                'message': 'Ordering after grouping may be expensive',
                'recommendation': 'Consider if ordering is necessary or can be done client-side'
            })
        
        return suggestions
    
    def generate_optimized_query(self, original_query, recommendations):
        """基于建议生成优化后的查询"""
        optimized_query = original_query
        
        for rec in recommendations:
            if rec['type'] == 'column_pruning' and 'SELECT *' in optimized_query:
                # 这里需要更复杂的逻辑来确定需要的列
                optimized_query = optimized_query.replace('SELECT *', 'SELECT /* specify needed columns */')
            
            elif rec['type'] == 'aggregation_optimization' and 'DISTINCT' in optimized_query:
                optimized_query = re.sub(r'COUNT\s*\(\s*DISTINCT', 'APPROX_DISTINCT(', optimized_query, flags=re.IGNORECASE)
        
        return optimized_query

# 使用示例
def optimize_query_example():
    optimizer = PrestoQueryOptimizer()
    
    query = """
    SELECT *
    FROM large_table t1
    JOIN medium_table t2 ON t1.id = t2.id
    JOIN small_table t3 ON t2.ref_id = t3.id
    WHERE t1.status = 'active'
    GROUP BY t1.category
    ORDER BY COUNT(DISTINCT t1.user_id) DESC
    """
    
    recommendations = optimizer.analyze_query(query)
    
    print("Query Optimization Analysis:")
    print("=" * 50)
    
    for i, rec in enumerate(recommendations, 1):
        print(f"{i}. {rec['type'].upper()} ({rec['severity']})")
        print(f"   Issue: {rec['message']}")
        print(f"   Recommendation: {rec['recommendation']}")
        print()
    
    optimized = optimizer.generate_optimized_query(query, recommendations)
    print("Optimized Query:")
    print("-" * 20)
    print(optimized)

if __name__ == "__main__":
    optimize_query_example()

💾 内存诊断与调优

🧠 内存使用分析

-- 内存使用诊断视图
CREATE VIEW catalog.schema.memory_usage_diagnosis AS
WITH memory_stats AS (
    SELECT 
        node_id,
        pool_id,
        max_bytes,
        reserved_bytes,
        reserved_revocable_bytes,
        query_count,
        -- 计算内存使用率
        CAST(reserved_bytes AS DOUBLE) / CAST(max_bytes AS DOUBLE) AS usage_ratio,
        -- 计算可撤销内存比例
        CAST(reserved_revocable_bytes AS DOUBLE) / NULLIF(CAST(reserved_bytes AS DOUBLE), 0.0) AS revocable_ratio
    FROM system.runtime.memory_pools
),
memory_pressure AS (
    SELECT 
        *,
        CASE 
            WHEN usage_ratio > 0.9 THEN 'critical'
            WHEN usage_ratio > 0.7 THEN 'high'
            WHEN usage_ratio > 0.5 THEN 'medium'
            ELSE 'low'
        END AS pressure_level,
        CASE 
            WHEN query_count > 100 THEN 'high_concurrency'
            WHEN query_count > 50 THEN 'medium_concurrency'
            ELSE 'low_concurrency'
        END AS concurrency_level
    FROM memory_stats
),
recommendations AS (
    SELECT 
        *,
        CASE 
            WHEN pressure_level = 'critical' THEN 
                'URGENT: Increase memory allocation or reduce concurrent queries'
            WHEN pressure_level = 'high' AND concurrency_level = 'high_concurrency' THEN 
                'Consider increasing memory or implementing query queuing'
            WHEN revocable_ratio < 0.1 THEN 
                'Enable spilling to disk for better memory management'
            ELSE 'Memory usage is acceptable'
        END AS recommendation
    FROM memory_pressure
)
SELECT * FROM recommendations;

🔄 内存泄漏检测

#!/bin/bash
# 内存泄漏检测脚本

PRESTO_HOME="/opt/presto"
MONITORING_INTERVAL=60  # 秒
ALERT_THRESHOLD=90      # 内存使用百分比

detect_memory_leaks() {
    local output_file="/tmp/memory_leak_detection_$(date +%Y%m%d_%H%M%S).log"
    
    echo "Starting memory leak detection at $(date)" | tee $output_file
    echo "Monitoring interval: ${MONITORING_INTERVAL}s" | tee -a $output_file
    echo "Alert threshold: ${ALERT_THRESHOLD}%" | tee -a $output_file
    echo "=========================================" | tee -a $output_file
    
    local previous_memory=0
    local consecutive_increases=0
    local max_consecutive=5
    
    while true; do
        # 获取Presto进程内存使用
        local presto_pid=$(pgrep -f "presto-server")
        if [ -z "$presto_pid" ]; then
            echo "$(date): Presto process not found" | tee -a $output_file
            sleep $MONITORING_INTERVAL
            continue
        fi
        
        # 获取内存使用情况
        local memory_info=$(ps -p $presto_pid -o pid,ppid,pmem,rss,vsz --no-headers)
        local memory_percent=$(echo $memory_info | awk '{print $3}')
        local memory_rss=$(echo $memory_info | awk '{print $4}')
        
        echo "$(date): PID=$presto_pid, Memory=$memory_percent%, RSS=${memory_rss}KB" | tee -a $output_file
        
        # 检查内存是否持续增长
        if (( $(echo "$memory_rss > $previous_memory" | bc -l) )); then
            consecutive_increases=$((consecutive_increases + 1))
        else
            consecutive_increases=0
        fi
        
        # 内存泄漏告警
        if [ $consecutive_increases -ge $max_consecutive ]; then
            echo "$(date): MEMORY GROWTH ALERT - Memory has increased for $consecutive_increases consecutive intervals; this alone does not establish a memory leak" | tee -a $output_file
            send_alert "Memory Growth" "Presto memory usage has increased consecutively for $consecutive_increases intervals; investigate workload, GC and retained objects before concluding a leak"
            consecutive_increases=0
        fi
        
        # 高内存使用告警
        if (( $(echo "$memory_percent > $ALERT_THRESHOLD" | bc -l) )); then
            echo "$(date): HIGH MEMORY ALERT - Memory usage: $memory_percent%" | tee -a $output_file
            send_alert "High Memory Usage" "Presto memory usage is at $memory_percent%"
        fi
        
        previous_memory=$memory_rss
        sleep $MONITORING_INTERVAL
    done
}

send_alert() {
    local alert_type="$1"
    local message="$2"
    
    # 发送到监控系统 (示例)
    curl -X POST http://monitoring-system/alerts \
         -H "Content-Type: application/json" \
         -d "{
             \"type\": \"$alert_type\",
             \"message\": \"$message\",
             \"timestamp\": \"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",
             \"service\": \"presto\"
         }" 2>/dev/null || echo "Failed to send alert: $alert_type"
}

# Java堆内存分析
analyze_heap_dump() {
    local heap_dump_file="$1"
    local analysis_output="/tmp/heap_analysis_$(date +%Y%m%d_%H%M%S).txt"
    
    echo "Analyzing heap dump: $heap_dump_file" | tee $analysis_output
    
    # 使用jhat分析堆转储 (需要安装JDK)
    if command -v jhat >/dev/null 2>&1; then
        echo "Generating heap analysis..." | tee -a $analysis_output
        jhat -J-Xmx4g $heap_dump_file > /dev/null 2>&1 &
        local jhat_pid=$!
        
        sleep 10
        
        # 获取对象统计
        curl -s "http://localhost:7000/histo/" | \
        head -50 >> $analysis_output
        
        kill $jhat_pid 2>/dev/null
    else
        echo "jhat not available. Install JDK for heap analysis." | tee -a $analysis_output
    fi
    
    echo "Heap analysis saved to: $analysis_output"
}

# 自动清理内存
cleanup_memory() {
    echo "Attempting to cleanup memory..."
    
    # 触发JVM垃圾回收
    local presto_pid=$(pgrep -f "presto-server")
    if [ ! -z "$presto_pid" ]; then
        echo "Automatic GC action skipped: SIGUSR1 is not a portable JVM garbage collection interface"
    fi
    
    # 系统级内存清理
    sync
    echo 1 > /proc/sys/vm/drop_caches
    echo "Cleared system caches"
}

# 主函数
main() {
    case "$1" in
        "detect")
            detect_memory_leaks
            ;;
        "analyze")
            if [ -z "$2" ]; then
                echo "Usage: $0 analyze <heap_dump_file>"
                exit 1
            fi
            analyze_heap_dump "$2"
            ;;
        "cleanup")
            cleanup_memory
            ;;
        *)
            echo "Usage: $0 {detect|analyze <heap_file>|cleanup}"
            exit 1
            ;;
    esac
}

main "$@"

🌐 网络诊断与调优

📡 网络连接诊断

#!/usr/bin/env python3
# 网络诊断工具

import socket
import time
import subprocess
import threading
from concurrent.futures import ThreadPoolExecutor, as_completed

class PrestoNetworkDiagnostic:
    def __init__(self, coordinator_host, coordinator_port=8080):
        self.coordinator_host = coordinator_host
        self.coordinator_port = coordinator_port
        self.worker_nodes = []
        
    def discover_workers(self):
        """发现工作节点"""
        try:
            # 通过REST API获取节点信息
            import requests
            response = requests.get(f"http://{self.coordinator_host}:{self.coordinator_port}/v1/node")
            nodes = response.json()
            
            self.worker_nodes = [
                {'host': node['host'], 'port': node['httpUri'].split(':')[-1]}
                for node in nodes if not node.get('coordinator', False)
            ]
            
            print(f"Discovered {len(self.worker_nodes)} worker nodes")
            return self.worker_nodes
            
        except Exception as e:
            print(f"Error discovering workers: {e}")
            return []
    
    def test_connectivity(self, host, port, timeout=5):
        """测试连接性"""
        try:
            start_time = time.time()
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.settimeout(timeout)
            result = sock.connect_ex((host, int(port)))
            end_time = time.time()
            sock.close()
            
            return {
                'host': host,
                'port': port,
                'status': 'connected' if result == 0 else 'failed',
                'latency': round((end_time - start_time) * 1000, 2),  # ms
                'error': None if result == 0 else f"Connection failed: {result}"
            }
        except Exception as e:
            return {
                'host': host,
                'port': port,
                'status': 'error',
                'latency': None,
                'error': str(e)
            }
    
    def test_bandwidth(self, host, port, duration=10):
        """测试带宽"""
        try:
            # 简单的带宽测试 - 实际实现会更复杂
            start_time = time.time()
            sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            sock.connect((host, int(port)))
            
            # 发送测试数据
            test_data = b'x' * 1024  # 1KB
            bytes_sent = 0
            
            while time.time() - start_time < duration:
                sock.send(test_data)
                bytes_sent += len(test_data)
            
            sock.close()
            elapsed = time.time() - start_time
            bandwidth_mbps = (bytes_sent * 8) / (elapsed * 1024 * 1024)  # Mbps
            
            return {
                'host': host,
                'port': port,
                'bandwidth_mbps': round(bandwidth_mbps, 2),
                'bytes_sent': bytes_sent,
                'duration': round(elapsed, 2)
            }
            
        except Exception as e:
            return {
                'host': host,
                'port': port,
                'bandwidth_mbps': 0,
                'error': str(e)
            }
    
    def ping_test(self, host, count=4):
        """Ping测试"""
        try:
            result = subprocess.run(
                ['ping', '-c', str(count), host],
                capture_output=True,
                text=True,
                timeout=30
            )
            
            output = result.stdout
            
            # 解析ping结果
            packet_loss = 0
            avg_latency = 0
            
            for line in output.split('\n'):
                if 'packet loss' in line:
                    packet_loss = float(line.split('%')[0].split()[-1])
                elif 'min/avg/max' in line:
                    avg_latency = float(line.split('/')[4])
            
            return {
                'host': host,
                'packet_loss': packet_loss,
                'avg_latency': avg_latency,
                'status': 'success' if result.returncode == 0 else 'failed'
            }
            
        except Exception as e:
            return {
                'host': host,
                'packet_loss': 100,
                'avg_latency': 0,
                'status': 'error',
                'error': str(e)
            }
    
    def comprehensive_network_test(self):
        """综合网络测试"""
        print("Starting comprehensive network diagnostic...")
        
        # 发现节点
        if not self.worker_nodes:
            self.discover_workers()
        
        all_nodes = [{'host': self.coordinator_host, 'port': self.coordinator_port}] + self.worker_nodes
        
        results = {
            'connectivity': [],
            'ping': [],
            'bandwidth': []
        }
        
        # 并发测试连接性
        with ThreadPoolExecutor(max_workers=10) as executor:
            # 连接性测试
            connectivity_futures = {
                executor.submit(self.test_connectivity, node['host'], node['port']): node
                for node in all_nodes
            }
            
            for future in as_completed(connectivity_futures):
                result = future.result()
                results['connectivity'].append(result)
            
            # Ping测试
            ping_futures = {
                executor.submit(self.ping_test, node['host']): node
                for node in all_nodes
            }
            
            for future in as_completed(ping_futures):
                result = future.result()
                results['ping'].append(result)
        
        return results
    
    def generate_network_report(self, results):
        """生成网络诊断报告"""
        report = []
        report.append("=" * 60)
        report.append("PRESTO NETWORK DIAGNOSTIC REPORT")
        report.append("=" * 60)
        report.append(f"Generated at: {time.strftime('%Y-%m-%d %H:%M:%S')}")
        report.append("")
        
        # 连接性报告
        report.append("CONNECTIVITY TEST RESULTS:")
        report.append("-" * 30)
        failed_connections = []
        slow_connections = []
        
        for conn in results['connectivity']:
            status_emoji = "✅" if conn['status'] == 'connected' else "❌"
            latency_info = f"{conn['latency']}ms" if conn['latency'] else "N/A"
            report.append(f"{status_emoji} {conn['host']}:{conn['port']} - {latency_info}")
            
            if conn['status'] != 'connected':
                failed_connections.append(conn)
            elif conn['latency'] and conn['latency'] > 100:
                slow_connections.append(conn)
        
        # Ping测试报告
        report.append("")
        report.append("PING TEST RESULTS:")
        report.append("-" * 30)
        high_latency_nodes = []
        packet_loss_nodes = []
        
        for ping in results['ping']:
            status_emoji = "✅" if ping['status'] == 'success' else "❌"
            report.append(f"{status_emoji} {ping['host']} - {ping['avg_latency']}ms avg, {ping['packet_loss']}% loss")
            
            if ping['avg_latency'] > 50:
                high_latency_nodes.append(ping)
            if ping['packet_loss'] > 0:
                packet_loss_nodes.append(ping)
        
        # 问题汇总
        report.append("")
        report.append("ISSUES DETECTED:")
        report.append("-" * 30)
        
        if failed_connections:
            report.append(f"❌ {len(failed_connections)} connection failures detected")
        
        if slow_connections:
            report.append(f"⚠️  {len(slow_connections)} slow connections detected (>100ms)")
        
        if high_latency_nodes:
            report.append(f"⚠️  {len(high_latency_nodes)} high latency nodes detected (>50ms)")
        
        if packet_loss_nodes:
            report.append(f"❌ {len(packet_loss_nodes)} nodes with packet loss detected")
        
        if not (failed_connections or slow_connections or high_latency_nodes or packet_loss_nodes):
            report.append("✅ No issues detected by the performed TCP connectivity and Ping checks; bandwidth and overall network health remain unassessed")
        
        # 建议
        report.append("")
        report.append("RECOMMENDATIONS:")
        report.append("-" * 30)
        
        if failed_connections:
            report.append("• Check firewall rules and network configuration")
            report.append("• Verify Presto services are running on failed nodes")
        
        if slow_connections or high_latency_nodes:
            report.append("• Consider network topology optimization")
            report.append("• Check for network congestion")
            
        if packet_loss_nodes:
            report.append("• Investigate network infrastructure issues")
            report.append("• Check network hardware and cables")
        
        return "\n".join(report)

# 使用示例
def main():
    diagnostic = PrestoNetworkDiagnostic('presto-coordinator.example.com')
    results = diagnostic.comprehensive_network_test()
    report = diagnostic.generate_network_report(results)
    
    print(report)
    
    # 保存报告
    with open(f'/tmp/presto_network_report_{int(time.time())}.txt', 'w') as f:
        f.write(report)

if __name__ == "__main__":
    main()

🚨 故障响应自动化

🤖 自动故障修复

#!/bin/bash
# Presto自动故障修复脚本

PRESTO_HOME="/opt/presto"
PRESTO_CONFIG="$PRESTO_HOME/etc"
PRESTO_LOGS="$PRESTO_HOME/var/log"
RESTART_THRESHOLD=3
MEMORY_THRESHOLD=90

log_message() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a /var/log/presto-auto-repair.log
}

check_presto_health() {
    local coordinator_url="http://localhost:8080"
    
    # 检查HTTP响应
    if ! curl -f -s "$coordinator_url/v1/cluster" >/dev/null 2>&1; then
        log_message "ERROR: Presto coordinator is not responding"
        return 1
    fi
    
    # 检查集群状态
    local active_workers=$(curl -s "$coordinator_url/v1/node" | jq -r '.[] | select(.coordinator==false) | .nodeId' | wc -l)
    local total_workers=$(curl -s "$coordinator_url/v1/node" | jq -r '.[] | select(.coordinator==false) | .nodeId' | wc -l)
    
    if [ "$active_workers" -lt "$((total_workers / 2))" ]; then
        log_message "WARNING: Less than 50% of workers are active ($active_workers/$total_workers)"
        return 2
    fi
    
    log_message "INFO: Presto cluster health check passed"
    return 0
}

fix_memory_issues() {
    log_message "INFO: Attempting to fix memory issues"
    
    # 检查内存使用
    local memory_usage=$(free | grep Mem | awk '{printf "%.0f", $3/$2 * 100.0}')
    
    if [ "$memory_usage" -gt "$MEMORY_THRESHOLD" ]; then
        log_message "WARNING: High memory usage detected: ${memory_usage}%"
        
        # 清理系统缓存
        sync
        echo 1 > /proc/sys/vm/drop_caches
        log_message "INFO: System caches cleared"
        
        # 触发JVM垃圾回收
        local presto_pid=$(pgrep -f "presto-server")
        if [ ! -z "$presto_pid" ]; then
            log_message "WARNING: Automatic GC action skipped; SIGUSR1 is not a portable JVM garbage collection interface"
        fi
        
        # 等待一段时间检查改善情况
        sleep 30
        local new_memory_usage=$(free | grep Mem | awk '{printf "%.0f", $3/$2 * 100.0}')
        
        if [ "$new_memory_usage" -lt "$memory_usage" ]; then
            log_message "INFO: Memory usage improved from ${memory_usage}% to ${new_memory_usage}%"
            return 0
        else
            log_message "WARNING: Memory cleanup did not improve usage significantly"
            return 1
        fi
    fi
    
    return 0
}

fix_connection_issues() {
    log_message "INFO: Attempting to fix connection issues"
    
    # 检查端口监听
    if ! netstat -tulpn | grep -q ":8080.*LISTEN"; then
        log_message "ERROR: Presto coordinator port 8080 is not listening"
        restart_presto_service
        return $?
    fi
    
    # 检查网络连接
    if ! ping -c 1 -W 5 localhost >/dev/null 2>&1; then
        log_message "ERROR: Network connectivity issues detected"
        # 这里可以添加网络修复逻辑
        return 1
    fi
    
    return 0
}

fix_query_failures() {
    log_message "INFO: Checking for query failures"
    
    # 分析最近的查询失败
    local failed_queries=$(curl -s "http://localhost:8080/v1/query" | jq -r '.[] | select(.state=="FAILED") | .queryId' | head -10)
    
    if [ ! -z "$failed_queries" ]; then
        log_message "WARNING: Recent query failures detected"
        
        # 检查常见失败原因
        for query_id in $failed_queries; do
            local error_info=$(curl -s "http://localhost:8080/v1/query/$query_id" | jq -r '.errorCode.name // "UNKNOWN"')
            log_message "INFO: Query $query_id failed with error: $error_info"
            
            case "$error_info" in
                "EXCEEDED_MEMORY_LIMIT")
                    fix_memory_issues
                    ;;
                "TIMEOUT")
                    # 可以调整超时配置
                    log_message "INFO: Query timeout detected, consider adjusting timeout settings"
                    ;;
                "CONNECTOR_ERROR")
                    # 重启连接器相关服务
                    log_message "INFO: Connector error detected, may need service restart"
                    ;;
            esac
        done
    fi
    
    return 0
}

restart_presto_service() {
    log_message "INFO: Restarting Presto service"
    
    # 优雅关闭
    if pgrep -f "presto-server" >/dev/null; then
        log_message "INFO: Stopping Presto service gracefully"
        pkill -TERM -f "presto-server"
        
        # 等待进程结束
        local wait_count=0
        while [ $wait_count -lt 30 ] && pgrep -f "presto-server" >/dev/null; do
            sleep 1
            wait_count=$((wait_count + 1))
        done
        
        # 强制终止如果还在运行
        if pgrep -f "presto-server" >/dev/null; then
            log_message "WARNING: Force killing Presto process"
            pkill -KILL -f "presto-server"
        fi
    fi
    
    # 启动服务
    log_message "INFO: Starting Presto service"
    cd "$PRESTO_HOME"
    nohup bin/launcher run >/dev/null 2>&1 &
    
    # 等待服务启动
    local startup_wait=0
    while [ $startup_wait -lt 60 ]; do
        if curl -f -s "http://localhost:8080/v1/cluster" >/dev/null 2>&1; then
            log_message "INFO: Presto service started successfully"
            return 0
        fi
        sleep 5
        startup_wait=$((startup_wait + 5))
    done
    
    log_message "ERROR: Presto service failed to start within 60 seconds"
    return 1
}

emergency_procedures() {
    log_message "CRITICAL: Executing emergency procedures"
    
    # 备份当前配置
    local backup_dir="/tmp/presto_emergency_backup_$(date +%Y%m%d_%H%M%S)"
    mkdir -p "$backup_dir"
    cp -r "$PRESTO_CONFIG" "$backup_dir/"
    cp -r "$PRESTO_LOGS" "$backup_dir/"
    log_message "INFO: Configuration and logs backed up to $backup_dir"
    
    # 重置到安全配置
    if [ -f "$PRESTO_CONFIG/config.properties.safe" ]; then
        cp "$PRESTO_CONFIG/config.properties.safe" "$PRESTO_CONFIG/config.properties"
        log_message "INFO: Restored safe configuration"
    fi
    
    # 清理临时文件
    find "$PRESTO_HOME/var" -name "*.tmp" -delete
    log_message "INFO: Cleaned temporary files"
    
    # 强制重启
    restart_presto_service
}

auto_repair_main() {
    log_message "INFO: Starting Presto auto-repair check"
    
    local health_status
    check_presto_health
    health_status=$?
    
    case $health_status in
        0)
            log_message "INFO: Presto cluster is healthy"
            ;;
        1)
            log_message "WARNING: Presto coordinator issues detected"
            fix_connection_issues
            fix_memory_issues
            ;;
        2)
            log_message "WARNING: Worker node issues detected"
            # 这里可以添加工作节点修复逻辑
            ;;
    esac
    
    # 检查查询失败
    fix_query_failures
    
    # 再次检查健康状态
    check_presto_health
    health_status=$?
    
    if [ $health_status -ne 0 ]; then
        log_message "WARNING: Auto-repair did not resolve all issues"
        
        # 检查是否需要紧急处理
        local failure_count=$(cat /var/log/presto-auto-repair.log | grep "ERROR\|CRITICAL" | tail -10 | wc -l)
        
        if [ "$failure_count" -gt "$RESTART_THRESHOLD" ]; then
            emergency_procedures
        fi
    fi
    
    log_message "INFO: Auto-repair check completed"
}

# Cron job模式
if [ "$1" = "--cron" ]; then
    auto_repair_main
elif [ "$1" = "--emergency" ]; then
    emergency_procedures
else
    echo "Usage: $0 [--cron|--emergency]"
    echo "  --cron      Run in cron job mode"
    echo "  --emergency Run emergency procedures"
fi

📚 学习连接

🎯 本节核心技能

  • 日志分析:掌握Presto日志的解读和分析技巧
  • 性能诊断:识别和解决查询性能问题
  • 内存管理:诊断和解决内存相关问题
  • 网络诊断:网络连接和带宽问题排查
  • 自动化运维:实现故障自动检测和修复

🔗 下一步学习

诊断技能检查:

  • ✅ 能够快速定位Presto集群问题?
  • ✅ 掌握查询性能优化方法?
  • ✅ 理解内存和网络诊断技巧?

学习路径完成:

  • 企业实践:企业级应用实践 - 综合应用案例
  • 高级优化:深入性能调优和架构设计
  • 运维自动化:构建完整的运维体系

tip 💡 故障诊断掌握要点

🔧 诊断精髓:

  • 📊 系统观察 - 多维度监控,快速问题定位
  • 🔍 根因分析 - 深入日志分析,找到问题本质
  • ⚡ 快速响应 - 自动化修复,减少服务中断
  • 🛡️ 预防为主 - 监控告警,防患于未然

🚀 实践应用:故障诊断技能掌握后,进入 企业级应用实践 学习完整的企业应用方案!