PostgreSQL笔记8: 基于二八法则的基础性能调优

纲要

  • PostgreSQL 基础性能调优的核心原则:帕累托法则(二八法则)
  • 调优辅助工具:pgtunepg_configurator
  • 内存相关参数调优
    • shared_buffers ------ 共享内存缓冲区
    • work_mem ------ 每个后端进程的工作内存
    • maintenance_work_mem ------ 维护操作专用内存
    • effective_cache_size ------ 优化器缓存估算(非实际分配)
    • temp_buffers ------ 临时表缓冲区
  • 检查点(Checkpoint)相关参数调优
    • checkpoint_timeout
    • checkpoint_completion_target
    • max_wal_size / min_wal_size
  • 自动清理(Autovacuum)参数调优
    • autovacuum_max_workers
    • autovacuum_naptime
    • autovacuum_vacuum_cost_limit / autovacuum_vacuum_cost_delay
  • 查询规划器成本常量调优
    • random_page_cost
    • seq_page_cost
  • 配置文件生效规则:以最后一次出现的参数值为准

二八法则:性能调优的核心指导思想

PostgreSQL 的默认配置面向兼容性和通用场景设计,并未针对服务器的具体硬件资源(CPU 核心数、内存大小、磁盘类型)做针对性优化。因此,数据库安装完成后,首要任务便是根据实际硬件配置与业务负载特征,对关键参数进行基础调整。

在调优实践中,帕累托法则(Pareto Principle,即二八法则) 是一个极为实用的指导原则:80% 的性能收益往往来自于对 20% 的关键参数进行调整。PostgreSQL 拥有数百个配置参数,但在绝大多数业务系统的初期阶段,只需将内存、检查点、自动清理和 I/O 成本这几类核心参数调整到位,即可获得显著的性能提升。无需一开始便深入研究全部参数,也无需盲目照搬他人的配置文件。

调优辅助工具:pgtune 与 pg_configurator

在手动调整参数之前,可借助自动化工具生成一份基于硬件规格的基础配置,再根据实际业务场景进行微调。

pgtune

pgtune 是一款经典的 PostgreSQL 配置向导工具。它根据用户输入的硬件信息(CPU 数量、内存大小、磁盘类型等)和预期工作负载类型(OLTP、数据仓库、混合负载等),自动计算并生成推荐的 postgresql.conf 配置参数。

bash 复制代码
# pgtune 命令行示例(以 pgtune 的 Python 实现为例)
pgtune -i postgresql.conf -o postgresql-new.conf

pgtune 也提供在线 Web 版本,用户可通过表单填写服务器规格后一键生成配置。生成后的参数可直接追加到 postgresql.conf 文件末尾,或通过 ALTER SYSTEM 命令设置。

pg_configurator

pg_configurator 是一个更为现代化的图形化配置工具。与 pgtune 相比,它的可配置项更加丰富,支持设置日志回收策略、数据可靠性级别、备库数量等高级选项。其核心原理与 pgtune 一致:根据输入的硬件与业务参数生成优化后的配置参数。

建议:先使用上述工具生成一份基础配置,再结合实际监控数据与业务特点进行针对性微调。

内存相关参数调优

PostgreSQL 采用多进程架构,进程间通信依赖共享内存。以下是几个最核心的内存参数。

shared_buffers

shared_buffers 是 PostgreSQL 的共享内存缓冲区,用于缓存数据页(表数据和索引数据)。所有增删改查操作均在 shared_buffers 池中进行。

  • 默认值:128MB(通常过小)
  • 推荐设置 :服务器物理内存的 25%
    • 内存 < 2GB:设为总内存的 20%
    • 内存 < 32GB:设为总内存的 25%
    • 内存 > 32GB:可设为 8GB 左右

配置

postgresql 复制代码
# postgresql.conf
shared_buffers = 4GB      # 假设服务器内存为 16GB

为何不建议设为 80%~90%?PostgreSQL 采用独特的双缓存架构 :数据库自身管理 shared_buffers,操作系统同时也有自己的页面缓存(Page Cache)。两者共同作用,需为操作系统缓存预留足够内存。

work_mem

work_mem 是每个后端进程在执行排序(ORDER BYDISTINCT)、哈希连接(Hash Join)等操作时可用的内存量。此为私有内存,每个会话的每个执行节点都可能消耗此内存。

  • 默认值:4MB
  • 风险:设置过大且并发连接数较高时,可能导致内存溢出(OOM)
  • 建议:在会话级别按需设置,而非全局设置过大
sql 复制代码
-- 会话级别设置 work_mem(仅对当前会话生效)
SET work_mem = '64MB';

-- 执行大型排序或哈希操作
EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM large_table ORDER BY large_column;
postgresql 复制代码
# 全局默认值(谨慎设置)
work_mem = '32MB'   # 保守值,建议从 32-64MB 起步

maintenance_work_mem

maintenance_work_mem 专用于维护性操作的内存,包括 VACUUMCREATE INDEXREINDEXALTER TABLE ADD FOREIGN KEY 等。

  • 默认值:64MB
  • 推荐设置:每 1GB 内存配置 50MB,即 16GB 内存可设为 800MB
  • 上限:一般不超过 1GB~2GB

配置

postgresql 复制代码
# postgresql.conf
maintenance_work_mem = '1GB'

effective_cache_size

effective_cache_size 用于告知查询优化器操作系统和数据库可用的磁盘缓存总量。此参数并非实际分配内存,仅影响优化器的执行计划选择。

  • 推荐设置 :服务器总内存的 50%~75%

    postgresql 复制代码
    # postgresql.conf
    effective_cache_size = '12GB'   # 假设服务器内存为 16GB

temp_buffers

temp_buffers 用于临时表操作的内存缓冲。每个会话可独立设置。

  • 默认值:8MB

    sql 复制代码
    -- 会话级别设置
    SET temp_buffers = '64MB';
    CREATE TEMP TABLE temp_data AS SELECT * FROM large_table;

检查点(Checkpoint)参数调优

检查点(Checkpoint)是将内存中的脏页(Dirty Pages)刷写到磁盘的操作,用于保证数据一致性。检查点过于频繁会增加 I/O 压力;间隔过长则会延长崩溃恢复时间。

checkpoint_timeout

两次自动检查点之间的最大时间间隔。

  • 默认值:5 分钟(300 秒)

  • 取值范围:30 秒 ~ 1 小时

  • 推荐设置15~30 分钟

    postgresql 复制代码
    # postgresql.conf
    checkpoint_timeout = '15min'

checkpoint_completion_target

检查点完成的目标比例,即在下一次检查点开始前,本次检查点应完成的比例。

  • 默认值:0.5(50%)

  • 取值范围:0.0 ~ 1.0

  • 推荐设置0.9(让写入更平滑,避免 I/O 尖峰)

    postgresql 复制代码
    # postgresql.conf
    checkpoint_completion_target = 0.9

max_wal_size / min_wal_size

控制 WAL(Write-Ahead Log)日志总量的上下限。当 WAL 总量达到 max_wal_size 时会触发检查点。

  • 默认值max_wal_size = 1GBmin_wal_size = 80MB

  • 推荐设置:根据实际业务写入量调整,通常可适当增大

    postgresql 复制代码
    # postgresql.conf
    max_wal_size = '4GB'
    min_wal_size = '1GB'

自动清理(Autovacuum)参数调优

PostgreSQL 采用 MVCC(多版本并发控制)机制,更新和删除操作会产生"过期"元组(Dead Tuples),需通过 VACUUM 进行清理回收。autovacuum 是 PostgreSQL 的自动清理守护进程。

autovacuum_max_workers

同时运行的 autovacuum 工作进程的最大数量。

  • 默认值:3

  • 推荐设置 :根据 CPU 核心数调整,32 核可设为 5~10

    postgresql 复制代码
    # postgresql.conf
    autovacuum_max_workers = 5

autovacuum_naptime

autovacuum 启动器检查各数据库并触发清理任务的时间间隔。

  • 默认值:1 分钟(1min)

  • 推荐设置:通常保持默认即可

    postgresql 复制代码
    # postgresql.conf
    autovacuum_naptime = '1min'

autovacuum_vacuum_cost_limit / autovacuum_vacuum_cost_delay

控制 autovacuum 的 I/O 成本上限与延迟,用于限制清理操作对正常业务的影响。

  • 默认值autovacuum_vacuum_cost_limit = -1(使用 vacuum_cost_limit 的值,默认为 200),autovacuum_vacuum_cost_delay = 2ms
  • 推荐设置
    • SSD 存储:可将 autovacuum_vacuum_cost_limit 设为 1000~2000
    • NVMe SSD:可设为 2000~10000

配置

postgresql 复制代码
# postgresql.conf
autovacuum_vacuum_cost_limit = 2000
autovacuum_vacuum_cost_delay = '2ms'

查询规划器成本常量调优

PostgreSQL 的查询优化器基于成本模型选择执行计划。seq_page_costrandom_page_cost 是其中两个最重要的 I/O 成本常量。

seq_page_cost 与 random_page_cost

  • seq_page_cost :顺序读取一页的成本,默认 1.0
  • random_page_cost :随机读取一页的成本,默认 4.0

两者的比值决定了优化器在顺序扫描(Sequential Scan)与索引扫描(Index Scan)之间的偏好。

postgresql 复制代码
# postgresql.conf(适用于 NVMe SSD 或高性能存储)
random_page_cost = 1.5
seq_page_cost = 1.0

注意 :虽然系统允许将 random_page_cost 设为小于 seq_page_cost,但这在物理上是不合理的。对于全内存缓存的数据库,可考虑将两者设为相等。

调优原理

可将数据库选择执行计划的过程类比为从成都到北京的出行方式选择:

出行方式 时间成本 金钱成本 对应数据库操作
飞机 索引扫描(随机 I/O)
高铁 位图扫描
自驾 顺序扫描(顺序 I/O)

random_page_costseq_page_cost 的比值就是优化器判断"走索引"还是"走全表扫描"的决策依据。对于现代 SSD 或 NVMe 存储,随机 I/O 与顺序 I/O 的性能差距已大幅缩小,因此应适当降低 random_page_cost

配置文件生效规则

PostgreSQL 的 postgresql.conf 配置文件采用以最后一次出现的参数值为准的生效规则。若同一参数在文件中出现多次,仅最后一次生效。

因此,建议将调优后的参数统一追加至文件末尾,并添加注释说明:

postgresql 复制代码
# ============================================
# 以下为 pgtune 生成的调优参数(2026-08-13)
# ============================================
shared_buffers = 4GB
work_mem = 32MB
maintenance_work_mem = 1GB
effective_cache_size = 12GB
checkpoint_timeout = '15min'
checkpoint_completion_target = 0.9
max_wal_size = '4GB'
random_page_cost = 1.5
autovacuum_max_workers = 5
autovacuum_vacuum_cost_limit = 2000

修改配置文件后,需重启 PostgreSQL 或执行 pg_ctl reload 使参数生效(部分参数需重启)。

API 速览

本博客涉及的核心 PostgreSQL 配置参数 API 汇总如下:

参数名 所属类别 类型 默认值 推荐范围 生效方式
shared_buffers 内存 integer 128MB 总内存的 20%~25% 重启
work_mem 内存 integer 4MB 32~64MB(起步) 会话/全局
maintenance_work_mem 内存 integer 64MB 每 1GB 内存 50MB 会话/全局
effective_cache_size 内存 integer 4GB 总内存的 50%~75% 重载
temp_buffers 内存 integer 8MB 视临时表大小而定 会话/全局
checkpoint_timeout 检查点 integer 300s 15~30min 重载
checkpoint_completion_target 检查点 floating 0.5 0.8~0.9 重载
max_wal_size 检查点 integer 1GB 视业务写入量而定 重载
autovacuum_max_workers 自动清理 integer 3 CPU 核数的 1/4~1/3 重启
autovacuum_naptime 自动清理 integer 1min 保持默认 重载
autovacuum_vacuum_cost_limit 自动清理 integer -1 (200) 1000~10000(SSD) 重载
random_page_cost 查询规划 floating 4.0 1.0~2.0(SSD) 重载
seq_page_cost 查询规划 floating 1.0 1.0 重载

参数生效方式说明

  • 重启:需重启 PostgreSQL 服务方可生效
  • 重载 :执行 SELECT pg_reload_conf()pg_ctl reload 即可生效,无需重启

Demo 简单示例

以下 Demo 演示了如何使用 Node.js 和 pg 驱动连接 PostgreSQL,并通过 SHOW 命令查看当前配置参数,以及通过 SET 命令在会话级别调整 work_mem

运行说明

bash 复制代码
# 1. 初始化项目
mkdir pg-tuning-demo && cd pg-tuning-demo
npm init -y

# 2. 安装依赖
npm install pg

# 3. 创建 index.js 文件(内容见下方)

# 4. 运行
node index.js

代码说明

js 复制代码
const { Client } = require('pg');

// PostgreSQL 连接配置
const config = {
  host: 'localhost',
  port: 5432,
  database: 'postgres',
  user: 'postgres',
  password: 'your_password',
};

async function main() {
  const client = new Client(config);
  
  try {
    await client.connect();
    console.log('✅ 已连接到 PostgreSQL');

    // 1. 查看当前关键参数
    const params = [
      'shared_buffers',
      'work_mem',
      'maintenance_work_mem',
      'effective_cache_size',
      'checkpoint_timeout',
      'max_wal_size',
      'random_page_cost',
      'autovacuum_max_workers'
    ];

    console.log('\n📊 当前配置参数:');
    for (const param of params) {
      const res = await client.query(`SHOW ${param};`);
      console.log(`  ${param} = ${res.rows[0][param]}`);
    }

    // 2. 会话级别调整 work_mem
    console.log('\n⚙️  会话级别调整 work_mem...');
    await client.query(`SET work_mem = '64MB';`);
    const res = await client.query(`SHOW work_mem;`);
    console.log(`  work_mem (当前会话) = ${res.rows[0].work_mem}`);

    // 3. 创建测试表并插入数据
    await client.query(`
      DROP TABLE IF EXISTS tuning_demo;
      CREATE TABLE tuning_demo (
        id SERIAL PRIMARY KEY,
        value INTEGER,
        created_at TIMESTAMP DEFAULT NOW()
      );
    `);
    console.log('\n📋 已创建测试表 tuning_demo');

    // 4. 插入 10 万条测试数据
    console.log('📝 插入 100,000 条测试数据...');
    await client.query(`
      INSERT INTO tuning_demo (value)
      SELECT generate_series(1, 100000);
    `);
    console.log('✅ 数据插入完成');

    // 5. 执行排序查询,验证 work_mem 效果
    console.log('\n🔍 执行排序查询 (ORDER BY value DESC)...');
    const explainRes = await client.query(`
      EXPLAIN (ANALYZE, BUFFERS) 
      SELECT * FROM tuning_demo ORDER BY value DESC LIMIT 100;
    `);
    console.log('\n📈 执行计划:');
    explainRes.rows.forEach(row => {
      console.log(`  ${row['QUERY PLAN']}`);
    });

  } catch (err) {
    console.error('❌ 错误:', err.message);
  } finally {
    await client.end();
    console.log('\n🔌 连接已关闭');
  }
}

main();

对应的 PostgreSQL 原生指令

sql 复制代码
-- 查看当前配置参数
SHOW shared_buffers;
SHOW work_mem;
SHOW maintenance_work_mem;
SHOW effective_cache_size;
SHOW checkpoint_timeout;
SHOW max_wal_size;
SHOW random_page_cost;
SHOW autovacuum_max_workers;

-- 会话级别设置 work_mem
SET work_mem = '64MB';

-- 创建测试表
DROP TABLE IF EXISTS tuning_demo;
CREATE TABLE tuning_demo (
    id SERIAL PRIMARY KEY,
    value INTEGER,
    created_at TIMESTAMP DEFAULT NOW()
);

-- 插入测试数据
INSERT INTO tuning_demo (value)
SELECT generate_series(1, 100000);

-- 查看执行计划
EXPLAIN (ANALYZE, BUFFERS) 
SELECT * FROM tuning_demo ORDER BY value DESC LIMIT 100;

技术点总结

  1. pg 驱动连接 :使用 Node.js 的 pg 库连接 PostgreSQL 数据库
  2. SHOW 命令:查看当前生效的配置参数值
  3. SET 命令 :在会话级别动态调整 work_mem,无需重启数据库
  4. EXPLAIN (ANALYZE, BUFFERS):分析查询执行计划,观察内存与 I/O 使用情况
  5. 参数分层管理 :全局参数(postgresql.conf)与会话级参数(SET)的优先级关系

多语言 Demo 示例

以下分别提供 Go、Python、Java 三种语言的完整可运行示例,功能与前述 Node.js 版本一致:连接 PostgreSQL,查看关键配置参数,会话级调整 work_mem,创建测试表并插入 10 万条数据,最后执行带 EXPLAIN (ANALYZE, BUFFERS) 的查询并输出执行计划。

Go 语言示例

使用 lib/pqpgx 驱动,此处采用 pgx/v5(推荐)。

运行说明
bash 复制代码
# 初始化模块
go mod init pg-tuning-demo
go get github.com/jackc/pgx/v5

# 运行
go run main.go
代码 (main.go)
go 复制代码
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	"github.com/jackc/pgx/v5"
)

func main() {
	ctx := context.Background()

	// 连接配置
	connStr := "postgres://postgres:your_password@localhost:5432/postgres"
	conn, err := pgx.Connect(ctx, connStr)
	if err != nil {
		log.Fatalf("无法连接数据库: %v", err)
	}
	defer conn.Close(ctx)

	fmt.Println("✅ 已连接到 PostgreSQL")

	// 1. 查看当前关键参数
	params := []string{
		"shared_buffers",
		"work_mem",
		"maintenance_work_mem",
		"effective_cache_size",
		"checkpoint_timeout",
		"max_wal_size",
		"random_page_cost",
		"autovacuum_max_workers",
	}

	fmt.Println("\n📊 当前配置参数:")
	for _, p := range params {
		var val string
		err = conn.QueryRow(ctx, fmt.Sprintf("SHOW %s;", p)).Scan(&val)
		if err != nil {
			log.Printf("获取参数 %s 失败: %v", p, err)
			continue
		}
		fmt.Printf("  %s = %s\n", p, val)
	}

	// 2. 会话级别调整 work_mem
	fmt.Println("\n⚙️  会话级别调整 work_mem...")
	_, err = conn.Exec(ctx, "SET work_mem = '64MB';")
	if err != nil {
		log.Fatalf("设置 work_mem 失败: %v", err)
	}
	var workMem string
	err = conn.QueryRow(ctx, "SHOW work_mem;").Scan(&workMem)
	if err == nil {
		fmt.Printf("  work_mem (当前会话) = %s\n", workMem)
	}

	// 3. 创建测试表
	fmt.Println("\n📋 创建测试表 tuning_demo...")
	_, err = conn.Exec(ctx, `
		DROP TABLE IF EXISTS tuning_demo;
		CREATE TABLE tuning_demo (
			id SERIAL PRIMARY KEY,
			value INTEGER,
			created_at TIMESTAMP DEFAULT NOW()
		);
	`)
	if err != nil {
		log.Fatalf("创建表失败: %v", err)
	}

	// 4. 插入 10 万条数据
	fmt.Println("📝 插入 100,000 条测试数据...")
	_, err = conn.Exec(ctx, `
		INSERT INTO tuning_demo (value)
		SELECT generate_series(1, 100000);
	`)
	if err != nil {
		log.Fatalf("插入数据失败: %v", err)
	}
	fmt.Println("✅ 数据插入完成")

	// 5. 执行排序查询并查看执行计划
	fmt.Println("\n🔍 执行排序查询 (ORDER BY value DESC)...")
	rows, err := conn.Query(ctx, `
		EXPLAIN (ANALYZE, BUFFERS) 
		SELECT * FROM tuning_demo ORDER BY value DESC LIMIT 100;
	`)
	if err != nil {
		log.Fatalf("查询执行计划失败: %v", err)
	}
	defer rows.Close()

	fmt.Println("\n📈 执行计划:")
	for rows.Next() {
		var planLine string
		if err := rows.Scan(&planLine); err != nil {
			log.Printf("扫描结果失败: %v", err)
			continue
		}
		fmt.Printf("  %s\n", planLine)
	}

	fmt.Println("\n🔌 连接已关闭")
}
对应的 PostgreSQL 原生指令(与 Node 版相同,此处省略重复,请参照前文)

Python 语言示例

使用 psycopg2(或 asyncpg,此处选用同步的 psycopg2)。

运行说明
bash 复制代码
# 创建虚拟环境(可选)
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate

# 安装依赖
pip install psycopg2-binary

# 运行
python main.py
代码 (main.py)
python 复制代码
import psycopg2
from psycopg2 import sql

def main():
    # 连接配置
    conn = psycopg2.connect(
        host="localhost",
        port=5432,
        database="postgres",
        user="postgres",
        password="your_password"
    )
    conn.autocommit = True
    cur = conn.cursor()

    print("✅ 已连接到 PostgreSQL")

    # 1. 查看当前关键参数
    params = [
        "shared_buffers",
        "work_mem",
        "maintenance_work_mem",
        "effective_cache_size",
        "checkpoint_timeout",
        "max_wal_size",
        "random_page_cost",
        "autovacuum_max_workers",
    ]

    print("\n📊 当前配置参数:")
    for p in params:
        cur.execute(sql.SQL("SHOW {};").format(sql.Identifier(p)))
        val = cur.fetchone()[0]
        print(f"  {p} = {val}")

    # 2. 会话级别调整 work_mem
    print("\n⚙️  会话级别调整 work_mem...")
    cur.execute("SET work_mem = '64MB';")
    cur.execute("SHOW work_mem;")
    work_mem = cur.fetchone()[0]
    print(f"  work_mem (当前会话) = {work_mem}")

    # 3. 创建测试表
    print("\n📋 创建测试表 tuning_demo...")
    cur.execute("""
        DROP TABLE IF EXISTS tuning_demo;
        CREATE TABLE tuning_demo (
            id SERIAL PRIMARY KEY,
            value INTEGER,
            created_at TIMESTAMP DEFAULT NOW()
        );
    """)

    # 4. 插入 10 万条数据
    print("📝 插入 100,000 条测试数据...")
    cur.execute("""
        INSERT INTO tuning_demo (value)
        SELECT generate_series(1, 100000);
    """)
    print("✅ 数据插入完成")

    # 5. 执行排序查询并查看执行计划
    print("\n🔍 执行排序查询 (ORDER BY value DESC)...")
    cur.execute("""
        EXPLAIN (ANALYZE, BUFFERS) 
        SELECT * FROM tuning_demo ORDER BY value DESC LIMIT 100;
    """)
    rows = cur.fetchall()

    print("\n📈 执行计划:")
    for row in rows:
        print(f"  {row[0]}")

    cur.close()
    conn.close()
    print("\n🔌 连接已关闭")

if __name__ == "__main__":
    main()
对应的 PostgreSQL 原生指令(同前,略)

Java 语言示例

使用 JDBC 驱动(org.postgresql:postgresql)和 Maven 构建。

运行说明
bash 复制代码
# 创建 Maven 项目结构
mkdir -p pg-tuning-demo/src/main/java
cd pg-tuning-demo

# 创建 pom.xml(内容见下方)
# 将 Java 源码放入 src/main/java/PgTuningDemo.java

# 编译并运行
mvn compile exec:java -Dexec.mainClass="PgTuningDemo"
pom.xml
xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>pg-tuning-demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.6.0</version>
        </dependency>
    </dependencies>
</project>
代码 (PgTuningDemo.java)
java 复制代码
import java.sql.*;
import java.util.Arrays;
import java.util.List;

public class PgTuningDemo {
    public static void main(String[] args) {
        String url = "jdbc:postgresql://localhost:5432/postgres";
        String user = "postgres";
        String password = "your_password";

        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            System.out.println("✅ 已连接到 PostgreSQL");

            // 1. 查看当前关键参数
            List<String> params = Arrays.asList(
                "shared_buffers",
                "work_mem",
                "maintenance_work_mem",
                "effective_cache_size",
                "checkpoint_timeout",
                "max_wal_size",
                "random_page_cost",
                "autovacuum_max_workers"
            );

            System.out.println("\n📊 当前配置参数:");
            for (String p : params) {
                try (Statement stmt = conn.createStatement();
                     ResultSet rs = stmt.executeQuery("SHOW " + p + ";")) {
                    if (rs.next()) {
                        System.out.println("  " + p + " = " + rs.getString(1));
                    }
                }
            }

            // 2. 会话级别调整 work_mem
            System.out.println("\n⚙️  会话级别调整 work_mem...");
            try (Statement stmt = conn.createStatement()) {
                stmt.execute("SET work_mem = '64MB';");
            }
            try (Statement stmt = conn.createStatement();
                 ResultSet rs = stmt.executeQuery("SHOW work_mem;")) {
                if (rs.next()) {
                    System.out.println("  work_mem (当前会话) = " + rs.getString(1));
                }
            }

            // 3. 创建测试表
            System.out.println("\n📋 创建测试表 tuning_demo...");
            try (Statement stmt = conn.createStatement()) {
                stmt.execute("DROP TABLE IF EXISTS tuning_demo;");
                stmt.execute("""
                    CREATE TABLE tuning_demo (
                        id SERIAL PRIMARY KEY,
                        value INTEGER,
                        created_at TIMESTAMP DEFAULT NOW()
                    );
                """);
            }

            // 4. 插入 10 万条数据
            System.out.println("📝 插入 100,000 条测试数据...");
            try (Statement stmt = conn.createStatement()) {
                stmt.execute("""
                    INSERT INTO tuning_demo (value)
                    SELECT generate_series(1, 100000);
                """);
            }
            System.out.println("✅ 数据插入完成");

            // 5. 执行排序查询并查看执行计划
            System.out.println("\n🔍 执行排序查询 (ORDER BY value DESC)...");
            try (Statement stmt = conn.createStatement();
                 ResultSet rs = stmt.executeQuery("""
                     EXPLAIN (ANALYZE, BUFFERS) 
                     SELECT * FROM tuning_demo ORDER BY value DESC LIMIT 100;
                 """)) {
                System.out.println("\n📈 执行计划:");
                while (rs.next()) {
                    System.out.println("  " + rs.getString(1));
                }
            }

            System.out.println("\n🔌 连接已关闭");

        } catch (SQLException e) {
            System.err.println("❌ 错误: " + e.getMessage());
            e.printStackTrace();
        }
    }
}
对应的 PostgreSQL 原生指令(同前,略)

各语言技术点对比

语言 驱动/库 连接方式 参数查询 会话级 SET 执行计划
Node.js pg Client 连接 client.query('SHOW ...') client.query('SET ...') EXPLAIN (ANALYZE, BUFFERS)
Go pgx/v5 pgx.Connect conn.QueryRow conn.Exec conn.Query
Python psycopg2 connect() + cursor cur.execute(sql.SQL(...)) cur.execute(...) cur.execute(...) + fetchall
Java JDBC (PostgreSQL) DriverManager.getConnection Statement.executeQuery Statement.execute Statement.executeQuery

以上四种语言示例均实现了相同逻辑,可根据项目技术栈选择参考。

官方文档

参考链接

总结

本文围绕 PostgreSQL 基础性能调优展开,核心要点如下:

  1. 二八法则:聚焦 20% 的关键参数(内存、检查点、自动清理、I/O 成本),即可获得 80% 的性能收益。
  2. 内存参数shared_buffers 设为总内存 25%;effective_cache_size 设为 50%~75%(非实际分配);work_mem 建议在会话级别按需设置,避免全局过大导致 OOM;maintenance_work_mem 按每 1GB 内存 50MB 配置。
  3. 检查点参数checkpoint_timeout 设为 15~30 分钟,checkpoint_completion_target 设为 0.9 以实现平滑写入。
  4. 自动清理 :根据 CPU 核心数调整 autovacuum_max_workers,SSD 存储可提高 autovacuum_vacuum_cost_limit
  5. I/O 成本常量 :现代 SSD 环境下,将 random_page_cost 从默认 4.0 降至 1.5~2.0,让优化器更准确地选择索引扫描。
  6. 辅助工具 :使用 pgtunepg_configurator 生成初始配置,再结合实际监控数据进行微调。
  7. 配置生效:PostgreSQL 以最后一次出现的参数值为准,建议将调优参数统一追加至配置文件末尾。
相关推荐
SelectDB2 小时前
抖音集团 实时数据仓库:Apache Doris / SelectDB 的技术能力与实践
数据库
不会代码的小猴2 小时前
C++新增关键字
开发语言·c++·笔记
SelectDB2 小时前
网易 日志与时序数据分析:Apache Doris / SelectDB 的技术能力与实践
数据库
weixin_539446782 小时前
Navicat Premium 17报缺少ODBC驱动
数据库
哦虎!3 小时前
【数据库】事务
java·数据库·mysql
倔强的石头_3 小时前
时序数据库的难题不只在写入速度
数据库
小赵AI手记4 小时前
技术拆解(十七)具身智能:机器人动作生成为何走向Diffusion Policy?
人工智能·笔记·python·机器人
IT古董5 小时前
【WMS学习笔记系列】01-需求分析
大数据·数据库
zlinear数据采集卡5 小时前
数据采集卡从入门到精通(9):分辨率与精度——16位卡不等于1/65536的精度
开发语言·数据库·fpga开发·开源·c#