PostgreSQL笔记27: 双缓存架构下的内存管理与性能观测实践

VCSA 6.7证书过期别慌!手把手教你通过SSH修改系统时间恢复登录(附STS证书修复脚本) 本文详细介绍了VCSA 6.7证书过期的应急处理方案,包括通过SSH修改系统时间临时恢复登录、全面更新证书体系以及专项修复STS证书的实战步骤。文章还提供了后期验证和系统加固的建议,帮助运维人员有效应对证书过期问题,确保虚拟化平台的稳定运行。 阅读详情

纲要

  • PostgreSQL 缓存架构概览
    • 双缓存(Double Buffering):shared_buffers 与操作系统页缓存(OS Page Cache)
    • shared_buffers:数据库共享内存缓冲区
    • OS Page Cache:操作系统级文件缓存
  • Linux 内存水位线与回收机制
    • 内存水位线:WMARK_HIGH、WMARK_LOW、WMARK_MIN
    • 异步回收:kswapd 内核线程
    • 直接内存回收(Direct Reclaim):同步阻塞
    • OOM Killer 的触发
  • PostgreSQL 私有缓存
    • SysCache:系统表缓存
    • RelCache:关系缓存
    • 缓存淘汰机制的缺失与 idle_session_timeout(PG 14+)
    • pg_timeout 插件与手动清理方案
  • 缓存观测与预热工具
    • pg_buffercache:观测 shared_buffers 状态
    • pg_prewarm:数据预热
    • pgfincore:观测 OS Page Cache
    • pg_buffercache_evict()(PG 17+)
  • shared_buffers 大小配置建议
    • 经验法则:总内存的 25% ~ 40%
    • 双缓存架构下的权衡

PostgreSQL 的双缓存架构

PostgreSQL 采用双缓存(Double Buffering)架构,即数据页同时被数据库自身的共享缓冲区与操作系统页缓存所缓存。

操作系统

PostgreSQL

应用层

未命中

未命中

双缓存
数据冗余

进程私有

查询请求

shared_buffers
共享缓冲区

SysCache / RelCache
私有缓存

OS Page Cache
页缓存

物理磁盘

数据库层面由 shared_buffers 参数定义共享内存缓冲区的大小。当查询需要读取数据块时,PostgreSQL 优先在 shared_buffers 中查找。若未命中,则发起系统调用从操作系统页缓存读取;若操作系统页缓存也未命中,则最终从物理磁盘读取。

这种设计的核心特征在于:PostgreSQL 并不管理操作系统页缓存,而是由内核自主管理。两个缓存层相互独立,互不知晓对方缓存了哪些数据块,因此经常出现同一数据块被同时缓存在两层的现象,即双缓存。

与 Oracle 等数据库严格区分逻辑读与物理读不同,PostgreSQL 的 I/O 统计口径更为复杂——一次数据读取可能命中 shared_buffers、可能命中 OS Page Cache、也可能真正落盘,三者对应完全不同的性能特征。

Linux 内存水位线与直接内存回收

理解 PostgreSQL 双缓存架构的性能影响,首先需要掌握 Linux 内核的内存管理机制。

Linux 内核为每个内存区域(Zone)维护三条水位线(Watermark):

水位线内核常量含义
高水位WMARK_HIGH空闲内存充足,系统不进行回收
低水位WMARK_LOW空闲内存偏低,触发异步回收
最低水位WMARK_MIN内存紧张,触发同步回收

当空闲内存降至低水位(WMARK_LOW)以下时,内核唤醒 kswapd 内核线程,在后台异步回收内存页。kswapd 是一个异步进程,持续扫描并尝试释放内存,以缓解内存压力。

若内存消耗速度超过 kswapd 的回收能力,空闲内存进一步降至最低水位(WMARK_MIN)以下,内核将触发直接内存回收(Direct Reclaim)。直接内存回收是同步阻塞操作:任何试图申请内存的用户态进程都必须等待内核完成内存回收后才能继续执行。此时通过 ps 命令查看,相关进程通常处于 D 状态(不可中断睡眠),典型场景包括等待 I/O 或内存回收完成。

在极端情况下,若直接内存回收仍无法释放足够内存,内核将触发 OOM Killer,依据评分机制选择终止某些进程以释放内存。

在生产环境中,可通过 sar -B 命令观察 pgscan(kswapd 扫描页数)和 pgsteal(直接回收扫描页数)等指标,判断系统是否频繁陷入内存回收压力。

SysCache 与 RelCache:进程私有缓存

除共享的 shared_buffers 外,PostgreSQL 每个后端进程还维护两类私有缓存:

  • SysCache(系统表缓存) :缓存系统表的元数据信息,避免每次操作都访问系统表
  • RelCache(关系缓存) :缓存表的模式信息(RelationData),包括列定义、索引信息等

原生 PostgreSQL 的 SysCache 和 RelCache 没有淘汰机制。进程首次访问某个表后,其元数据将一直保留在该进程的私有内存中,直至进程退出或收到缓存失效消息(如 DDL 操作触发的 sinval 系统广播)。

在多租户或大量表的场景下,长期运行的连接可能导致 SysCache 和 RelCache 占用大量私有内存。可通过 pmap 或 smem 等工具观测进程内存占用情况。

全局

共享内存

后端进程

缓存元数据

缓存关系

缓存数据页

私有内存

SysCache

RelCache

shared_buffers

系统表

磁盘

针对此问题,PostgreSQL 14 引入了 idle_session_timeout 参数,用于控制空闲连接(非事务中)的超时自动断开:

-- 设置空闲会话超时为 5 分钟
ALTER SYSTEM SET idle_session_timeout = '5min';
SELECT pg_reload_conf();

-- 查看当前设置
SHOW idle_session_timeout;

在 PostgreSQL 14 之前,可借助 pg_timeout 插件,或通过定期查询 pg_stat_activity 手动终止空闲连接:

-- 查询空闲连接(非事务中)
SELECT pid, state, state_change, query
FROM pg_stat_activity
WHERE state = 'idle'
  AND state_change < NOW() - INTERVAL '5 minutes'
  AND backend_type = 'client backend';

-- 终止指定连接
SELECT pg_terminate_backend(<pid>);

部分云数据库(如阿里云 RDS、腾讯云 RDS)已在内核层面为 SysCache 和 RelCache 增加了 LRU 淘汰机制,以缓解内存膨胀问题。

缓存观测与预热工具

pg_buffercache:观测 shared_buffers

pg_buffercache 是 PostgreSQL 官方 contrib 模块,提供实时检查共享缓冲区状态的能力。每个缓冲区对应视图中的一行。

安装与使用:

-- 安装扩展
CREATE EXTENSION pg_buffercache;

-- 查看共享缓冲区概要
SELECT * FROM pg_buffercache_summary();

pg_buffercache 视图核心字段:

字段类型说明
bufferidinteger缓冲区 ID,范围 1 ~ shared_buffers
relfilenodeoid关系文件节点号
reltablespaceoid表空间 OID
reldatabaseoid数据库 OID
relblocknumberbigint页面号
isdirtyboolean是否为脏页
usagecountsmallintClock-sweep 访问计数
pinning_backendsinteger固定该缓冲区的后端数

查询特定表的缓存命中情况:

-- 获取表的 relfilenode
SELECT relfilenode, relname FROM pg_class WHERE relname = 'your_table';

-- 查询该表在 shared_buffers 中的缓存块数
SELECT count(*) FROM pg_buffercache
WHERE relfilenode = <relfilenode>
  AND reldatabase = (SELECT oid FROM pg_database WHERE datname = current_database());

PostgreSQL 17:pg_buffercache_evict()

PostgreSQL 17 在 pg_buffercache 模块中新增了 pg_buffercache_evict() 函数,允许按缓冲区 ID 从共享缓冲区中驱逐指定数据块。此外还提供了 pg_buffercache_evict_relation() 和 pg_buffercache_evict_all() 两个函数。

-- PostgreSQL 17+ 示例:驱逐指定缓冲区
SELECT pg_buffercache_evict(bufferid) FROM pg_buffercache
WHERE relfilenode = <relfilenode> LIMIT 10;

该函数仅限超级用户使用,主要用于开发测试场景。在 PostgreSQL 17 之前,若要清空 shared_buffers 中的特定数据,只能重启数据库实例。

pg_prewarm:数据预热

pg_prewarm 模块提供将关系数据预加载到操作系统缓存或 PostgreSQL 缓冲区的能力。

-- 安装扩展
CREATE EXTENSION pg_prewarm;

-- 函数签名
pg_prewarm(
    regclass,        -- 关系名称
    mode text,       -- 'prefetch' | 'read' | 'buffer'
    fork text,       -- 通常为 'main'
    first_block int8, -- 起始块,NULL 表示 0
    last_block int8   -- 结束块,NULL 表示到末尾
) RETURNS int8       -- 返回预热的块数

三种预热模式:

模式行为特点
prefetch向操作系统发出异步预读请求非阻塞,需 OS 支持
read同步读取指定范围的块阻塞,所有平台支持
buffer将块读入 PostgreSQL 缓冲区缓存到 shared_buffers
-- 将整个表预热到 PostgreSQL 缓冲区
SELECT pg_prewarm('your_table', 'buffer');

-- 将表预热到操作系统缓存
SELECT pg_prewarm('your_table', 'read');

-- 自动预热:配置 shared_preload_libraries
-- postgresql.conf:
-- shared_preload_libraries = 'pg_prewarm'
-- pg_prewarm.autoprewarm = true
-- pg_prewarm.autoprewarm_interval = 300s

启用自动预热后,后台工作进程会定期将 shared_buffers 中的缓冲区列表保存到 autoprewarm.blocks 文件,并在数据库重启后恢复这些块。

pgfincore:观测操作系统页缓存

pgfincore 是第三方插件,用于查看关系文件在操作系统页缓存中的状态。

-- 查看表的 OS 页缓存状态
SELECT * FROM pgfincore('your_table');

输出字段包括 pages_mem(已在内存中的页数)、os_pages_free(系统空闲页数)等。

双缓存的性能影响与配置建议

双缓存的利弊

优势:

  • 数据库重启后,数据仍可能保留在 OS Page Cache 中,避免完全从磁盘读取
  • 操作系统层面的 I/O 合并可减少总 I/O 次数

劣势:

  • 同一数据块可能同时在两处缓存,造成内存浪费
  • shared_buffers 过大将挤压 OS Page Cache 可用内存
  • 查询响应时间不稳定,呈现"时快时慢"的特征

shared_buffers 配置建议

PostgreSQL 官方文档建议:对于 1GB 以上内存的专用数据库服务器,shared_buffers 的合理起始值为系统内存的 25%。将超过 40% 的 RAM 分配给 shared_buffers 不太可能比较小分配量带来更好的性能。

-- 查看当前 shared_buffers 大小
SHOW shared_buffers;

-- 修改配置(需重启)
ALTER SYSTEM SET shared_buffers = '4GB';
-- 或直接在 postgresql.conf 中设置
-- shared_buffers = 4GB

双缓存观测实验

以下实验演示如何观测双缓存的实际效果:

-- 1. 创建测试表并插入数据
CREATE TABLE cache_test (id serial, data text);
INSERT INTO cache_test (data) SELECT generate_series(1, 100000)::text;

-- 2. 获取表的 relfilenode
SELECT relfilenode, relname FROM pg_class WHERE relname = 'cache_test';

-- 3. 使用 pg_prewarm 预热到 shared_buffers
SELECT pg_prewarm('cache_test', 'buffer');

-- 4. 查看 shared_buffers 中的缓存情况
SELECT count(*) FROM pg_buffercache
WHERE relfilenode = <relfilenode>;

-- 5. 执行查询并观察执行时间
EXPLAIN (ANALYZE, BUFFERS) SELECT count(*) FROM cache_test;

-- 6. PostgreSQL 17+: 驱逐该表的所有缓冲区
SELECT pg_buffercache_evict(bufferid)
FROM pg_buffercache
WHERE relfilenode = <relfilenode>;

-- 7. 再次执行查询,观察 buffer 命中率变化
EXPLAIN (ANALYZE, BUFFERS) SELECT count(*) FROM cache_test;

API 速览

pg_buffercache

API所属库签名说明
pg_buffercache 视图pg_buffercache-每个缓冲区一行,展示缓存状态
pg_buffercache_summary()pg_buffercache() RETURNS record返回共享缓冲区概要统计
pg_buffercache_usage_counts()pg_buffercache() RETURNS SETOF record按 usage count 分组统计
pg_buffercache_evict(bufferid integer)pg_buffercache(integer) RETURNS booleanPG 17+,驱逐指定缓冲区
pg_buffercache_evict_relation(regclass)pg_buffercache(regclass) RETURNS integerPG 17+,驱逐关系的所有缓冲区
pg_buffercache_evict_all()pg_buffercache() RETURNS integerPG 17+,驱逐所有缓冲区

pg_prewarm

API所属库签名说明
pg_prewarm(regclass, mode, fork, first_block, last_block)pg_prewarm(regclass, text, text, int8, int8) RETURNS int8预热关系数据到缓存
autoprewarm_start_worker()pg_prewarm() RETURNS void手动启动自动预热工作进程
autoprewarm_dump_now()pg_prewarm() RETURNS int8立即保存当前缓存块列表

系统视图与函数

API所属库签名说明
pg_stat_activity系统视图-查询当前连接与会话状态
pg_terminate_backend(pid integer)系统函数(integer) RETURNS boolean终止指定后端进程
pg_reload_conf()系统函数() RETURNS boolean重新加载配置文件

Demo 简单示例

以下是一个完整的 Node.js 示例,演示如何使用 pg 驱动连接 PostgreSQL,通过 pg_buffercache 观测缓存状态,并使用 pg_prewarm 进行数据预热。

pg-cache-demo/
├── package.json
├── index.js
└── sql/
    └── init.sql

package.json

{
  "name": "pg-cache-demo",
  "version": "1.0.0",
  "description": "PostgreSQL 缓存管理演示",
  "main": "index.js",
  "scripts": {
    "start": "node index.js"
  },
  "dependencies": {
    "pg": "^8.11.0"
  }
}

index.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);
  await client.connect();

  try {
    // 1. 创建测试表
    await client.query(`
      DROP TABLE IF EXISTS cache_demo;
      CREATE TABLE cache_demo (id serial, data text);
      INSERT INTO cache_demo (data)
      SELECT generate_series(1, 50000)::text;
    `);
    console.log('✅ 测试表创建完成');

    // 2. 安装扩展(如未安装)
    await client.query('CREATE EXTENSION IF NOT EXISTS pg_buffercache;');
    await client.query('CREATE EXTENSION IF NOT EXISTS pg_prewarm;');
    console.log('✅ 扩展已加载');

    // 3. 获取表的 relfilenode
    const relResult = await client.query(`
      SELECT relfilenode, relname
      FROM pg_class
      WHERE relname = 'cache_demo';
    `);
    const relfilenode = relResult.rows[0].relfilenode;
    console.log(`📋 表 relfilenode: ${relfilenode}`);

    // 4. 预热前缓存状态
    const before = await client.query(`
      SELECT count(*) as cached_blocks
      FROM pg_buffercache
      WHERE relfilenode = $1
        AND reldatabase = (SELECT oid FROM pg_database WHERE datname = current_database());
    `, [relfilenode]);
    console.log(`📊 预热前 shared_buffers 缓存块数: ${before.rows[0].cached_blocks}`);

    // 5. 使用 pg_prewarm 预热
    const warmed = await client.query(`
      SELECT pg_prewarm('cache_demo', 'buffer') as blocks_warmed;
    `);
    console.log(`🔥 预热块数: ${warmed.rows[0].blocks_warmed}`);

    // 6. 预热后缓存状态
    const after = await client.query(`
      SELECT count(*) as cached_blocks
      FROM pg_buffercache
      WHERE relfilenode = $1
        AND reldatabase = (SELECT oid FROM pg_database WHERE datname = current_database());
    `, [relfilenode]);
    console.log(`📊 预热后 shared_buffers 缓存块数: ${after.rows[0].cached_blocks}`);

    // 7. 执行查询并分析缓存命中
    const explain = await client.query(`
      EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
      SELECT count(*) FROM cache_demo;
    `);
    const plan = explain.rows[0]['QUERY PLAN'][0];
    console.log('📈 查询执行计划:');
    console.log(`   - 执行时间: ${plan['Execution Time']} ms`);
    console.log(`   - 共享缓冲区命中: ${plan['Shared Hit Blocks'] || 0} 块`);
    console.log(`   - 共享缓冲区读取: ${plan['Shared Read Blocks'] || 0} 块`);

    // 8. 查看共享缓冲区概要
    const summary = await client.query('SELECT * FROM pg_buffercache_summary();');
    console.log('📊 共享缓冲区概要:');
    console.log(`   - 已使用缓冲区: ${summary.rows[0].buffers_used}`);
    console.log(`   - 未使用缓冲区: ${summary.rows[0].buffers_unused}`);
    console.log(`   - 脏缓冲区: ${summary.rows[0].buffers_dirty}`);
    console.log(`   - 平均使用计数: ${summary.rows[0].usagecount_avg}`);

  } catch (err) {
    console.error('❌ 错误:', err.message);
  } finally {
    await client.end();
  }
}

main();

运行说明

  1. 安装依赖:npm install pg
  2. 修改 index.js 中的数据库连接配置
  3. 确保 PostgreSQL 已安装 pg_buffercache 和 pg_prewarm 扩展
  4. 运行:npm start

技术点总结

  • 使用 pg 驱动连接 PostgreSQL 并执行原生 SQL
  • 通过 pg_buffercache 视图观测 shared_buffers 缓存状态
  • 使用 pg_prewarm 函数将表数据预热到共享缓冲区
  • 通过 EXPLAIN (ANALYZE, BUFFERS) 分析查询的缓存命中情况
  • 调用 pg_buffercache_summary() 获取缓冲区概要统计

多语言示例

基于前文 Node.js 示例,本节提供 Go、Python 和 Java 三种语言的完整实现,功能保持一致:连接 PostgreSQL,创建测试表,安装扩展,观测 shared_buffers 缓存状态,执行 pg_prewarm 预热,并输出缓存统计信息。

Go 示例

使用 pgx 驱动(推荐),需提前安装:go get github.com/jackc/pgx/v5

pg-cache-demo-go/
├── go.mod
├── go.sum
└── main.go

go.mod

module pg-cache-demo

go 1.21

require github.com/jackc/pgx/v5 v5.5.0

main.go

package main

import (
	"context"
	"fmt"
	"log"

	"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)

	// 1. 创建测试表
	_, err = conn.Exec(ctx, `
		DROP TABLE IF EXISTS cache_demo;
		CREATE TABLE cache_demo (id serial, data text);
		INSERT INTO cache_demo (data) SELECT generate_series(1, 50000)::text;
	`)
	if err != nil {
		log.Fatalf("创建表失败: %v", err)
	}
	fmt.Println("✅ 测试表创建完成")

	// 2. 安装扩展
	_, err = conn.Exec(ctx, "CREATE EXTENSION IF NOT EXISTS pg_buffercache;")
	if err != nil {
		log.Fatalf("安装 pg_buffercache 失败: %v", err)
	}
	_, err = conn.Exec(ctx, "CREATE EXTENSION IF NOT EXISTS pg_prewarm;")
	if err != nil {
		log.Fatalf("安装 pg_prewarm 失败: %v", err)
	}
	fmt.Println("✅ 扩展已加载")

	// 3. 获取表的 relfilenode
	var relfilenode uint32
	err = conn.QueryRow(ctx, `
		SELECT relfilenode FROM pg_class WHERE relname = 'cache_demo';
	`).Scan(&relfilenode)
	if err != nil {
		log.Fatalf("获取 relfilenode 失败: %v", err)
	}
	fmt.Printf("📋 表 relfilenode: %d\n", relfilenode)

	// 4. 预热前缓存状态
	var beforeCount int
	err = conn.QueryRow(ctx, `
		SELECT count(*) FROM pg_buffercache
		WHERE relfilenode = $1
		  AND reldatabase = (SELECT oid FROM pg_database WHERE datname = current_database());
	`, relfilenode).Scan(&beforeCount)
	if err != nil {
		log.Fatalf("查询预热前缓存失败: %v", err)
	}
	fmt.Printf("📊 预热前 shared_buffers 缓存块数: %d\n", beforeCount)

	// 5. 使用 pg_prewarm 预热
	var warmed int64
	err = conn.QueryRow(ctx, "SELECT pg_prewarm('cache_demo', 'buffer');").Scan(&warmed)
	if err != nil {
		log.Fatalf("预热失败: %v", err)
	}
	fmt.Printf("🔥 预热块数: %d\n", warmed)

	// 6. 预热后缓存状态
	var afterCount int
	err = conn.QueryRow(ctx, `
		SELECT count(*) FROM pg_buffercache
		WHERE relfilenode = $1
		  AND reldatabase = (SELECT oid FROM pg_database WHERE datname = current_database());
	`, relfilenode).Scan(&afterCount)
	if err != nil {
		log.Fatalf("查询预热后缓存失败: %v", err)
	}
	fmt.Printf("📊 预热后 shared_buffers 缓存块数: %d\n", afterCount)

	// 7. 执行查询并分析缓存命中(使用 EXPLAIN)
	var planJSON string
	err = conn.QueryRow(ctx, `
		EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
		SELECT count(*) FROM cache_demo;
	`).Scan(&planJSON)
	if err != nil {
		log.Fatalf("执行 EXPLAIN 失败: %v", err)
	}
	fmt.Println("📈 查询执行计划 (JSON):")
	fmt.Println(planJSON[:200] + "...")

	// 8. 查看共享缓冲区概要
	var buffersUsed, buffersUnused, buffersDirty int
	var usagecountAvg float64
	err = conn.QueryRow(ctx, `
		SELECT buffers_used, buffers_unused, buffers_dirty, usagecount_avg
		FROM pg_buffercache_summary();
	`).Scan(&buffersUsed, &buffersUnused, &buffersDirty, &usagecountAvg)
	if err != nil {
		log.Fatalf("获取概要失败: %v", err)
	}
	fmt.Println("📊 共享缓冲区概要:")
	fmt.Printf("   - 已使用缓冲区: %d\n", buffersUsed)
	fmt.Printf("   - 未使用缓冲区: %d\n", buffersUnused)
	fmt.Printf("   - 脏缓冲区: %d\n", buffersDirty)
	fmt.Printf("   - 平均使用计数: %.2f\n", usagecountAvg)
}

运行说明

  1. 初始化模块:go mod tidy
  2. 修改 connStr 中的数据库连接信息
  3. 确保 PostgreSQL 已安装扩展
  4. 运行:go run main.go

Python 示例

使用 psycopg2 驱动,需提前安装:pip install psycopg2-binary

pg-cache-demo-py/
├── requirements.txt
└── main.py

requirements.txt

psycopg2-binary>=2.9.9

main.py

import psycopg2
import psycopg2.extras

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

    try:
        # 1. 创建测试表
        cur.execute("""
            DROP TABLE IF EXISTS cache_demo;
            CREATE TABLE cache_demo (id serial, data text);
            INSERT INTO cache_demo (data) SELECT generate_series(1, 50000)::text;
        """)
        print("✅ 测试表创建完成")

        # 2. 安装扩展
        cur.execute("CREATE EXTENSION IF NOT EXISTS pg_buffercache;")
        cur.execute("CREATE EXTENSION IF NOT EXISTS pg_prewarm;")
        print("✅ 扩展已加载")

        # 3. 获取表的 relfilenode
        cur.execute("SELECT relfilenode FROM pg_class WHERE relname = 'cache_demo';")
        relfilenode = cur.fetchone()[0]
        print(f"📋 表 relfilenode: {relfilenode}")

        # 4. 预热前缓存状态
        cur.execute("""
            SELECT count(*) FROM pg_buffercache
            WHERE relfilenode = %s
              AND reldatabase = (SELECT oid FROM pg_database WHERE datname = current_database());
        """, (relfilenode,))
        before_count = cur.fetchone()[0]
        print(f"📊 预热前 shared_buffers 缓存块数: {before_count}")

        # 5. 使用 pg_prewarm 预热
        cur.execute("SELECT pg_prewarm('cache_demo', 'buffer');")
        warmed = cur.fetchone()[0]
        print(f"🔥 预热块数: {warmed}")

        # 6. 预热后缓存状态
        cur.execute("""
            SELECT count(*) FROM pg_buffercache
            WHERE relfilenode = %s
              AND reldatabase = (SELECT oid FROM pg_database WHERE datname = current_database());
        """, (relfilenode,))
        after_count = cur.fetchone()[0]
        print(f"📊 预热后 shared_buffers 缓存块数: {after_count}")

        # 7. 执行查询并分析缓存命中
        cur.execute("""
            EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
            SELECT count(*) FROM cache_demo;
        """)
        plan = cur.fetchone()[0][0]  # 返回 JSON 数组
        print("📈 查询执行计划:")
        print(f"   - 执行时间: {plan['Execution Time']} ms")
        print(f"   - 共享缓冲区命中: {plan.get('Shared Hit Blocks', 0)} 块")
        print(f"   - 共享缓冲区读取: {plan.get('Shared Read Blocks', 0)} 块")

        # 8. 查看共享缓冲区概要
        cur.execute("""
            SELECT buffers_used, buffers_unused, buffers_dirty, usagecount_avg
            FROM pg_buffercache_summary();
        """)
        row = cur.fetchone()
        print("📊 共享缓冲区概要:")
        print(f"   - 已使用缓冲区: {row[0]}")
        print(f"   - 未使用缓冲区: {row[1]}")
        print(f"   - 脏缓冲区: {row[2]}")
        print(f"   - 平均使用计数: {row[3]:.2f}")

    except Exception as e:
        print(f"❌ 错误: {e}")
    finally:
        cur.close()
        conn.close()

if __name__ == "__main__":
    main()

运行说明

  1. 安装依赖:pip install -r requirements.txt
  2. 修改 main.py 中的数据库连接信息
  3. 运行:python main.py

Java 示例

使用 JDBC 驱动(需下载 postgresql-42.7.2.jar),并添加至 classpath。

pg-cache-demo-java/
├── Main.java
└── postgresql-42.7.2.jar

Main.java

import java.sql.*;
import org.json.JSONArray;
import org.json.JSONObject;

public class Main {
    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)) {
            conn.setAutoCommit(true);
            Statement stmt = conn.createStatement();

            // 1. 创建测试表
            stmt.executeUpdate(
                "DROP TABLE IF EXISTS cache_demo;" +
                "CREATE TABLE cache_demo (id serial, data text);" +
                "INSERT INTO cache_demo (data) SELECT generate_series(1, 50000)::text;"
            );
            System.out.println("✅ 测试表创建完成");

            // 2. 安装扩展
            stmt.executeUpdate("CREATE EXTENSION IF NOT EXISTS pg_buffercache;");
            stmt.executeUpdate("CREATE EXTENSION IF NOT EXISTS pg_prewarm;");
            System.out.println("✅ 扩展已加载");

            // 3. 获取表的 relfilenode
            ResultSet rs = stmt.executeQuery(
                "SELECT relfilenode FROM pg_class WHERE relname = 'cache_demo';"
            );
            rs.next();
            int relfilenode = rs.getInt(1);
            rs.close();
            System.out.printf("📋 表 relfilenode: %d%n", relfilenode);

            // 4. 预热前缓存状态
            rs = stmt.executeQuery(
                "SELECT count(*) FROM pg_buffercache " +
                "WHERE relfilenode = " + relfilenode +
                " AND reldatabase = (SELECT oid FROM pg_database WHERE datname = current_database());"
            );
            rs.next();
            int beforeCount = rs.getInt(1);
            rs.close();
            System.out.printf("📊 预热前 shared_buffers 缓存块数: %d%n", beforeCount);

            // 5. 使用 pg_prewarm 预热
            rs = stmt.executeQuery("SELECT pg_prewarm('cache_demo', 'buffer');");
            rs.next();
            long warmed = rs.getLong(1);
            rs.close();
            System.out.printf("🔥 预热块数: %d%n", warmed);

            // 6. 预热后缓存状态
            rs = stmt.executeQuery(
                "SELECT count(*) FROM pg_buffercache " +
                "WHERE relfilenode = " + relfilenode +
                " AND reldatabase = (SELECT oid FROM pg_database WHERE datname = current_database());"
            );
            rs.next();
            int afterCount = rs.getInt(1);
            rs.close();
            System.out.printf("📊 预热后 shared_buffers 缓存块数: %d%n", afterCount);

            // 7. 执行查询并分析缓存命中(JSON 解析需依赖 org.json)
            rs = stmt.executeQuery(
                "EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) SELECT count(*) FROM cache_demo;"
            );
            rs.next();
            String jsonStr = rs.getString(1);
            rs.close();
            JSONArray jsonArray = new JSONArray(jsonStr);
            JSONObject plan = jsonArray.getJSONObject(0);
            System.out.println("📈 查询执行计划:");
            System.out.printf("   - 执行时间: %s ms%n", plan.get("Execution Time"));
            System.out.printf("   - 共享缓冲区命中: %s 块%n", plan.optInt("Shared Hit Blocks", 0));
            System.out.printf("   - 共享缓冲区读取: %s 块%n", plan.optInt("Shared Read Blocks", 0));

            // 8. 查看共享缓冲区概要
            rs = stmt.executeQuery(
                "SELECT buffers_used, buffers_unused, buffers_dirty, usagecount_avg " +
                "FROM pg_buffercache_summary();"
            );
            rs.next();
            System.out.println("📊 共享缓冲区概要:");
            System.out.printf("   - 已使用缓冲区: %d%n", rs.getInt(1));
            System.out.printf("   - 未使用缓冲区: %d%n", rs.getInt(2));
            System.out.printf("   - 脏缓冲区: %d%n", rs.getInt(3));
            System.out.printf("   - 平均使用计数: %.2f%n", rs.getDouble(4));
            rs.close();

        } catch (Exception e) {
            System.err.println("❌ 错误: " + e.getMessage());
            e.printStackTrace();
        }
    }
}

运行说明

  1. 下载 postgresql-42.7.2.jar 和 org.json 库(如 json-20240303.jar)并置于同一目录
  2. 编译:javac -cp ".:postgresql-42.7.2.jar:json-20240303.jar" Main.java
  3. 运行:java -cp ".:postgresql-42.7.2.jar:json-20240303.jar" Main
  4. 修改数据库连接信息后即可执行。

多语言对比

维度Node.js (pg)Go (pgx)Python (psycopg2)Java (JDBC)
驱动/库pg (npm)pgx/v5psycopg2-binaryPostgreSQL JDBC 驱动
连接方式new Client(config)pgx.Connect(ctx, connStr)psycopg2.connect(**kwargs)DriverManager.getConnection(url, user, pwd)
参数占位符$1, $2$1, $2%s(使用 % 格式化)或 %s 配合元组字符串拼接(或 ? 但 PG 推荐 $n)
上下文支持回调/Promise/async原生 context.Context无(同步)无(同步)
JSON 解析原生 JSON.parse需自定义或使用 encoding/json内置 json 模块需外部库如 org.json
错误处理try-catch返回 errortry-excepttry-catch
连接池内置 Poolpgxpoolpsycopg2.poolHikariCP 等第三方
异步支持原生 async/await原生 goroutine + pgx 异步需 psycopg2 异步变体或 asyncpg需 CompletableFuture 或响应式驱动
依赖管理npmgo modulespipMaven/Gradle 或手动 jar
适用场景快速原型、轻量服务高性能并发微服务数据分析、脚本自动化企业级应用、Spring 生态

阶段性总结

以上四种语言实现均基于相同的 PostgreSQL 缓存管理流程,核心 SQL 与操作步骤完全一致,差异主要体现在驱动 API、参数化查询风格、错误处理模式及 JSON 解析方式。开发者可根据项目技术栈选择对应实现,所有示例均可在正确配置数据库连接后直接运行。

官方文档

参考链接

总结

本文系统阐述了 PostgreSQL 双缓存架构的内在机理与实践管理方法。核心要点包括:

  1. 双缓存架构:PostgreSQL 同时依赖 shared_buffers 与操作系统页缓存,两者独立运作且可能存在数据冗余,这是导致内存效率问题与查询响应时间不稳定的根本原因。
  2. 操作系统内存回收:Linux 通过 WMARK_HIGH、WMARK_LOW、WMARK_MIN 三条水位线驱动 kswapd 异步回收与直接内存回收(同步阻塞),在内存紧张时可能阻塞 PostgreSQL 进程。
  3. 私有缓存管理:SysCache 与 RelCache 无淘汰机制,长期连接可能导致内存膨胀。PostgreSQL 14 引入的 idle_session_timeout 及 pg_timeout 插件是有效的应对手段。
  4. 观测与预热工具:pg_buffercache 用于观测 shared_buffers 状态,pg_prewarm 用于数据预热,pgfincore 用于观测操作系统页缓存。PostgreSQL 17 新增的 pg_buffercache_evict() 系列函数为测试场景提供了缓冲区驱逐能力。
  5. 配置经验法则:shared_buffers 通常设置为系统总内存的 25%~40%,在双缓存架构下需为操作系统页缓存预留足够内存。
卫星载荷应用方案 本文将从技术和应用层面探讨卫星载荷的应用方案,分析各个领域的特点和挑战,以期为卫星载荷的应用提供一定的指导和参考。卫星载荷应用方案涵盖了通信、地球观测、气象预报、导航定位等诸多领域在每一个领域中,都存在着不同的技术挑战和应用需求。随着技术的进步和需求的变化,卫星载荷应用方案将不断发展和完善,为人类社会的进步和发展做出更大的贡献。用户可以通过接收多颗卫星的信号,计算自身的位置和速度信息。在覆盖扩展方面,可以采用多星座、MEO、GEO 等不同轨道高度的卫星布局,结合卫星的特点和应用需求,实现覆盖范围的扩展。 阅读详情

相关推荐

无人驾驶小车调试笔记(六)-- 车轮校准

简介:小车的动力完全来自于两个电机带动的车轮,在理想状态下,给两个电机同样的驱动参数,两个车轮会以同样的转速带动小车直线行驶,而实际情况是每个电机可能都会有个体差异,也就是说给予同样的驱动参数,表现出的转动速度会有些许不同,两个轮子的差速会导致车辆拐弯,后果就是给了直线行驶的控制命令,但是车走着走着就偏离了原本的方向,所以在开始控制之前,我们需要先平衡两个车轮的转速,保证在同样驱动参数下,两个车轮转速大致相同,也就是可以走直线。 车轮校准分为3步,测试、调整后再测试、保存参...

aibingjin的博客 1823

PostgreSQL笔记21: 文件系统、Buffer I/O与数据页底层结构解析

本文摘要介绍了PostgreSQL的存储结构与I/O模型,重点包括: 文件系统支持:PostgreSQL不支持裸设备,依赖XFS、EXT4等文件系统,默认使用Buffer I/O(经OS Page Cache),存在双缓存问题。 I/O演进:v16引入实验性Direct I/O(debug_io_direct),v17优化流式读取,v18支持异步I/O(AIO)和io_uring,提升性能。 数据页结构:默认8KB页大小,包含页头、行指针、元组数据等部分,采用双向增长设计(行指针向后、元组向前)优化插入效率

Wang的专栏 369

Vivado 2018.3 SDK烧写bin文件避坑指南:如何解决QSPI模式下的常见错误

本文深入解析了在Vivado 2018.3 SDK中为Zynq FPGA烧写QSPI Flash时遇到的常见问题,特别是启动模式为QSPI时出现的警告与失败。文章从Zynq启动机制入手,提供了修改FSBL代码以强制使用JTAG模式、调整硬件配置等核心解决方案,并分享了实战案例与最佳实践,帮助开发者高效避坑,确保bin文件烧写成功。

arduino9maker的博客 390

生产级大模型后端架构实战:流式响应、vLLM服务与四层分治设计

大模型后端架构不是堆砌前沿技术,而是解决真实约束下的稳定性、延迟与成本平衡问题。其核心原理在于将LLM推理、流式响应、模型服务等能力解耦为可独立演进的层级——接入网关保障SSE长连接可靠,能力编排层实现Prompt/RAG/降级等业务逻辑隔离,模型服务层依托vLLM实现高吞吐低延迟推理,数据层统一管理状态与配额。这种分层架构显著提升可观测性、可维护性与故障收敛速度,广泛适用于财税、电商、客服等中小规模LLM落地场景,尤其适合GPU资源有限(如单A10)、团队无专职MLOps的工程团队。

weixin_30252709的博客 495

手把手教你用Python处理MAHNOB-HCI多模态数据集(含脑电EEG/眼动/视频数据同步实战)

本文详细介绍了如何使用Python处理MAHNOB-HCI多模态数据集,涵盖脑电(EEG)、眼动追踪和视频数据的同步解析与对齐。通过MNE-Python、pandas和OpenCV等工具,实现从BDF、TSV到XML格式的高效处理,解决时间轴对齐难题,助力情感计算与人机交互研究。

weixin_30998797的博客 370

华为OD机考双机位C卷 - 风险投资计划(Java & Python& JS & C/C++ & GO )

华为OD机考双机位C卷 - 风险投资计划(OD上机考试双机位C卷)。提供Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。

算法大师! 548

操作系统缓存:被低估的性能加速器,Redis之外的高效选择

1. 先搞清楚“操作系统缓存”到底在解决什么问题 别再一提到缓存就只想到 Redis 了。很多性能问题,尤其是高并发、大数据量读取的场景,瓶颈往往不在应用层,而在底层的数据获取路径上。这时候,操作系统的缓存机制,特别是 Linux 内核的 Page Cache,才是那个被严重低估的“隐形王者”。 它解决的核心问题是 减少慢速 I/O(尤其是磁盘 I/O)对应用响应速度的拖累 。当你的程序频繁读取同一个文件,或者数据库查询需要扫描大量数据文件时,如果每次都去访问机械硬盘甚至网络存储,延迟会高得吓人。操作系统内

weixin_33994429的博客 519

人工智能大语言模型微调技术:SFT 监督微调、LoRA 微调方法、P-tuning v2 微调方法、Freeze 监督微调方法

人工智能大语言模型微调技术:SFT 监督微调、LoRA 微调方法、P-tuning v2 微调方法、Freeze 监督微调方法

丨汀、的博客 4万+

机器学习生产化落地:从Notebook到稳定服务的系统工程

机器学习模型部署不是简单的代码上线,而是涵盖数据加载、特征计算、模型推理与服务编排的全链路系统工程。其核心原理在于解耦异构组件、标准化交付物(如ONNX)、保障环境一致性(K8s+GitOps),并建立语义化可观测体系。技术价值体现在可信赖性(P99延迟可控)、可维护性(自动回滚/灰度发布)与可计量性(推理成本/业务指标挂钩)。典型应用场景包括电商推荐、金融反欺诈与工业预测性维护。本文聚焦真实产线中模型服务稳定性、特征实时供给、GPU资源治理及带业务含义的监控告警等关键实践,覆盖Triton模型服务、Fea

weixin_34320159的博客 430

【STM32项目】基于stm32智能鱼缸控制系统的设计与实现(完整工程资料源码)

随着现代化经济的快速发展,物质条件富足的人开始追求精神富足,越来越多的人通过饲养观赏鱼来充实生活、释放压力,但并非所有饲养者都有时间和经验来管理好鱼缸。一种由单片机控制,可实时检测和调节鱼缸环境,可把鱼缸环境实时反映给用户并能被用户远程操控的系统,可以解决养鱼成活率低、耗费时间和精力、参与感不强等诸多难题。

嵌入式技术开发 7355

工业质检终极方案:C#上位机融合 YOLO+Blob分析(一站式尺寸测量+缺陷检测)

本次上位机界面严格遵循工业现场操作规范简洁、直观、按钮大、文字清晰、参数可视化调节,操作人员为车间工人/质检员,无需复杂操作,所有功能一目了然。所有控件均为WinForms原生控件,拖拽即可完成布局,无任何自定义控件,开发效率拉满,核心布局永久复用本次实战完成的C#上位机+YOLO+Blob融合质检方案,是工业规则零件全尺寸质检的终极落地方案,完美解决了单一检测方案的所有短板,具备以下不可替代的工业价值。

这里只有干货:从 Modbus 通信到 MES 对接,从 YOLO 训练到工控机部署,带你搞定工业软件开发全流程。 1344

Mac下MySQL的配置和修改密码以及忘记密码后的重置密码

1 ) Mac下MySQL的配置和修改密码 2 ) 忘记并重置密码 启动MySQL服务,通过 $ps aux | grep mysql 判断是否启动该服务,若未启动,则到 系统偏好设置/MySQL面板 启动服务 软件默认会安装到:/usr/local/mysql 目录,进入这个目录下的bin目录:$cd /usr/local/mysql/bin 执行 $./mysql -u root -p 通过初始密码登录MySQL 通...

Wang的专栏 6259

Ego Planner规划算法(上)——Ego Planner流程介绍

本系列文章用于学习记录Ego-Planner规划算法的相关内容,本系列文章是在在Fast Planner规划算法系列文章的基础上,展开对Ego Planner的相关介绍,主要包括Ego Planner算法流程和程序框架两部分,主要学习资料是深蓝学院的移动机器人运动规划课程

慕羽★的博客 1万+

mysql数据库的一些简单的sql语句 内连接 - 表的关联查询

# 创建一个user表 create table users( id int primary key auto_increment, name varchar(20), dpt_id int ); # 向user表中插入数据 insert into users values (null,'刘备',1), (null,'关羽',2), (null,'赵

Wang的专栏 4264

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

数据库、数据库类型、RDBMS以及MySQL

什么是数据库 数据库 就是按一定的结构来组织、存储和管理数据的仓库 可以很方便的进行crud, 能提供多个用户共享、具有尽可能小的冗余度、与应用程序批次独立的数据集合。 其他持久储存缺点:文件操作(不灵活) 数据库类型 包括 : 关系型 和 非关系型 【关系型】 MySQL MariaDB PostgreSQL Microsoft SQL Server Oracle数据库 Sybase 【...

Wang的专栏 4039

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

Eggjs笔记:Eggjs集成ElasticSearch及CRUD操作

相关文档 https://www.elastic.co/guide/index.html https://www.elastic.co/guide/en/elasticsearch/client https://www.elastic.co/guide/en/elasticsearch/client/javascript-api/current/configuration.html https://www.elastic.co/guide/en/elasticsearch/client/javascrip

Wang的专栏 3530

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

Redis: 集群高可用之故障转移和集群迁移

若要检查源和目标是否数据统一,还可选择阿里配套工具RedisFullCheck。3.4 使用 RedisFullCheck 工具检查。下载官方编译好的工具包,直接解压后就能使用。故障转移,包括自动故障转移和手动故障转移。运行之后,就给一个反馈结果,如果是。1.2 原集群与目标集群结构不一致。1.1 原集群与目标集群结构一致。2 ) 手动故障转移。

Wang的专栏 2918

Python笔记:基于Django框架的数据库设计

概述 需要根据《项目建议书》、《需求说明书》、界面原型、各种标准规范对数据库进行设计, 我们目前先参考这一设计图 结构设计 1 ) 找实体 实体是实体-关系模型的基本对象, 是现实世界中各种事物的抽象。 凡是可以相互区 开并可以被识 的事、物、概念等对象均可认为是实体。 基本的实体列表如下: 会员 类别 商品 订单 订单详情 收货地址 商品评论 商品图片 友情链接 站内公告 … ...

Wang的专栏 2772

Elastic Stack梳理:集群搭建指南之高可用架构与实战部署

本文介绍了Elasticsearch分布式架构和Cerebro监控工具的部署策略。主要内容包括: ES集群角色划分(主节点、数据节点、协调节点)及功能说明 Cerebro监控工具的独立部署方法和访问方式 集群拓扑规划示例,包含节点IP分配和架构拓扑图 各类型节点的配置原则和数量计算公式 高可用主节点配置要点及示例配置 生产环境优化建议,包括分片规划、硬件配置和安全措施 文章提供了完整的集群部署方案,涵盖从基础概念到具体实施的各个环节,适用于不同规模的ES集群建设。

Wang的专栏 2545

Redis: 集群环境搭建,集群状态检查,分析主从日志,查看集群信息

当从节点获得足够多的选票时,它就会成为新的主节点,并增加自己的configEpoch来更新槽位配置。在Redis集群中,纪元的更新通常是由节点间的通信和状态变更触发的。例如,当某个节点接收到来自其他节点的状态变更通知时,它会比较通知中的纪元与自己的纪元。如果通知中的纪元更大,则更新自己的纪元。确保集群状态的一致性:通过递增的纪元,集群中的节点可以确保它们对集群状态的认知是一致的。通过递增的纪元版本号,集群中的节点可以确保它们对集群状态的认知是一致的,并解决可能出现的配置冲突问题。4 )纪元的更新机制。

Wang的专栏 2466

MySQL: 数据库性能的决定性因素之从双十一大促高并发场景看系统瓶颈与优化路径

电商大促期间,数据库面临高并发写入、连接数暴增、慢查询积压等核心挑战。本文分析了锁竞争、主从延迟等关键性能瓶颈,并提出多种优化方案:1)使用乐观锁通过版本号控制库存超卖;2)CAS机制实现无锁并发;3)分布式锁协调多节点操作。这些方案在保证数据一致性的同时提升吞吐量,适用于库存扣减、订单创建等典型场景,有效解决数据库扩展难题。

Wang的专栏 2426

Redis: Sentinel哨兵监控架构及环境搭建

在主从模式下,我们通过从节点只读模式提高了系统的并发能力并发不断增加,只需要扩展从节点即可,只要主从服务器之间,网络连接正常主服务器就会将写入自己的数据同步更新给从服务器,从而保证主从服务器的数据相同在这种架构下,它仍然是有一些缺点的,比如说它的可靠性保证不是很好,因为主从模式下,主节点故障了便无法提供写入的服务现在,我们通过哨兵来解决这个问题然后,主节点其实写的压力也是并没有被释放的包括主从之间的数据是相同的,它还有数据冗余一个问题后面集群也会解决这个问题。

Wang的专栏 2181

MongoDB小课堂: 高级查询操作符与游标管理综合指南之深度整合逻辑操作符、字段处理、数组查询与游标控制的最佳实践

MongoDB查询操作符与游标管理摘要 本文整合了MongoDB常用查询操作符和游标管理的关键知识点: 比较操作符: $in和$nin用于多值匹配,注意$nin会包含字段缺失文档 结合$exists可处理字段缺失问题 逻辑操作符: $and可简写为逗号分隔条件 $or可用$in优化等值查询性能 $not和$nor需谨慎处理字段缺失 字段操作符: $exists检查字段存在性 $type支持按BSON类型筛选 数组操作符: $all要求包含所有指定元素 $elemMatch匹配满足条件的数组元素 正则表达式:

Wang的专栏 2145

Redis: 集群测试和集群原理

Redis Cluster 没有单机那种 16个数据库 (0 - 15) 数据库的概念了,就是我们已经看不到数据库了。而是分成了 16384 个 slots (槽位) ,每个节点负责其中一部分槽位,槽位的信息存储于每个节点中。2 ) 16384 个 slots (槽位)那我们客户端这边是怎么来操作集群的呢?1 ) SET/GET 命令。2 ) 开启从节点只读模式。

Wang的专栏 2102

Eggjs笔记:详解Mongoose的聚合管道以及populate实现关联查询

基于mongodb的聚合管道 此处忽略之前mongodb在eggjs中需要配置的步骤,前文已有说明,现在开始在controller中调用(应该封装到service中的,仅作为示例) const result = await this.app.mongo.aggregate('order', { pipeline: [ { $lookup: {...

Wang的专栏 2096

MySQL: 数据库监控核心要素与实施策略

数据库监控是确保系统稳定的关键,涵盖服务可用性、性能、主从复制和资源使用等方面。核心监控包括:通过真实连接和查询验证数据库可用性;跟踪QPS、TPS和并发线程数等性能指标;监控主从复制的链路状态和延迟;以及关注磁盘空间等服务器资源。实现方案包括网络连接验证、读写测试和连接数阈值监控。性能监控需持续记录数据趋势,并采用精准方法检测阻塞事务和复制延迟。全面的数据库监控能有效预防故障,保障系统稳定运行。

Wang的专栏 2095
上一篇: PostgreSQL笔记26:MVCC多版本并发控制机制深度解析
下一篇: PostgreSQL笔记28: PostgreSQL ACID 特性与实现机制深度解析
Wang's Blog
博客等级 码龄12年 7438粉丝 2421原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值