PostgreSQL笔记33: 索引底层原理、扫描类型与属性体系深度解析

PostgreSQL 索引原理深度解析:从 B-tree 到 GIN,底层逻辑 + 实战优化 PostgreSQL索引性能优化解析:从JSONB查询加速到10亿级大表处理 PostgreSQL凭借丰富的索引类型(B-tree、GIN、GiST、BRIN)实现高效查询。GIN索引通过倒排结构使JSONB字段查询从10秒降至0.1秒;B-tree在非聚簇设计下比MySQL更快;GiST索引支持地理数据检索;BRIN索引仅需几MB即可处理10亿级数据。每种索引各有适用场景:B-tree用于基础查询,GIN处理JSONB/数组,GiST支持地理/全文检索,BRIN优化大表时序数据。关键要理解底层原理,避免过 阅读详情

纲要

  • PostgreSQL 索引的核心作用
    • 加速条件检索
    • 加速排序(B-tree 索引的有序性)
    • 加速关联(Nestloop 中被驱动表的索引检索)
    • 功能属性:唯一性与排他性约束
  • PostgreSQL 索引扫描类型
    • Index Scan:普通索引扫描,通过索引键定位 TID 后回表
    • Bitmap Scan:位图扫描,将多个索引的结果在内存中合并,优化随机 I/O
    • Index Only Scan:仅索引扫描,依赖 Visibility Map (VM) 跳过回表
  • PostgreSQL 索引访问方法属性(pg_am 与 pg_indexam_has_property)
    • can_order:是否支持排序(仅 B-tree)
    • can_unique:是否支持唯一约束与主键(仅 B-tree)
    • can_multi_col:是否支持多列复合索引
    • can_exclude:是否支持排除约束
    • can_backward:是否支持反向扫描
  • PostgreSQL 索引与表物理存储
    • CLUSTER 命令:按索引顺序重写表数据
    • 堆表(Heap Table)与索引的分离存储
  • PostgreSQL 列级索引属性
    • ASC / DESC:排序方向
    • NULLS FIRST / NULLS LAST:空值排序位置
    • distance_orderable:按操作符结果排序(GiST / SP-GiST)
    • search_array:数组值搜索支持

索引的核心作用

在 PostgreSQL 中,索引是提升查询性能的关键手段。理解索引的底层运行原理,对于创建索引、维护索引以及基于索引优化查询语句都具有重要的指导意义。

加速条件检索

索引最基础的作用是加速条件检索。可以将索引类比为书籍的目录:在没有索引的情况下,查询需要扫描整个表(顺序扫描),如同逐页翻阅整本书;而有了索引之后,查询优化器可以通过索引快速定位到目标数据所在的物理位置,大幅减少磁盘 I/O 次数。

加速排序

B-tree 索引内部维护了键值的有序结构。因此,当查询需要按照某个字段排序并返回结果时(例如 ORDER BY 子句),优化器可以直接利用 B-tree 索引的有序性来避免额外的显式排序操作。

加速关联

在多表关联查询中,Nestloop Join 是一种常见的连接策略。该策略使用驱动表的每一行去被驱动表中查找匹配行。如果被驱动表的关联字段上存在索引,则每次查找都可以通过索引快速完成,从而显著提升关联查询的效率。

功能属性:唯一性与排他性约束

索引还可以承担功能性角色:

  • 唯一性约束:通过创建 UNIQUE INDEX 来保证表中某一列或某几列的值不会重复。
  • 排他性约束:通过 EXCLUDE 约束实现更复杂的排他条件。

关于唯一约束与唯一索引:在 PostgreSQL 中,当为表定义唯一约束(UNIQUE CONSTRAINT)或主键(PRIMARY KEY)时,系统会自动在后端创建一个对应的唯一索引。唯一约束是语义层面的属性定义,而唯一索引是实现该属性的物理机制。两者在功能上基本等价,但在某些场景下(如分区表不支持全局唯一索引时),仍可通过其他方式保证唯一性。

索引扫描类型

PostgreSQL 的查询优化器会根据查询条件、表统计信息和索引类型,选择不同的索引扫描策略。以下是三种主要的索引扫描类型。

Index Scan(普通索引扫描)

Index Scan 是最基础的索引扫描方式。执行流程如下:

  1. 在索引中查找满足 WHERE 条件的键值,获取对应的 TID(Tuple ID,即堆表中数据行的物理地址)。
  2. 根据 TID 访问堆表(Heap),获取完整的行数据并检查其可见性。

由于索引和堆表是分开存储的,因此 Index Scan 的每一次行检索都需要同时访问索引和堆表。此外,索引中匹配的条目在物理上通常相邻,但它们所引用的堆表行可能散布在堆表的各个位置,导致大量的随机 I/O。

-- 创建测试表与索引
CREATE TABLE test_index_scan (
    id SERIAL PRIMARY KEY,
    name TEXT,
    created_at TIMESTAMP DEFAULT NOW()
);

-- 插入测试数据
INSERT INTO test_index_scan (name) SELECT 'user_' || generate_series(1, 10000);

-- 显式创建索引(主键已自动创建唯一索引)
CREATE INDEX idx_test_name ON test_index_scan (name);

-- 强制走 Index Scan(通过关闭 Bitmap Scan 来演示)
SET enable_bitmapscan = OFF;

EXPLAIN (ANALYZE, BUFFERS) SELECT * FROM test_index_scan WHERE name = 'user_5000';

Bitmap Scan(位图扫描)

Bitmap Scan 主要用于解决 Index Scan 中随机 I/O 过多的问题。其执行流程如下:

  1. 扫描索引,将所有满足条件的 TID 收集到一个内存位图(Bitmap)中。
  2. 根据位图对 TID 进行排序(按物理块顺序)。
  3. 按排序后的顺序访问堆表,将顺序 I/O 的收益最大化。

Bitmap Scan 的一个关键优势是能够将多个索引的条件组合起来。例如,当查询包含 WHERE id = ? AND name = ? 时,优化器可以同时使用 id 上的索引和 name 上的索引,将两个位图进行 AND 运算后,再统一访问堆表。

-- 创建第二个索引
CREATE INDEX idx_test_id ON test_index_scan (id);

-- 允许使用 Bitmap Scan
SET enable_bitmapscan = ON;

-- 查询条件涉及两个索引列,优化器可能选择 BitmapAnd
EXPLAIN (ANALYZE, BUFFERS) 
SELECT * FROM test_index_scan WHERE id BETWEEN 100 AND 200 AND name LIKE 'user_1%';

Index Only Scan(仅索引扫描)

Index Only Scan 是 PostgreSQL 为了进一步优化查询性能而引入的扫描方式。其核心思想是:如果索引中已经包含了查询所需的所有列,则无需访问堆表,直接从索引中返回数据。

可见性映射(Visibility Map, VM)

然而,索引条目中并不存储行的可见性信息(MVCC 可见性信息仅存储在堆表中)。为了在无需访问堆表的情况下判断行的可见性,PostgreSQL 为每个堆表维护了一个可见性映射(Visibility Map)。

  • VM 为每个堆页面存储两个比特位。
  • 第一个比特位(all-visible)表示该页面中的所有行对所有当前及未来的事务均可见。
  • VM 仅由 VACUUM 操作设置,任何数据修改操作都会清除对应的 VM 位。

Index Only Scan 的执行逻辑如下:

  1. 在索引中找到满足条件的索引条目,获取对应的堆页面号。
  2. 检查该页面的 VM 位:
    • 若 all-visible 为真,则直接返回索引中存储的数据,无需访问堆表。
    • 若 all-visible 为假,则仍需回表访问堆条目以检查可见性,此时退化为普通的 Index Scan。
判断是否真正回表:Heap Fetches

执行计划中显示 Index Only Scan 并不代表完全没有回表。需要检查 Heap Fetches 字段:若 Heap Fetches 为 0,则说明本次查询真正做到了仅索引扫描;若 Heap Fetches 大于 0,则说明仍有回表操作发生。

-- 创建覆盖索引(包含查询所需的所有列)
CREATE INDEX idx_test_covering ON test_index_scan (name) INCLUDE (id);

-- 该查询只需访问索引中的 name 和 id,无需回表
EXPLAIN (ANALYZE, BUFFERS) SELECT id, name FROM test_index_scan WHERE name = 'user_3000';

-- 观察 Heap Fetches 字段是否为 0

注意:Index Only Scan 要求索引类型本身支持该扫描方式。B-tree 索引始终支持;GiST 和 SP-GiST 索引仅部分操作符类支持;GIN 索引由于索引条目通常只存储原始数据的一部分,因此不支持 Index Only Scan。

索引访问方法属性

PostgreSQL 的索引访问方法(Index Access Methods)具有多种属性,这些属性决定了该方法支持哪些功能和操作。在 PostgreSQL 9.6 之前,这些属性直接存储在 pg_am 系统表中。从 PostgreSQL 9.6 开始,系统引入了 pg_indexam_has_property()、pg_index_has_property() 和 pg_index_column_has_property() 等函数,用于以 SQL 方式查询索引属性。

访问方法级属性

以下查询可以查看当前所有索引访问方法的属性:

SELECT 
    a.amname,
    p.name,
    pg_indexam_has_property(a.oid, p.name) AS property_value
FROM 
    pg_am a,
    unnest(ARRAY[
        'can_order', 
        'can_unique', 
        'can_multi_col', 
        'can_exclude',
        'can_backward'
    ]) AS p(name)
WHERE 
    a.amname IN ('btree', 'hash', 'gist', 'gin', 'spgist', 'brin')
ORDER BY 
    a.amname, p.name;
can_order(是否支持排序)

该属性表示索引访问方法是否支持在创建索引时指定值的排序顺序。在 PostgreSQL 当前版本中,仅有 B-tree 索引支持该属性。

can_unique(是否支持唯一索引)

该属性表示索引访问方法是否支持唯一约束和主键。仅有 B-tree 索引可以声明为唯一索引。

can_multi_col(是否支持多列索引)

该属性表示索引访问方法是否支持在多个列上创建索引。B-tree、GiST、GIN、BRIN 等均支持多列索引。

can_exclude(是否支持排除约束)

该属性表示索引访问方法是否支持 EXCLUDE 约束。B-tree 和 GiST 支持该属性。

can_backward(是否支持反向扫描)

该属性表示索引访问方法是否支持按相反的顺序扫描。B-tree 索引支持反向扫描,这意味着一个按升序创建的索引也可以满足 ORDER BY ... DESC 的查询需求。

索引级属性

除了访问方法级属性外,还可以查询特定索引的属性:

-- 查询指定索引是否支持 CLUSTER
SELECT pg_index_has_property('idx_test_name'::regclass, 'clusterable') AS clusterable;

-- 查询指定索引是否支持 Index Scan
SELECT pg_index_has_property('idx_test_name'::regclass, 'index_scan') AS index_scan;

-- 查询指定索引是否支持 Bitmap Scan
SELECT pg_index_has_property('idx_test_name'::regclass, 'bitmap_scan') AS bitmap_scan;

-- 查询指定索引是否支持反向扫描
SELECT pg_index_has_property('idx_test_name'::regclass, 'backward_scan') AS backward_scan;

列级索引属性

列级属性描述了索引列的具体行为:

-- 查询索引第一列是否支持升序扫描
SELECT pg_index_column_has_property('idx_test_name'::regclass, 1, 'asc') AS asc;

-- 查询索引第一列是否支持降序扫描
SELECT pg_index_column_has_property('idx_test_name'::regclass, 1, 'desc') AS desc;

-- 查询索引第一列是否支持 NULLS FIRST
SELECT pg_index_column_has_property('idx_test_name'::regclass, 1, 'nulls_first') AS nulls_first;

-- 查询索引第一列是否支持 NULLS LAST
SELECT pg_index_column_has_property('idx_test_name'::regclass, 1, 'nulls_last') AS nulls_last;

CLUSTER:按索引顺序重写表数据

PostgreSQL 采用堆表(Heap Table)存储模型,数据行的物理存储顺序与插入顺序相关,通常是无序的。CLUSTER 命令可以按照指定索引的顺序对表进行物理重排。

CLUSTER 的作用

CLUSTER 命令会将表中的数据按照索引键的顺序重新写入存储,使得在逻辑上相邻的数据在物理上也尽可能相邻。这对于范围查询(如 WHERE id BETWEEN ? AND ?)尤为有利,因为相关的数据行可能集中在同一个或少数几个数据块中,从而减少磁盘 I/O。

-- 创建测试表
CREATE TABLE test_cluster (
    id SERIAL PRIMARY KEY,
    value INT
);

-- 插入无序数据
INSERT INTO test_cluster (value) VALUES (5), (1), (8), (3), (2), (7), (4), (6);

-- 创建索引
CREATE INDEX idx_test_cluster_value ON test_cluster (value);

-- 查看当前数据顺序(堆表顺序,非索引顺序)
SELECT * FROM test_cluster;

-- 按 value 索引的顺序重写表
CLUSTER test_cluster USING idx_test_cluster_value;

-- 再次查看数据顺序(现已按 value 排序)
SELECT * FROM test_cluster;

CLUSTER 的注意事项

  • 一次性操作:CLUSTER 仅对存量数据进行重排。后续的 INSERT、UPDATE 操作不会自动维持聚类顺序。
  • 排他锁:CLUSTER 会在表上持有 ACCESS EXCLUSIVE 锁,阻塞所有读写操作。
  • 磁盘空间:CLUSTER 需要额外的磁盘空间来存储表的临时副本。
  • 增量维护:若需定期维持聚类顺序,可以周期性执行 CLUSTER 命令,或通过调整表的 fillfactor 参数来为更新预留空间。

列级排序与空值处理

在创建 B-tree 索引时,可以通过 ASC / DESC 和 NULLS FIRST / NULLS LAST 选项精确控制索引的排序行为。

-- 创建按 value 升序排列,NULL 值排在最后的索引(默认行为)
CREATE INDEX idx_value_asc_nulls_last ON test_cluster (value ASC NULLS LAST);

-- 创建按 value 降序排列,NULL 值排在最前的索引
CREATE INDEX idx_value_desc_nulls_first ON test_cluster (value DESC NULLS FIRST);

-- 创建按 value 升序排列,NULL 值排在最前的索引
CREATE INDEX idx_value_asc_nulls_first ON test_cluster (value ASC NULLS FIRST);
  • ASC 为默认排序方向,NULLS LAST 为 ASC 模式下的默认空值位置。
  • DESC 模式下,NULLS FIRST 为默认空值位置。
  • 索引的排序方向决定了其能够加速的 ORDER BY 子句类型。

API 速览

pg_indexam_has_property

所属库:系统信息函数(System Information Functions)

方法签名:

pg_indexam_has_property(am_oid oid, property_name text) RETURNS boolean

参数说明:

  • am_oid:索引访问方法的 OID(可从 pg_am 系统表中获取)
  • property_name:属性名称,可选值包括 can_order、can_unique、can_multi_col、can_exclude、can_backward 等

返回值:若指定的访问方法拥有该属性,返回 true;否则返回 false

代码示例:

-- 查询 B-tree 是否支持排序
SELECT pg_indexam_has_property(
    (SELECT oid FROM pg_am WHERE amname = 'btree'), 
    'can_order'
) AS btree_can_order;

-- 查询 Hash 索引是否支持唯一约束
SELECT pg_indexam_has_property(
    (SELECT oid FROM pg_am WHERE amname = 'hash'), 
    'can_unique'
) AS hash_can_unique;

pg_index_has_property

所属库:系统信息函数(System Information Functions)

方法签名:

pg_index_has_property(index_oid regclass, property_name text) RETURNS boolean

参数说明:

  • index_oid:索引的 OID 或名称(regclass 类型)
  • property_name:属性名称,可选值包括 clusterable、index_scan、bitmap_scan、backward_scan 等

返回值:若指定的索引拥有该属性,返回 true;否则返回 false

代码示例:

-- 查询指定索引是否支持 CLUSTER
SELECT pg_index_has_property('idx_test_cluster_value'::regclass, 'clusterable');

-- 查询指定索引是否支持 Bitmap Scan
SELECT pg_index_has_property('idx_test_cluster_value'::regclass, 'bitmap_scan');

pg_index_column_has_property

所属库:系统信息函数(System Information Functions)

方法签名:

pg_index_column_has_property(index_oid regclass, column_no integer, property_name text) RETURNS boolean

参数说明:

  • index_oid:索引的 OID 或名称(regclass 类型)
  • column_no:列在索引中的位置(从 1 开始计数)
  • property_name:属性名称,可选值包括 asc、desc、nulls_first、nulls_last、distance_orderable、search_array 等

返回值:若指定索引的指定列拥有该属性,返回 true;否则返回 false

代码示例:

-- 查询索引第一列是否支持升序
SELECT pg_index_column_has_property('idx_test_cluster_value'::regclass, 1, 'asc');

-- 查询索引第一列是否将 NULL 值排在最后
SELECT pg_index_column_has_property('idx_test_cluster_value'::regclass, 1, 'nulls_last');

Demo 简单示例

以下是一个基于 Node.js 和 node-postgres(pg 库)的完整示例,演示了 PostgreSQL 索引的创建、扫描类型观察以及 Heap Fetches 的检查。

运行说明

  1. 确保本地已安装 PostgreSQL(版本 12 或以上)和 Node.js(版本 14 或以上)。
  2. 创建测试数据库 test_db。
  3. 安装依赖:npm init -y && npm install pg
  4. 运行脚本:node index_demo.js

代码说明

const { Client } = require('pg');

// PostgreSQL 连接配置
const client = new Client({
    host: 'localhost',
    port: 5432,
    database: 'test_db',
    user: 'postgres',
    password: 'your_password'
});

async function runDemo() {
    await client.connect();
    console.log('Connected to PostgreSQL');

    try {
        // 1. 创建测试表
        await client.query(`
            DROP TABLE IF EXISTS demo_index CASCADE;
            CREATE TABLE demo_index (
                id SERIAL PRIMARY KEY,
                code VARCHAR(50),
                value INT,
                created_at TIMESTAMP DEFAULT NOW()
            );
        `);
        console.log('Table created');

        // 2. 插入 50000 条测试数据
        await client.query(`
            INSERT INTO demo_index (code, value)
            SELECT 
                'CODE_' || (random() * 1000)::INT,
                (random() * 10000)::INT
            FROM generate_series(1, 50000);
        `);
        console.log('Inserted 50,000 rows');

        // 3. 创建覆盖索引
        await client.query(`
            CREATE INDEX idx_demo_code_value ON demo_index (code) INCLUDE (value);
        `);
        console.log('Covering index created: idx_demo_code_value');

        // 4. 执行 VACUUM 以更新 VM(可见性映射)
        await client.query('VACUUM ANALYZE demo_index;');
        console.log('VACUUM ANALYZE executed');

        // 5. 执行 Index Only Scan 查询并分析
        const explainResult = await client.query(`
            EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
            SELECT code, value FROM demo_index WHERE code = 'CODE_500';
        `);

        const plan = explainResult.rows[0]['QUERY PLAN'];
        console.log('Execution Plan:');
        console.log(JSON.stringify(plan, null, 2));

        // 6. 提取 Heap Fetches 信息
        const planNode = plan[0].Plan;
        if (planNode['Node Type'] === 'Index Only Scan') {
            const heapFetches = planNode['Heap Fetches'];
            console.log(`\\nHeap Fetches: ${heapFetches}`);
            if (heapFetches === 0) {
                console.log('✅ This is a true Index Only Scan — no heap access occurred.');
            } else {
                console.log('⚠️ This Index Only Scan still performed heap fetches — VM bits may not be set.');
            }
        } else {
            console.log(`Scan Type: ${planNode['Node Type']}`);
        }

        // 7. 查询索引属性
        const propsResult = await client.query(`
            SELECT 
                pg_indexam_has_property(a.oid, 'can_order') AS can_order,
                pg_indexam_has_property(a.oid, 'can_unique') AS can_unique,
                pg_index_has_property(i.indexrelid, 'index_scan') AS index_scan,
                pg_index_has_property(i.indexrelid, 'bitmap_scan') AS bitmap_scan,
                pg_index_has_property(i.indexrelid, 'backward_scan') AS backward_scan
            FROM 
                pg_index i
                JOIN pg_am a ON a.oid = i.indexam
            WHERE 
                i.indexrelid = 'idx_demo_code_value'::regclass;
        `);
        console.log('\\nIndex Properties:');
        console.table(propsResult.rows[0]);

        // 8. 列级属性查询
        const colPropsResult = await client.query(`
            SELECT 
                pg_index_column_has_property('idx_demo_code_value'::regclass, 1, 'asc') AS col1_asc,
                pg_index_column_has_property('idx_demo_code_value'::regclass, 1, 'desc') AS col1_desc,
                pg_index_column_has_property('idx_demo_code_value'::regclass, 1, 'nulls_first') AS col1_nulls_first,
                pg_index_column_has_property('idx_demo_code_value'::regclass, 1, 'nulls_last') AS col1_nulls_last;
        `);
        console.log('\\nColumn Properties:');
        console.table(colPropsResult.rows[0]);

    } catch (err) {
        console.error('Error:', err);
    } finally {
        await client.end();
        console.log('\\nConnection closed');
    }
}

runDemo();

对应的 PostgreSQL 原生指令注释

-- ============================================================
-- 等价的 PostgreSQL 原生指令
-- ============================================================

-- 1. 创建表
CREATE TABLE demo_index (
    id SERIAL PRIMARY KEY,
    code VARCHAR(50),
    value INT,
    created_at TIMESTAMP DEFAULT NOW()
);

-- 2. 插入测试数据
INSERT INTO demo_index (code, value)
SELECT 
    'CODE_' || (random() * 1000)::INT,
    (random() * 10000)::INT
FROM generate_series(1, 50000);

-- 3. 创建覆盖索引(INCLUDE 非键列)
CREATE INDEX idx_demo_code_value ON demo_index (code) INCLUDE (value);

-- 4. 更新统计信息和可见性映射
VACUUM ANALYZE demo_index;

-- 5. 查看执行计划并检查 Heap Fetches
EXPLAIN (ANALYZE, BUFFERS) 
SELECT code, value FROM demo_index WHERE code = 'CODE_500';

-- 6. 查询索引访问方法属性
SELECT 
    amname,
    pg_indexam_has_property(a.oid, 'can_order') AS can_order,
    pg_indexam_has_property(a.oid, 'can_unique') AS can_unique
FROM pg_am a
WHERE a.amname = 'btree';

-- 7. 查询特定索引的属性
SELECT 
    pg_index_has_property('idx_demo_code_value'::regclass, 'clusterable') AS clusterable,
    pg_index_has_property('idx_demo_code_value'::regclass, 'index_scan') AS index_scan,
    pg_index_has_property('idx_demo_code_value'::regclass, 'bitmap_scan') AS bitmap_scan;

-- 8. 查询列级属性
SELECT 
    pg_index_column_has_property('idx_demo_code_value'::regclass, 1, 'asc') AS asc,
    pg_index_column_has_property('idx_demo_code_value'::regclass, 1, 'desc') AS desc,
    pg_index_column_has_property('idx_demo_code_value'::regclass, 1, 'nulls_first') AS nulls_first,
    pg_index_column_has_property('idx_demo_code_value'::regclass, 1, 'nulls_last') AS nulls_last;

多语言示例

以下分别提供 Go、Python 和 Java 的完整示例代码,功能与前述 Node.js 示例完全一致:创建测试表、插入 50000 条数据、创建覆盖索引、执行 VACUUM ANALYZE、执行 EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON) 查询并解析 Heap Fetches,以及查询索引属性。

Go 示例

使用 pgx 驱动(推荐)或 lib/pq,本示例采用 pgx/v5。

运行说明:

  • 安装依赖:go mod init demo && go get github.com/jackc/pgx/v5
  • 修改数据库连接字符串中的密码。
  • 运行:go run main.go

代码:

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"

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

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

	// 连接配置
	connStr := "postgres://postgres:your_password@localhost:5432/test_db"
	conn, err := pgx.Connect(ctx, connStr)
	if err != nil {
		log.Fatal("Unable to connect:", err)
	}
	defer conn.Close(ctx)

	fmt.Println("Connected to PostgreSQL")

	// 1. 创建表
	_, err = conn.Exec(ctx, `
		DROP TABLE IF EXISTS demo_index CASCADE;
		CREATE TABLE demo_index (
			id SERIAL PRIMARY KEY,
			code VARCHAR(50),
			value INT,
			created_at TIMESTAMP DEFAULT NOW()
		);
	`)
	if err != nil {
		log.Fatal("Create table error:", err)
	}
	fmt.Println("Table created")

	// 2. 插入 50000 条数据
	_, err = conn.Exec(ctx, `
		INSERT INTO demo_index (code, value)
		SELECT 
			'CODE_' || (random() * 1000)::INT,
			(random() * 10000)::INT
		FROM generate_series(1, 50000);
	`)
	if err != nil {
		log.Fatal("Insert error:", err)
	}
	fmt.Println("Inserted 50,000 rows")

	// 3. 创建覆盖索引
	_, err = conn.Exec(ctx, `
		CREATE INDEX idx_demo_code_value ON demo_index (code) INCLUDE (value);
	`)
	if err != nil {
		log.Fatal("Create index error:", err)
	}
	fmt.Println("Covering index created: idx_demo_code_value")

	// 4. VACUUM ANALYZE
	_, err = conn.Exec(ctx, `VACUUM ANALYZE demo_index;`)
	if err != nil {
		log.Fatal("VACUUM error:", err)
	}
	fmt.Println("VACUUM ANALYZE executed")

	// 5. 执行 EXPLAIN 并解析 JSON
	var explainJSON string
	err = conn.QueryRow(ctx, `
		EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
		SELECT code, value FROM demo_index WHERE code = 'CODE_500';
	`).Scan(&explainJSON)
	if err != nil {
		log.Fatal("Explain error:", err)
	}

	// 解析 JSON
	var explainResult []map[string]interface{}
	if err := json.Unmarshal([]byte(explainJSON), &explainResult); err != nil {
		log.Fatal("JSON parse error:", err)
	}

	fmt.Println("Execution Plan:")
	planJSON, _ := json.MarshalIndent(explainResult, "", "  ")
	fmt.Println(string(planJSON))

	// 6. 提取 Heap Fetches
	if len(explainResult) > 0 {
		planNode, ok := explainResult[0]["Plan"].(map[string]interface{})
		if ok {
			nodeType, _ := planNode["Node Type"].(string)
			if nodeType == "Index Only Scan" {
				heapFetches, _ := planNode["Heap Fetches"].(float64)
				fmt.Printf("\nHeap Fetches: %v\n", heapFetches)
				if heapFetches == 0 {
					fmt.Println("✅ True Index Only Scan — no heap access.")
				} else {
					fmt.Println("⚠️ Index Only Scan with heap fetches.")
				}
			} else {
				fmt.Printf("Scan Type: %s\n", nodeType)
			}
		}
	}

	// 7. 查询索引属性
	var canOrder, canUnique, indexScan, bitmapScan, backwardScan bool
	err = conn.QueryRow(ctx, `
		SELECT 
			pg_indexam_has_property(a.oid, 'can_order'),
			pg_indexam_has_property(a.oid, 'can_unique'),
			pg_index_has_property(i.indexrelid, 'index_scan'),
			pg_index_has_property(i.indexrelid, 'bitmap_scan'),
			pg_index_has_property(i.indexrelid, 'backward_scan')
		FROM 
			pg_index i
			JOIN pg_am a ON a.oid = i.indexam
		WHERE 
			i.indexrelid = 'idx_demo_code_value'::regclass;
	`).Scan(&canOrder, &canUnique, &indexScan, &bitmapScan, &backwardScan)
	if err != nil {
		log.Fatal("Property query error:", err)
	}

	fmt.Println("\nIndex Properties:")
	fmt.Printf("can_order: %v, can_unique: %v, index_scan: %v, bitmap_scan: %v, backward_scan: %v\n",
		canOrder, canUnique, indexScan, bitmapScan, backwardScan)

	// 8. 列级属性
	var colAsc, colDesc, colNullsFirst, colNullsLast bool
	err = conn.QueryRow(ctx, `
		SELECT 
			pg_index_column_has_property('idx_demo_code_value'::regclass, 1, 'asc'),
			pg_index_column_has_property('idx_demo_code_value'::regclass, 1, 'desc'),
			pg_index_column_has_property('idx_demo_code_value'::regclass, 1, 'nulls_first'),
			pg_index_column_has_property('idx_demo_code_value'::regclass, 1, 'nulls_last');
	`).Scan(&colAsc, &colDesc, &colNullsFirst, &colNullsLast)
	if err != nil {
		log.Fatal("Column property error:", err)
	}

	fmt.Println("\nColumn Properties:")
	fmt.Printf("asc: %v, desc: %v, nulls_first: %v, nulls_last: %v\n",
		colAsc, colDesc, colNullsFirst, colNullsLast)

	fmt.Println("\nDone.")
}

Python 示例

使用 psycopg2(或 asyncpg,本示例采用 psycopg2)。

运行说明:

  • 安装依赖:pip install psycopg2-binary
  • 修改数据库连接参数(数据库名、用户、密码)。
  • 运行:python demo.py

代码:

import psycopg2
import json

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

    try:
        # 1. 创建表
        cur.execute("""
            DROP TABLE IF EXISTS demo_index CASCADE;
            CREATE TABLE demo_index (
                id SERIAL PRIMARY KEY,
                code VARCHAR(50),
                value INT,
                created_at TIMESTAMP DEFAULT NOW()
            );
        """)
        print("Table created")

        # 2. 插入数据
        cur.execute("""
            INSERT INTO demo_index (code, value)
            SELECT 
                'CODE_' || (random() * 1000)::INT,
                (random() * 10000)::INT
            FROM generate_series(1, 50000);
        """)
        print("Inserted 50,000 rows")

        # 3. 创建覆盖索引
        cur.execute("""
            CREATE INDEX idx_demo_code_value ON demo_index (code) INCLUDE (value);
        """)
        print("Covering index created: idx_demo_code_value")

        # 4. VACUUM ANALYZE
        cur.execute("VACUUM ANALYZE demo_index;")
        print("VACUUM ANALYZE executed")

        # 5. EXPLAIN
        cur.execute("""
            EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
            SELECT code, value FROM demo_index WHERE code = 'CODE_500';
        """)
        explain_json = cur.fetchone()[0]
        explain_result = json.loads(explain_json)

        print("Execution Plan:")
        print(json.dumps(explain_result, indent=2))

        # 6. 解析 Heap Fetches
        if explain_result:
            plan_node = explain_result[0].get("Plan", {})
            node_type = plan_node.get("Node Type")
            if node_type == "Index Only Scan":
                heap_fetches = plan_node.get("Heap Fetches", -1)
                print(f"\nHeap Fetches: {heap_fetches}")
                if heap_fetches == 0:
                    print("✅ True Index Only Scan — no heap access.")
                else:
                    print("⚠️ Index Only Scan with heap fetches.")
            else:
                print(f"Scan Type: {node_type}")

        # 7. 索引属性
        cur.execute("""
            SELECT 
                pg_indexam_has_property(a.oid, 'can_order'),
                pg_indexam_has_property(a.oid, 'can_unique'),
                pg_index_has_property(i.indexrelid, 'index_scan'),
                pg_index_has_property(i.indexrelid, 'bitmap_scan'),
                pg_index_has_property(i.indexrelid, 'backward_scan')
            FROM 
                pg_index i
                JOIN pg_am a ON a.oid = i.indexam
            WHERE 
                i.indexrelid = 'idx_demo_code_value'::regclass;
        """)
        row = cur.fetchone()
        print("\nIndex Properties:")
        print(f"can_order: {row[0]}, can_unique: {row[1]}, index_scan: {row[2]}, bitmap_scan: {row[3]}, backward_scan: {row[4]}")

        # 8. 列级属性
        cur.execute("""
            SELECT 
                pg_index_column_has_property('idx_demo_code_value'::regclass, 1, 'asc'),
                pg_index_column_has_property('idx_demo_code_value'::regclass, 1, 'desc'),
                pg_index_column_has_property('idx_demo_code_value'::regclass, 1, 'nulls_first'),
                pg_index_column_has_property('idx_demo_code_value'::regclass, 1, 'nulls_last');
        """)
        col_row = cur.fetchone()
        print("\nColumn Properties:")
        print(f"asc: {col_row[0]}, desc: {col_row[1]}, nulls_first: {col_row[2]}, nulls_last: {col_row[3]}")

    except Exception as e:
        print("Error:", e)
    finally:
        cur.close()
        conn.close()
        print("\nDone.")

if __name__ == "__main__":
    main()

Java 示例

使用 JDBC 官方驱动 org.postgresql:postgresql,并利用 jackson-databind 解析 JSON。

运行说明:

  • Maven 依赖:
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.6.0</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.15.2</version>
    </dependency>
    
  • 修改数据库 URL、用户名、密码。
  • 运行 Main.main()。

代码:

import com.fasterxml.jackson.databind.ObjectMapper;
import java.sql.*;
import java.util.List;
import java.util.Map;

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

        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            System.out.println("Connected to PostgreSQL");

            try (Statement stmt = conn.createStatement()) {
                // 1. 创建表
                stmt.execute("DROP TABLE IF EXISTS demo_index CASCADE;");
                stmt.execute("""
                    CREATE TABLE demo_index (
                        id SERIAL PRIMARY KEY,
                        code VARCHAR(50),
                        value INT,
                        created_at TIMESTAMP DEFAULT NOW()
                    );
                """);
                System.out.println("Table created");

                // 2. 插入数据
                stmt.execute("""
                    INSERT INTO demo_index (code, value)
                    SELECT 
                        'CODE_' || (random() * 1000)::INT,
                        (random() * 10000)::INT
                    FROM generate_series(1, 50000);
                """);
                System.out.println("Inserted 50,000 rows");

                // 3. 创建覆盖索引
                stmt.execute("CREATE INDEX idx_demo_code_value ON demo_index (code) INCLUDE (value);");
                System.out.println("Covering index created: idx_demo_code_value");

                // 4. VACUUM ANALYZE
                stmt.execute("VACUUM ANALYZE demo_index;");
                System.out.println("VACUUM ANALYZE executed");

                // 5. EXPLAIN
                String explainJson;
                try (ResultSet rs = stmt.executeQuery("""
                        EXPLAIN (ANALYZE, BUFFERS, FORMAT JSON)
                        SELECT code, value FROM demo_index WHERE code = 'CODE_500';
                    """)) {
                    rs.next();
                    explainJson = rs.getString(1);
                }

                ObjectMapper mapper = new ObjectMapper();
                List<Map<String, Object>> explainResult = mapper.readValue(explainJson, List.class);
                System.out.println("Execution Plan:");
                System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(explainResult));

                // 6. 解析 Heap Fetches
                if (!explainResult.isEmpty()) {
                    Map<String, Object> planNode = (Map<String, Object>) explainResult.get(0).get("Plan");
                    String nodeType = (String) planNode.get("Node Type");
                    if ("Index Only Scan".equals(nodeType)) {
                        Number heapFetches = (Number) planNode.get("Heap Fetches");
                        System.out.println("\nHeap Fetches: " + heapFetches);
                        if (heapFetches != null && heapFetches.longValue() == 0) {
                            System.out.println("✅ True Index Only Scan — no heap access.");
                        } else {
                            System.out.println("⚠️ Index Only Scan with heap fetches.");
                        }
                    } else {
                        System.out.println("Scan Type: " + nodeType);
                    }
                }

                // 7. 索引属性
                try (ResultSet rs = stmt.executeQuery("""
                        SELECT 
                            pg_indexam_has_property(a.oid, 'can_order'),
                            pg_indexam_has_property(a.oid, 'can_unique'),
                            pg_index_has_property(i.indexrelid, 'index_scan'),
                            pg_index_has_property(i.indexrelid, 'bitmap_scan'),
                            pg_index_has_property(i.indexrelid, 'backward_scan')
                        FROM 
                            pg_index i
                            JOIN pg_am a ON a.oid = i.indexam
                        WHERE 
                            i.indexrelid = 'idx_demo_code_value'::regclass;
                    """)) {
                    if (rs.next()) {
                        System.out.println("\nIndex Properties:");
                        System.out.printf("can_order: %b, can_unique: %b, index_scan: %b, bitmap_scan: %b, backward_scan: %b%n",
                            rs.getBoolean(1), rs.getBoolean(2), rs.getBoolean(3),
                            rs.getBoolean(4), rs.getBoolean(5));
                    }
                }

                // 8. 列级属性
                try (ResultSet rs = stmt.executeQuery("""
                        SELECT 
                            pg_index_column_has_property('idx_demo_code_value'::regclass, 1, 'asc'),
                            pg_index_column_has_property('idx_demo_code_value'::regclass, 1, 'desc'),
                            pg_index_column_has_property('idx_demo_code_value'::regclass, 1, 'nulls_first'),
                            pg_index_column_has_property('idx_demo_code_value'::regclass, 1, 'nulls_last');
                    """)) {
                    if (rs.next()) {
                        System.out.println("\nColumn Properties:");
                        System.out.printf("asc: %b, desc: %b, nulls_first: %b, nulls_last: %b%n",
                            rs.getBoolean(1), rs.getBoolean(2), rs.getBoolean(3), rs.getBoolean(4));
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("\nDone.");
    }
}

多语言对比表格

特性 / 语言Node.js (pg)Go (pgx)Python (psycopg2)Java (JDBC)
主要驱动/库node-postgres (pg)pgx/v5psycopg2PostgreSQL JDBC Driver
连接方式new Client() + connect()pgx.Connect()psycopg2.connect()DriverManager.getConnection()
执行 SQLclient.query()conn.Exec() / conn.QueryRow()cur.execute()stmt.execute() / stmt.executeQuery()
JSON 解析原生 JSON.parse() / JSON.stringify()encoding/json 标准库json 标准库Jackson 或 Gson(示例使用 Jackson)
异步支持原生 async/await原生 context 和 goroutine同步(可使用 asyncpg 支持异步)同步(可使用 Reactive 或 R2DBC 异步)
类型安全动态类型强类型动态类型强类型
连接池内置(可通过 Pool)pgxpool 提供psycopg2.pool 或 SQLAlchemyHikariCP 等第三方
执行计划解析解析 JSON 对象解析 map[string]interface{}解析 dict/list解析 Map / List
错误处理try/catchif err != niltry/excepttry/catch
依赖管理npmgo modpipMaven/Gradle

共同点:

  • 所有示例均使用 PostgreSQL 原生的 EXPLAIN (FORMAT JSON) 输出,解析方式大同小异。
  • 均支持 VACUUM ANALYZE 更新可见性映射。
  • 均能查询 pg_indexam_has_property 等系统函数获取索引属性。
  • 核心数据库操作(DDL、DML)完全相同,仅客户端驱动和语言语法不同。

差异点:

  • Go 和 Java 为静态类型语言,需要对 JSON 进行类型断言或使用结构体/类映射。
  • Node.js 和 Python 动态类型,解析更灵活但缺少编译时检查。
  • 异步处理方式各异:Node.js 原生异步,Go 使用 goroutine+channel,Python 同步(可改用 asyncpg),Java 同步(可改用响应式驱动)。
  • 连接字符串格式略有不同(JDBC URL 和 libpq 风格)。

技术点总结

  • 覆盖索引(Covering Index) :通过 INCLUDE 子句将非键列存入索引,使 Index Only Scan 成为可能。
  • 可见性映射(Visibility Map) :VACUUM 更新 VM 后,Index Only Scan 才能发挥真正的性能优势。
  • Heap Fetches 指标:执行计划中 Heap Fetches 为 0 才表示真正的仅索引扫描。
  • 索引属性查询:通过 pg_indexam_has_property、pg_index_has_property 和 pg_index_column_has_property 函数,可以动态获取索引的能力信息。

官方文档

参考链接

总结

本文深入探讨了 PostgreSQL 索引的底层运行原理,系统性地梳理了索引的核心作用、三种主要的索引扫描类型(Index Scan、Bitmap Scan、Index Only Scan)及其适用场景,详细阐述了 Visibility Map (VM) 在 Index Only Scan 中的关键作用以及 Heap Fetches 指标的真实含义。

同时,本文全面介绍了 PostgreSQL 索引访问方法的属性体系,涵盖访问方法级属性(can_order、can_unique、can_multi_col、can_exclude、can_backward)、索引级属性(clusterable、index_scan、bitmap_scan、backward_scan)以及列级属性(asc、desc、nulls_first、nulls_last),并通过 pg_indexam_has_property()、pg_index_has_property() 和 pg_index_column_has_property() 函数提供了可编程的属性查询能力。

此外,本文还介绍了 CLUSTER 命令在物理存储层面的优化作用以及 B-tree 索引的排序与空值处理选项。通过完整的 Node.js 示例代码,演示了覆盖索引的创建、Index Only Scan 的执行分析以及索引属性的动态查询,为读者提供了理论与实践相结合的技术参考。

PostgreSQL底层原理:MVCC、WAL与查询优化全解析 关系型数据库(RDBMS)的核心能力源于ACID事务保障与多版本并发控制(MVCC)机制,而PostgreSQL作为开源RDBMS的标杆,其可靠性与高性能并非来自黑盒封装,而是由WAL日志、共享缓冲区、事务快照、元组可见性检查等底层组件协同实现。理解MVCC如何通过xmin/xmax字段管理数据版本,掌握WAL写前日志如何确保崩溃恢复的一致性,结合EXPLAIN ANALYZE中的Buffers指标诊断I/O瓶颈,是构建高并发、低延迟数据库系统的工程基础。本文深入PostgreSQL进程模型、内存结构与物理 阅读详情

相关推荐

PostgreSQL:索引与查询优化

索引就像数据库的"超能力药剂",但错误的使用方式可能导致性能雪崩:某知名社交平台曾因错误添加GIN索引导致写入延迟增加300%,直接造成用户发帖量下降15%。这警示我们,索引的威力与风险并存。 本文将带您深入PostgreSQL的索引迷宫,不仅解析各种索引的运行机制,更将揭示查询优化器的决策逻辑。通过20+个真实故障案例的逆向分析,带您了解: 如何为JSONB数据设计复合GIN索引 B-Tree索引的隐藏排序特性如何影响分页查询 并行查询执行计划的特征识别

在技术的广袤天地里,本博客如精准罗盘。剖析前沿科技,深掘代码奥秘,以精炼笔触,带您穿越复杂技术迷宫,速达知识彼岸。 2986

PostgreSQL专栏 :索引原理与应用深度解析

- 场景:电商订单表,设计合适的索引-- 常见查询模式:-- 1. 查询客户的所有订单:WHERE customer_id =?-- 2. 查询特定状态的订单:WHERE status =?-- 3. 查询时间范围内的订单:WHERE order_date BETWEEN?AND?-- 4. 客户最近订单:WHERE customer_id =?-- 推荐索引设计索引类型原理:B-tree、Hash、GIN、GiST、BRIN的结构和适用场景复合索引设计。

苦哈哈的 C++ 程序员,写这个技术博客不是为了装逼,也不是为了立人设,是为了收割流量 1295

深度解读:PostgreSQL执行计划的高效优化指南

作为开发者或数据库管理员,查询优化在日常工作中占据了非常重要的位置。特别是在处理大规模数据的应用场景时,SQL查询的执行效率直接影响到系统的整体性能。而PostgreSQL,作为一款功能强大、开源的关系型数据库管理系统,提供了非常详细的查询执行计划,通过它我们可以精准地分析SQL语句的执行过程,发现性能瓶颈并进行相应优化。本篇文章将详细解读PostgreSQL执行计划的各个要素,帮助你掌握如何使用EXPLAIN命令,结合实际案例,深入理解数据库的运行机制。

喵手的博客 2871

PostgreSQL 数据库索引原理深度解析

讲解postgresql数据库索引结构,尤其与mysql数据库索引的区别

dylan的博客 603

GCC调试黑科技:-fomit-frame-pointer优化下如何保证栈回溯能力

本文深入探讨了GCC编译器在启用`-fomit-frame-pointer`优化选项时如何保证栈回溯能力。通过分析`.eh_frame`段的结构与DWARF调试信息,提供了从编译选项配置到实战调试的完整解决方案,帮助开发者在性能优化与调试能力之间找到最佳平衡点。

u6v7w8x的博客 907

十一、机器学习进阶知识:径向基神经网络(RBFNN)的实现(Python,附源码及数据集)

本文对径向基神经网络(RBFNN)的理论基础及建模步骤进行介绍,之后使用Python实现基于RBFNN的数据预测,通俗易懂,适合新手学习,附源码及实验数据集。

weixin_42051846的博客 1万+

PostgreSQL B+树索引原理:从磁盘优化到查询加速的深度解析

数据库索引是提升查询性能的核心技术,其本质是通过预排序的数据结构加速数据检索。B+树作为最广泛应用的索引结构,通过多路平衡设计将树高控制在3-4层,极大减少了磁盘I/O次数。其技术价值在于高效平衡了等值查询、范围查询与数据维护成本,特别适合磁盘块设备的访问特性。在应用场景中,B+树通过叶子节点的双向链表支持高效范围扫描,同时利用页内二分查找实现快速定位。PostgreSQL的B+树索引在此基础上实现了HOT更新优化和MVCC兼容,通过监控索引大小、填充率等指标,结合REINDEX和ANALYZE维护手段,可

weixin_30268921的博客 527

【SABRE 3D, SABRE 3DxT,LAM设备】2025年9月12日-生产期间维护用的双重隔离

生产期间维护用的双重隔离。该程序用于准备将一个或多个双工位设置为维护状态,同时SABRE 3D/3DxT/3D Scimitar保持生产运行。其目的是在不中断生产的情况下将双工位与运行中的设备隔离。

weixin_46735260的博客 574

PostgreSQL索引深度解析:谢飞机闯关记 —— 大厂面试官的“友好”考验

谢飞机:“嗯……PostgreSQL是一个开源数据库,对吧?像MySQL一样,但它更‘高级’一些,比如支持JSON数据类型、地理信息存储什么的。面试官:“不错,基本点抓到了。不过它的特点远不止这些,你还知道哪些?谢飞机挠头:“呃……好像还有事务支持、MVCC机制、复杂查询能力等等?面试官点头:“很好,看来你做了些功课。那我们继续。谢飞机:“就是只给满足条件的数据建立索引,比如只给活跃用户建索引,这样可以减小索引体积,提升查询效率。面试官:“嗯,理解正确。那么如何创建一个?

wujie10的专栏 1201

脑影像分析入门:如何用一般线性模型(GLM)处理fMRI数据?

本文为神经影像研究入门者详细解析了如何运用一般线性模型(GLM)处理fMRI数据。文章从GLM的核心原理出发,逐步拆解了从实验设计、构建稳健设计矩阵、模型估计与统计推断,到高阶分析及多重比较校正的全流程,并指出了常见陷阱与结果解读要点,是掌握fMRI数据分析基础的关键指南。

weixin_29091445的博客 400

Vue - ElementUI el-date-picker 周日期选择器(type=week),获取开始和结束的年月日、当前选择日期的周数等,支持将选择范围修改为 “礼拜一到礼拜日“(提供示例源码)

vue,element-ui,DatePicker日期选择器,vue2elementUI日期选择器选择一周7天的数据格式怎么拿到,el-date-picker设置type等于week后怎么设置format属性,format和value-format怎么设置,范围选择器周,一周,选择一周7天时间,el-date-picker,周,type=week,周选择器,面板显示周数,显示第n年第n周数据,详细示例代码教程,elementui官方文档,vue2element-UI日期时间选择器怎么设置week周选

🏆 前端领域优质创作者 6439

PostgreSQL枚举类型深度解析:pgx EnumCodec实现原理与实战指南

你是否在Go语言开发PostgreSQL应用时遇到过枚举类型处理效率低下的问题?是否想知道如何避免枚举值字符串重复分配导致的内存浪费?本文将深入解析pgx驱动中EnumCodec的实现原理,并通过实战案例展示如何高效处理PostgreSQL枚举类型,帮助你提升应用性能并优化内存使用。读完本文,你将掌握EnumCodec的工作机制、使用方法以及最佳实践。 ## 枚举类型与EnumCodec概述 ...

gitblog_00099的博客 740

智能驾驶域控制器的“终极大考”:解析EOL测试的核心价值与技术实践

在智能驾驶技术高速发展的今天,域控制器(Domain Controller)作为车辆“大脑”的核心部件,承担着传感器数据处理、决策规划、执行控制等关键任务。然而,这个集成了数十个芯片、运行着数百万行代码的复杂系统,如何确保其出厂前的功能安全与性能稳定?答案就藏在被称为“生产线最后一道防线”的EOL(End of Line)测试中。

汽车电子软件领域知识分享 705

Active Record 与 PostgreSQL:官方指南深度解析——从专属数据类型到全文搜索与约束实现

本指南以 Rails 仓库 [guides/source/active_record_postgresql.md](https://link.gitcode.com/i/e8efb8b83d42e38ea75f53f1bfbc33f4) 为主线,系统讲解 Active Record PostgreSQL 适配器的专属能力,包括数据类型映射、UUID 主键、包含列索引、可延迟外键/唯一约束、排他约束

gitblog_00341的博客 821

【nvidia-B200 生产环境】NVIDIA 570.124.06+Cuda12.8 Ubuntu22.04 nccl-test 一键部署脚本(免修改复制即用)

目录1. 初始化与日志记录2. 安装基础工具与配置更新3. NVIDIA 驱动安装 570.124.064. 安装 Mellanox OFED 24.10-2.1.8.05. 安装 CUDA 12.86. 安装 NCCL 和测试7. 配置 rc.local 和其他优化8. B200 单独步骤9. 最终测试与重启10. 完整生产脚本,复制即可直接使用总结 功能: 设置日志目录和日志文件路径。 使用 命令将标准输出和错误输出同时写入日志文件和终端。 记录脚本开始时间,并计算开始的秒数

jundao1997的专栏 2799

SCI投稿Title Page、Cover letter、Highlights、Manuscript模板

(简要介绍一下论文的创新性). I hope this paper issuitable for “Journal Name”.版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。原文链接:https://blog.youkuaiyun.com/weixin_48615832/article/details/120317336。

qq_15963745的博客 3702

PostgreSQL 18 主流程源码深度解析与实战应用

PostgreSQL 18 主流程采用模块化、解耦、智能优化等设计思想,实现了高性能、高可靠、高扩展性的数据库系统。通过逐步剖析源码、口诀速记、业务场景结合,开发者不仅能知其然,更能知其所以然,为实际业务落地和高阶应用奠定坚实基础。速记口诀总览认证查配置,解析树先行;优化看统计,计划选路径;执行器分节点,事务写日志;存储多样化,并发锁机制。如需深入源码学习与项目应用,建议结合官方文档与社区实践,持续关注 PostgreSQL 内核新特性与架构演进。

IT技术学习与工作笔记分享 1868

贝叶斯统计 [韦来生编]

贝叶斯(Bayes)统计是近几十年来迅速发展起来的数理统计的一个重要分支。贝叶斯方法与经典统计方法的主要不同之处是进行统计推断时除了利用样本信息外,还要利用参数的先验信息,因此可以提高统计推断或统计决策的效果。它在经济、金融、生物、医学、自然科学和社会科学等许多领域具有广泛的应用。贝叶斯方法的研究已渗透到了统计学的几乎所有领域。作者在给中国科学技术大学概率论与数理统计专业本科生开设的“数理统计”课程中有一章专门讲授“贝叶斯方法和统计决策理论”,近十年来也给中国科学技术大学概率统计专业研究生开设过几次“贝叶斯分析”课。本书是在对过去讲稿的内容作了适当的增减和调整的基础上完成的。  本书共分六章。第1章是绪论,介绍了贝叶斯统计的若干基本概念,同时对必要的数理统计的基础知识有重点地作了回顾。第二章介绍了确定先验分布的若干可供选择的方法。第三章介绍了几类常见统计模型参数的后验分布的主要结果和计算方法。第四章和第五章分别介绍了“贝叶斯统计推断”和“贝叶斯统计决策”的内容。第六章介绍了贝叶斯统计计算的若干方法,包括蒙特卡洛方法和马尔可夫链蒙特卡洛(MCMC)方法以及相关统计软件的简介。本书的每一章附右大量的例题和习题,书末附有常用的几个表格和部分习题解答供读者查用。  大约可在54学时内讲授本书的第1章至第六章的主要内容,教师可根据学时需要选讲本书第1章至第六章的部分内容。书中标“*”号的章节可略去不讲,留给读者作为阅读材料。第六章例题中的R代码和部分例题的数据文件等可从作者个人主页下载。  作者在编写本书的过程中参考了J.O.Berger教授的《统计决策论及贝叶斯分析》和茆诗松教授编写的《贝叶斯统计》等书中的一些内容,以及《贝叶斯分析》一书中由张伟平博士编写的有关贝叶斯统计计算的一些内容。在此表示衷心感谢。  本书编写准备过程中,中国科学技术大学统计与金融系的几个研究生帮助完成了本书部分中文的录入和排版,作者对他们的辛勤工作表示真诚的感谢。高等教育出版社为本书的出版给予了大力支持。在此一并致谢。

PostgreSQL数值类型深度解析:从整数到浮点数的选型与避坑指南

在数据库设计与开发中,数值类型的选择是构建稳定、高效系统的基石。理解不同数值类型(如整数、浮点数、精确小数)的底层存储原理和计算机制,对于保障数据一致性、优化查询性能至关重要。整数类型(SMALLINT、INTEGER、BIGINT)通过固定字节存储,范围明确且运算高效,常用于ID、计数等场景;浮点数(REAL、DOUBLE PRECISION)采用IEEE 754标准实现近似存储,适用于科学计算、统计分析等容忍误差的领域,其硬件加速特性带来显著性能优势。而NUMERIC(或DECIMAL)类型以可变长度、

weixin_33696822的博客 1465

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

PostgreSQL索引深度解析:大厂Java求职者必备面试指南

提示:这一轮主要是为了考察候选人对PostgreSQL基本概念的理解,特别是对索引的基本认识和分类。提示:这一轮开始进入更深层次的技术讨论,重点在于候选人对索引底层实现和事务机制的理解。提示:本阶段主要考察候选人在实际项目中如何运用索引优化技巧,以及他们对复杂查询的理解能力。PostgreSQL是一款开源的关系型数据库管理系统,以其高度可扩展性、支持复杂查询、外键、触发器、视图、事务等功能而闻名。它还支持多种高级特性,如JSONB数据类型、全文检索、空间数据支持(PostGIS)、流复制等。

wujie10的专栏 690

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

PostgreSQL全文搜索性能瓶颈?RUM索引技术深度解析与实战指南

当PostgreSQL的全文搜索功能遇到性能瓶颈时,开发团队常常面临两难选择:要么接受缓慢的查询响应时间,要么投入大量资源进行复杂的查询优化。传统GIN索引虽然提供了基础的全文搜索能力,但在处理复杂排序、短语匹配和时间敏感查询时往往力不从心。这正是RUM索引技术诞生的背景——一个旨在解决PostgreSQL全文搜索性能瓶颈的革命性解决方案。 ## 全文搜索的痛点:为什么传统方案不够用? 在深入

gitblog_00319的博客 471

PostgreSQL执行计划深度解析:从扫描、连接到实战优化

执行计划是数据库查询优化的核心诊断工具,它揭示了SQL语句在数据库引擎中的具体执行路径。其原理基于成本估算模型,优化器会评估不同访问路径(如顺序扫描、索引扫描)和连接策略(如嵌套循环、哈希连接、归并连接)的成本,从而选择最优方案。理解执行计划的技术价值在于,它能将性能优化从经验猜测转变为数据驱动的实证分析,帮助开发者精准定位瓶颈,避免盲目添加索引或调整硬件。在应用场景上,无论是处理慢查询、诊断统计信息不准、还是优化复杂多表连接,执行计划都是不可或缺的利器。本文聚焦PostgreSQL,通过解读执行计划中的关

weixin_29722783的博客 299

PostgreSQL 索引内部机制与表重建深度解析

索引膨胀来源:MVCC 死元组 → 死亡索引条目 → VACUUM 清理但不收缩文件解决索引膨胀:├─ 日常:VACUUM 自动清理死亡条目(不收缩)├─ 重建:REINDEX CONCURRENTLY(不阻塞,PG12+)└─ 预防:合理设置 fillfactor,利用 HOT 更新减少索引写入。

无双-放飞的自我 613

PostgreSQL 慢查询深度剖析:从 EXPLAIN ANALYZE 到索引重构

PostgreSQL 慢查询优化的核心法则:先看执行计划(EXPLAIN ANALYZE),再查统计信息(pg_stat_user_tables),最后调整索引和配置(VACUUM + 覆盖索引)。获取真实执行计划和 I/O 统计;检查确认统计信息是否为最新;若死元组过多,执行;若索引未被使用,分析谓词选择性并考虑创建覆盖索引;使用EXPLAIN验证新索引是否被优化器采纳。

iymei4986533030的博客 3330

<postgreSQL查询优化深度探索>之第六章:扫描路径

文章目录一、代价1.代价基准单位2.启动代价和整体代价3.表达式代价的计算二、路径PATH1.Path结构体2.并行参数3.参数化路径4.PathKey三、make_one_rel函数四、普通标的扫描路径1.顺序扫描2.索引扫描3.位图扫描五、小结 一、代价 1.代价基准单位 扫描路径是对基表进行扫描的执行路径,针对不同的基表有不同的扫描路径 eg:对于堆表有顺序扫描SeqScan、针对索引有索引扫描IndexScan和位图扫描BitmapScan,子查询有子查询扫描SubqueryScan、对于通用表达

喜欢打篮球的普通人 2888

PostgreSQL笔记49:向量检索核心算法、索引调优与过滤策略深度解析

本文介绍了 PostgreSQL 中 pgvector 扩展的向量检索技术,包括核心算法(暴力检索和近似最近邻)、过滤策略(预过滤、后过滤和迭代扫描),以及 HNSW 和 IVFFlat 两种索引类型的实现与优化。文章详细讲解了关键参数配置、性能优化手段(如降维、量化、部分索引等),并对比了不同方法的适用场景,为高效实现向量相似度搜索提供了实用指南。

Wang的专栏 310

ZomboDB访问方法解析:PostgreSQL索引API的深度集成

在当今数据驱动的世界中,**ZomboDB访问方法**为PostgreSQL带来了革命性的文本搜索能力。这个强大的扩展通过深度集成Elasticsearch作为PostgreSQL的索引类型,让开发者能够在传统的关系型数据库环境中享受现代搜索引擎的强大功能。 ## 什么是ZomboDB访问方法? ZomboDB访问方法是一个完全原生的PostgreSQL扩展,它使用PostgreSQL的**索

gitblog_01054的博客 345
上一篇: PostgreSQL笔记32:死锁的诊断、处理与加锁原则
下一篇: PostgreSQL笔记34:索引优化策略全景解析——从B-tree到HOT的核心原理与实践
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、付费专栏及课程。

余额充值