PostgreSQL笔记34:索引优化策略全景解析——从B-tree到HOT的核心原理与实践

MySQL索引(一)——B-Tree索引介绍 本文章介绍了MySQL的B-Tree索引结构 阅读详情

纲要

  • B-tree 索引
    • 适用场景与操作符
    • 索引项大小限制(约三分之一页)
  • Hash 索引
    • 仅支持等值查询
    • WAL日志记录问题(PostgreSQL 10以下版本)
  • BRIN 索引(Block Range Index)
    • 块范围摘要信息(minmax 操作符类)
    • 适用时序数据场景
    • 索引体积优势
  • GIN 索引(Generalized Inverted Index)
    • 倒排索引原理与全文检索
    • 数组、JSONB等复合类型支持
    • 更新代价较高
  • GiST 索引(Generalized Search Tree)
    • 地理空间数据与最近邻搜索
    • 有损索引与回表确认
  • 函数索引(Expression Index)
    • 索引表达式而非列值
    • 统计信息独立存储
  • 部分索引(Partial Index)
    • 条件索引缩小索引体积
  • 多字段索引(复合索引)
    • 最左前缀匹配原则
  • 唯一索引(Unique Index)
    • 唯一性约束实现机制
    • 死元组与表膨胀风险
  • correlation 相关度
    • 逻辑顺序与物理顺序的关联程度
    • 对索引扫描代价的影响
  • Index-Only Scan 与 Visibility Map
    • VM文件的作用与 all-visible 标记
    • 回表确认的必要性
  • HOT(Heap-Only Tuple)
    • 非索引列更新时的优化机制
    • 条件:同页存储 + 不更新索引键

B-tree 索引

PostgreSQL 默认创建的索引类型即为 B-tree 索引,通过 CREATE INDEX 命令(不指定 USING 子句)或显式指定 USING BTREE 均可创建。B-tree 索引适用于可排序数据的等值查询和范围查询,当索引列涉及 <、<=、=、>=、> 等比较运算符时,查询规划器均会考虑使用 B-tree 索引。此外,BETWEEN、IN 以及 IS NULL / IS NOT NULL 条件同样可以利用 B-tree 索引。

B-tree 索引还可用于排序检索(ORDER BY)以及前缀匹配的模式查询(如 col LIKE 'foo%')。

索引项大小限制:B-tree 索引的索引项不能超过大约一页的三分之一(在应用 TOAST 压缩后计算)。若索引字段长度超过该限制(例如在 8KB 页大小的数据库中,单个索引项超过约 2.7KB),创建索引时将报错。此时需考虑使用其他索引类型或对字段进行哈希处理。

-- 创建 B-tree 索引(默认)
CREATE INDEX idx_test_id ON test (id);

-- 显式指定 B-tree
CREATE INDEX idx_test_name ON test USING BTREE (name);

-- 范围查询示例
EXPLAIN SELECT * FROM test WHERE id BETWEEN 100 AND 200;

Hash 索引

Hash 索引存储从索引列值派生的 32 位哈希码,因此仅支持等值比较(= 运算符)。

WAL 日志问题:在 PostgreSQL 10 之前,Hash 索引操作不进行 WAL 日志记录。这意味着数据库崩溃后,Hash 索引可能需要通过 REINDEX 重建;同时,由于缺少 WAL 日志,Hash 索引也无法复制到备库。PostgreSQL 10 开始对 Hash 索引实现了 WAL 日志记录,但在实际生产环境中,B-tree 索引因其通用性仍是大多数场景的首选。

-- 创建 Hash 索引(注意版本限制)
CREATE INDEX idx_test_hash ON test USING HASH (id);

-- 仅支持等值查询
EXPLAIN SELECT * FROM test WHERE id = 100;

BRIN 索引(Block Range Index)

BRIN 全称为 Block Range Index(块范围索引)。其核心思想是:对于每个块范围(一组物理上相邻的数据页),索引存储该范围内索引列的摘要信息(如最小值和最大值)。查询时,BRIN 索引通过摘要信息快速排除不可能包含匹配数据的块范围,从而大幅减少需要扫描的数据量。

BRIN 索引是有损索引(lossy index)——索引返回的是整个块范围内的所有元组,由查询执行器负责重新检查并过滤不匹配的行。

-- 创建 BRIN 索引,默认 pages_per_range = 128
CREATE INDEX idx_test_brin ON test USING BRIN (created_at);

-- 指定块范围大小(页数),数值越大索引越大但精度越高
CREATE INDEX idx_test_brin_custom ON test USING BRIN (created_at) WITH (pages_per_range = 64);

适用场景:

  • 时序数据(仅插入、极少更新删除)
  • 索引列与物理存储位置存在自然相关性(如自增 ID、时间戳列)
  • 超大规模表(数十亿行级别)

优势:索引体积远小于 B-tree 索引,维护开销低。

摘要触发:BRIN 索引的摘要信息可通过 VACUUM 手动或自动触发,也可通过 brin_summarize_new_values() 和 brin_summarize_range() 函数主动触发。

GIN 索引(Generalized Inverted Index)

GIN 全称为 Generalized Inverted Index(通用倒排索引)。GIN 索引存储(键,倒排列表)对,其中倒排列表是键出现的行 ID 集合。每个键值只存储一次,因此对于同一键多次出现的情况非常紧凑。

GIN 索引内部构建于 B-tree 之上,但键是索引项的元素而非索引项本身。

-- 全文检索 GIN 索引(tsvector 类型)
CREATE INDEX idx_docs_gin ON documents USING GIN (to_tsvector('english', body));

-- 数组类型 GIN 索引
CREATE INDEX idx_tags_gin ON articles USING GIN (tags);

-- JSONB 类型 GIN 索引
CREATE INDEX idx_data_gin ON events USING GIN (payload jsonb_ops);

适用场景:

  • 全文检索(tsvector / tsquery)
  • 数组元素搜索(@>、&& 等操作符)
  • JSONB 文档查询(@>、?、?| 等操作符)
  • 多列任意组合查询(如 EAV 模型)

注意事项:GIN 索引的更新代价较高。类比书籍目录——每次数据变更都需要更新倒排列表,因此写入密集型场景需谨慎评估。

GiST 索引(Generalized Search Tree)

GiST 全称为 Generalized Search Tree(通用搜索树)。它本身并非一种具体的索引类型,而是一个基础设施(infrastructure),可在此基础上实现多种索引策略,如 B-tree、R-tree 等。

-- 创建 GiST 索引(几何数据类型)
CREATE INDEX idx_points_gist ON locations USING GIST (geom);

-- 最近邻搜索(KNN):查找离目标点最近的 10 个点
SELECT * FROM places ORDER BY location <-> point '(101,456)' LIMIT 10;

适用场景:

  • 地理空间数据(点、线、面)
  • 最近邻搜索(KNN,使用 <-> 等距离运算符)
  • 范围类型、网络地址类型等

有损索引特性:GiST 索引可能产生假匹配(false matches),需要回表确认实际行数据以消除假匹配。

函数索引(Expression Index)

函数索引(也称表达式索引)的索引键并非表的列,而是表达式或函数的结果。这在业务查询需要对列进行函数变换后再过滤时尤为有用。

-- 创建测试表
CREATE TABLE test (
    id INT,
    info TEXT
);

INSERT INTO test VALUES (1, 'hello'), (2, 'world'), (3, 'Hello');

-- 普通索引无法支持 upper(info) 查询
CREATE INDEX idx_info ON test (info);

-- 不走索引(函数套层)
EXPLAIN SELECT * FROM test WHERE upper(info) = 'HELLO';

-- 创建函数索引
CREATE INDEX idx_info_upper ON test (upper(info));

-- 收集统计信息
ANALYZE test;

-- 现在走索引扫描
EXPLAIN SELECT * FROM test WHERE upper(info) = 'HELLO';

统计信息:函数索引与普通表一样,拥有独立的统计信息,可通过 pg_stats 视图查询。

-- 查看函数索引的统计信息
SELECT tablename, attname, n_distinct, correlation
FROM pg_stats
WHERE tablename = 'idx_info_upper';

部分索引(Partial Index)

部分索引(条件索引)仅针对表中满足特定条件的行创建索引,可显著减小索引体积并降低维护开销。

-- 仅对 id < 500 的行创建索引
CREATE INDEX idx_test_partial ON test (id) WHERE id < 500;

-- 查看索引大小对比
SELECT
    relname,
    pg_size_pretty(pg_relation_size(oid)) AS size
FROM pg_class
WHERE relname IN ('idx_test_partial', 'idx_test_id');

适用场景:

  • 表中仅小部分数据被频繁查询(如仅查询活跃用户、未删除记录等)
  • 索引体积敏感场景
  • 写入频繁但仅需索引部分数据的场景

多字段索引(复合索引)

复合索引在单次索引扫描中可同时过滤多个条件,相比多个单列索引的组合效率更高。需注意最左前缀匹配原则:复合索引 (a, b, c) 可有效支持 a、(a, b)、(a, b, c) 的查询条件,但对 b 或 c 单独查询的效率有限。

-- 创建复合索引
CREATE INDEX idx_test_multi ON test (id, info);

-- 可走索引
EXPLAIN SELECT * FROM test WHERE id = 100 AND info = 'hello';

-- 也可走索引(最左前缀)
EXPLAIN SELECT * FROM test WHERE id = 100;

唯一索引(Unique Index)

唯一索引用于强制字段值的唯一性约束。

-- 创建唯一索引
CREATE UNIQUE INDEX idx_test_unique ON test (id);

-- 插入重复值会报错
INSERT INTO test VALUES (1, 'duplicate');  -- 成功
INSERT INTO test VALUES (1, 'duplicate2'); -- 违反唯一约束

死元组与表膨胀风险:PostgreSQL 唯一索引的重复检测机制在底层通过先插入、再检测、最后回滚的方式实现。当插入重复值时,数据库会先写入一条新元组,然后通过索引检测到重复,再将事务标记为无效。这一过程会留下死元组(dead tuple),若频繁插入重复值,将导致表不断膨胀。建议在应用层提前做唯一性校验,减少无效插入。

相关度(Correlation)

correlation 是 pg_stats 视图中的一个字段,表示列值的逻辑顺序与物理存储顺序的关联程度。取值范围为 -1 到 1:

  • 接近 1:逻辑顺序与物理顺序高度一致(如自增主键)
  • 接近 -1:逻辑顺序与物理顺序高度相反
  • 接近 0:逻辑顺序与物理顺序无关
-- 查看列的相关度
SELECT
    tablename,
    attname,
    correlation
FROM pg_stats
WHERE tablename = 'test';

对查询性能的影响:当 correlation 接近 1 时,范围查询(如 WHERE id BETWEEN 1 AND 100)可高效利用索引连续扫描少量数据块;当 correlation 接近 0 时,同一范围查询可能需要扫描大量分散的数据块,导致 I/O 开销显著增加。在索引扫描代价计算中,correlation 是重要输入参数之一。

Index-Only Scan 与 Visibility Map

Index-Only Scan 是 PostgreSQL 9.2 引入的优化特性。其核心条件是查询所需的所有列均包含在索引中(覆盖索引)。但仅有此条件还不够——索引本身不存储元组的可见性信息(事务 ID、删除标记等),因此即使索引包含所有所需列,PostgreSQL 仍需确认元组对当前事务是否可见。

Visibility Map(VM) 解决了这一问题。VM 为每个数据页维护两个标志位,其中 all-visible 位表示该页上所有元组对所有事务均可见。当查询走 Index-Only Scan 时:

  1. 从索引读取匹配的条目(含 ctid)
  2. 检查对应数据页的 VM 标志位
  3. 若 all-visible 为真,直接从索引返回数据(无需回表)
  4. 否则,回表确认元组可见性
-- 创建覆盖索引(INCLUDE 子句)
CREATE INDEX idx_test_cover ON test (id) INCLUDE (info);

-- 查询仅需索引列 + INCLUDE 列,可能走 Index-Only Scan
EXPLAIN (ANALYZE, BUFFERS) SELECT id, info FROM test WHERE id BETWEEN 1 AND 100;

VM 文件由 VACUUM 维护。VACUUM 扫描数据页,若某页所有元组均对所有事务可见,则将该页标记为 all-visible。

HOT(Heap-Only Tuple)

HOT(Heap-Only Tuple)是 PostgreSQL 为降低 UPDATE 操作索引维护开销而引入的优化机制。

HOT 更新的触发条件:

  1. 不更新任何索引列(包括表达式索引和部分索引的列)
  2. 新元组与旧元组位于同一数据页(页内有足够空闲空间)

满足上述条件时,HOT 更新不创建新的索引条目,而是通过版本链(tuple chain)定位最新元组。

-- 查看 HOT 更新统计
SELECT
    schemaname,
    tablename,
    n_tup_hot_upd,
    n_tup_upd,
    round(n_tup_hot_upd::numeric / NULLIF(n_tup_upd, 0) * 100, 2) AS hot_update_pct
FROM pg_stat_all_tables
WHERE tablename = 'test';

无法使用 HOT 的场景:

  • 更新涉及索引列
  • 新元组跨页存储(页内空间不足)

可通过调整表的 fillfactor 参数(默认 100)增加页内空闲空间,从而提高 HOT 更新的命中率。

API 速览

本节梳理博客中涉及的 PostgreSQL 核心 SQL 命令与系统视图。

CREATE INDEX

语法:

CREATE [ UNIQUE ] INDEX [ CONCURRENTLY ] [ name ] ON table_name
    [ USING method ]
    ( { column_name | ( expression ) } [ COLLATE collation ] [ opclass ] [ ASC | DESC ] [ NULLS { FIRST | LAST } ] [, ...] )
    [ INCLUDE ( column_name [, ...] ) ]
    [ WITH ( storage_parameter = value [, ... ] ) ]
    [ WHERE predicate ]

参数说明:

参数说明
UNIQUE创建唯一索引,强制唯一性约束
CONCURRENTLY不阻塞表的 INSERT/UPDATE/DELETE 操作(代价更高、耗时更长)
USING method索引类型:BTREE(默认)、HASH、BRIN、GIN、GiST、SP-GiST
INCLUDE覆盖索引,将非搜索列存储在索引中(PostgreSQL 11+)
WITH存储参数(如 BRIN 的 pages_per_range)
WHERE部分索引的条件谓词

EXPLAIN

语法:

EXPLAIN [ ( option [, ...] ) ] sql_statement;

常用选项:

选项说明
ANALYZE实际执行 SQL 并返回执行统计信息
BUFFERS显示缓冲区使用情况(需配合 ANALYZE)
VERBOSE显示详细的计划信息
COSTS显示启动成本和总成本(默认开启)

系统视图

视图用途
pg_stats列级统计信息,含 correlation、n_distinct、most_common_vals 等
pg_class表和索引的元数据,含 relname、relpages、reltuples
pg_stat_all_tables表级统计信息,含 n_tup_hot_upd、n_tup_upd 等
pg_indexes索引定义信息

BRIN 管理函数

函数说明适用版本
brin_summarize_new_values(regclass)摘要所有未摘要的块范围PostgreSQL 9.5+
brin_summarize_range(regclass, bigint)摘要包含指定页面的块范围PostgreSQL 9.5+
brin_desummarize_range(regclass, bigint)移除指定块范围的摘要PostgreSQL 11+

完整 Demo 示例

以下示例基于 Node.js + pg 驱动,演示各类索引的创建、查询与性能对比。

运行说明

环境要求:

  • Node.js 16+
  • PostgreSQL 14+(建议 16 以上版本以支持全部特性)
  • 已创建测试数据库

安装依赖:

mkdir pg-index-demo && cd pg-index-demo
npm init -y
npm install pg

创建测试表并插入数据(可直接在 psql 中执行,或通过 Node.js 脚本执行):

-- 创建测试表
CREATE TABLE orders (
    id SERIAL PRIMARY KEY,
    order_no TEXT NOT NULL,
    customer_id INT NOT NULL,
    amount NUMERIC(10,2),
    status TEXT DEFAULT 'pending',
    created_at TIMESTAMP DEFAULT now(),
    tags TEXT[] DEFAULT '{}',
    payload JSONB DEFAULT '{}'
);

-- 插入 100 万条测试数据(示例,实际可调整)
INSERT INTO orders (order_no, customer_id, amount, status, created_at, tags, payload)
SELECT
    'ORD-' || generate_series,
    (random() * 10000)::INT,
    (random() * 1000)::NUMERIC(10,2),
    (ARRAY['pending', 'paid', 'shipped', 'completed', 'cancelled'])[floor(random() * 5) + 1],
    now() - (random() * INTERVAL '365 days'),
    ARRAY['tag' || floor(random() * 10)::TEXT, 'tag' || floor(random() * 10)::TEXT],
    jsonb_build_object('source', (ARRAY['web', 'app', 'api'])[floor(random() * 3) + 1])
FROM generate_series(1, 1000000);

Demo 脚本(index-demo.js):

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

const client = new Client({
    host: 'localhost',
    port: 5432,
    database: 'testdb',
    user: 'postgres',
    password: 'postgres'
});

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

    // 1. B-tree 索引(默认)
    console.log('\n=== 1. B-tree Index ===');
    await client.query('DROP INDEX IF EXISTS idx_orders_btree');
    await client.query('CREATE INDEX idx_orders_btree ON orders (customer_id)');
    const btRes = await client.query(
        'EXPLAIN (ANALYZE, BUFFERS, COSTS) SELECT * FROM orders WHERE customer_id = 1234'
    );
    console.log(btRes.rows.map(r => r['QUERY PLAN']).join('\n'));

    // 2. 函数索引
    console.log('\n=== 2. Expression Index ===');
    await client.query('DROP INDEX IF EXISTS idx_orders_upper');
    await client.query('CREATE INDEX idx_orders_upper ON orders (upper(order_no))');
    const funcRes = await client.query(
        'EXPLAIN (ANALYZE, BUFFERS, COSTS) SELECT * FROM orders WHERE upper(order_no) = \'ORD-12345\''
    );
    console.log(funcRes.rows.map(r => r['QUERY PLAN']).join('\n'));

    // 3. 部分索引
    console.log('\n=== 3. Partial Index ===');
    await client.query('DROP INDEX IF EXISTS idx_orders_partial');
    await client.query('CREATE INDEX idx_orders_partial ON orders (status) WHERE status = \'pending\'');
    const partialRes = await client.query(
        'EXPLAIN (ANALYZE, BUFFERS, COSTS) SELECT * FROM orders WHERE status = \'pending\''
    );
    console.log(partialRes.rows.map(r => r['QUERY PLAN']).join('\n'));

    // 4. GIN 索引(数组)
    console.log('\n=== 4. GIN Index (Array) ===');
    await client.query('DROP INDEX IF EXISTS idx_orders_gin');
    await client.query('CREATE INDEX idx_orders_gin ON orders USING GIN (tags)');
    const ginRes = await client.query(
        'EXPLAIN (ANALYZE, BUFFERS, COSTS) SELECT * FROM orders WHERE tags @> ARRAY[\'tag1\']'
    );
    console.log(ginRes.rows.map(r => r['QUERY PLAN']).join('\n'));

    // 5. BRIN 索引(时序)
    console.log('\n=== 5. BRIN Index ===');
    await client.query('DROP INDEX IF EXISTS idx_orders_brin');
    await client.query('CREATE INDEX idx_orders_brin ON orders USING BRIN (created_at) WITH (pages_per_range = 128)');
    const brinRes = await client.query(
        'EXPLAIN (ANALYZE, BUFFERS, COSTS) SELECT * FROM orders WHERE created_at BETWEEN \'2025-01-01\' AND \'2025-01-31\''
    );
    console.log(brinRes.rows.map(r => r['QUERY PLAN']).join('\n'));

    // 6. 覆盖索引(Index-Only Scan)
    console.log('\n=== 6. Covering Index (Index-Only Scan) ===');
    await client.query('DROP INDEX IF EXISTS idx_orders_cover');
    await client.query('CREATE INDEX idx_orders_cover ON orders (customer_id) INCLUDE (amount, status)');
    const coverRes = await client.query(
        'EXPLAIN (ANALYZE, BUFFERS, COSTS) SELECT customer_id, amount, status FROM orders WHERE customer_id BETWEEN 100 AND 200'
    );
    console.log(coverRes.rows.map(r => r['QUERY PLAN']).join('\n'));

    // 7. 查看相关度
    console.log('\n=== 7. Correlation ===');
    const corrRes = await client.query(
        `SELECT attname, correlation FROM pg_stats WHERE tablename = 'orders' AND attname IN ('id', 'customer_id', 'created_at')`
    );
    console.table(corrRes.rows);

    // 8. 查看 HOT 更新统计
    console.log('\n=== 8. HOT Updates ===');
    await client.query('UPDATE orders SET amount = amount + 1 WHERE id < 10000');
    const hotRes = await client.query(
        `SELECT n_tup_upd, n_tup_hot_upd,
         round(n_tup_hot_upd::numeric / NULLIF(n_tup_upd, 0) * 100, 2) AS hot_pct
         FROM pg_stat_all_tables WHERE tablename = 'orders'`
    );
    console.table(hotRes.rows);

    await client.end();
}

runDemo().catch(console.error);

技术点总结

技术点对应代码
B-tree 索引创建与等值查询CREATE INDEX ... ON orders (customer_id)
函数索引(表达式索引)CREATE INDEX ... ON orders (upper(order_no))
部分索引(条件索引)CREATE INDEX ... ON orders (status) WHERE status = 'pending'
GIN 索引(数组倒排)CREATE INDEX ... USING GIN (tags)
BRIN 索引(时序数据)CREATE INDEX ... USING BRIN (created_at) WITH (pages_per_range = 128)
覆盖索引与 Index-Only ScanCREATE INDEX ... INCLUDE (amount, status)
相关度查询pg_stats.correlation
HOT 更新统计pg_stat_all_tables.n_tup_hot_upd

官方文档

参考链接

总结

本文系统梳理了 PostgreSQL 的七种核心索引类型(B-tree、Hash、BRIN、GIN、GiST、函数索引、部分索引),以及复合索引、唯一索引的特性和适用场景。

重点阐述了 correlation 相关度对索引扫描代价的影响、Index-Only Scan 与 Visibility Map 的协同机制、以及 HOT 优化如何降低 UPDATE 操作的索引维护开销。

全文基于 PostgreSQL 官方文档,所有代码示例均可在 PostgreSQL 14+ 环境中验证运行。索引选型需结合数据特征、查询模式与写入负载综合评估——没有"万能索引",只有"最适合的索引"。

POSTGRESQL (PG) 性能优化之索引使用技巧(复合多列索引、局部索引、表达式索引、覆盖索引、唯一索引) 索引优化技巧:复合索引(多列索引)、部分索引(局部索引)、表达式索引、覆盖索引和唯一索引,概念描述以及使用实例,优缺点等 阅读详情

相关推荐

Postgresql杂谈 04—Postgresql中的五种常规索引

一、索引的分类 Postgresql中索引一共分为5种,每一种都有它合适的应用场景,我们在使用时要根据不同业务的特点,选择合适的索引,这样才能加快sql语句的查询效率。下面,我们将就每种不同的索引,介绍其特点。 2.1 B树索引 这是我们最常用的索引结构了,B树是一颗多路平衡查找树,每个节点包含多个键,而且这些键对应的指针一般指向磁盘上同一个数据块,目的是一次从磁盘读取一个数据块,减少磁盘IO操作,加快查询的效率。 B树索引的结构如下所示: ...

Hyman的博客 3319

B树索引

B-Tree索引是最常见的索引结构,如oracle和mongodb的索引都是B-Tree,而mysql的索引类型是B+Tree 一、B树索引的结构 B-树索引是基于二叉树结构的。B-树索引结构有3个基本组成部分:根节点、分支节点和叶子节点。其中根节点位于索引结构的最顶端,而叶子节点位于索引结构的最底端,中间为分子节点。 叶子节点(Leaf node):包含条目直接指向表里的数据行。 分支节点(Branch node):包含的条目指向索引里其他的分支节点或者是叶子节点。 根...

XiaoGong1688的博客 5799

PostgreSQL 16 与 MySQL 8.0 性能对比:10万条数据下 5 类典型查询耗时实测

本文深度对比了PostgreSQL 16与MySQL 8.0在10万条数据下的性能表现,涵盖简单查询、多表连接、子查询、聚合计算和并发更新等5类典型场景。测试显示PostgreSQL在复杂查询和并行处理上优势明显,而MySQL在简单OLTP场景保持竞争力。文章还提供了针对两种数据库的性能优化建议和技术选型框架,帮助开发者根据业务需求做出合理选择。

weixin_30904593的博客 420

索引类型之B-Tree索引

Mysql的B-Tree索引

Stephen GS的博客 1610

PostgreSQL vs MySQL:核心差异、选型逻辑与迁移实战指南

关系型数据库是现代应用的数据基石,而PostgreSQL与MySQL则是其中最具代表性的开源选择。两者在架构设计上存在本质区别:MySQL以轻量、高效和成熟的复制机制见长,适合高并发短查询的互联网场景;PostgreSQL则追求功能完整与SQL标准,在复杂查询、JSON处理、索引类型和扩展性上具备显著优势。理解这些底层原理,有助于开发者根据业务的数据特征、查询模式和团队技术储备做出合理决策。在实际工程中,无论是从MySQL迁移至PostgreSQL的路径规划,还是高可用架构的搭建,都需要结合具体场景权衡。本

weixin_34208185的博客 367

PostGIS 3.4 空间索引实战:毫秒级查询全国省市行政区划的3种优化方案

本文深入解析PostGIS 3.4中GIST、SPGIST和BRIN三种空间索引技术,通过实战案例展示如何实现全国省市行政区划数据的毫秒级查询。针对不同数据分布和查询场景,提供详细的优化策略和性能对比,帮助开发者突破大规模空间数据处理的性能瓶颈,特别适合GIS系统开发者和数据库管理员参考。

weixin_33845881的博客 465

PostgreSQL考试通关指南:核心特性、索引优化与高可用架构全解析

本文系统解析PostgreSQL的核心优势与实战技巧,涵盖其架构特性、索引优化(B-Tree、GIN、部分索引等)、执行计划分析、高可用部署及选型场景。强调从“会写SQL”到掌握MVCC机制、索引选型与性能调优的进阶路径,助力开发者构建高效、可扩展的数据系统。

345

MySQL 8.0 与 PostgreSQL 16 主键约束对比:自增、UUID与业务键的5个设计陷阱

本文深入对比了MySQL 8.0与PostgreSQL 16在主键约束设计上的差异,重点分析了自增ID、UUID和业务键在实际应用中的5个关键陷阱,包括写入热点、索引膨胀、分库分表冲突等。通过性能数据和优化建议,帮助开发者避免常见的主键设计误区,提升数据库性能和扩展性。

weixin_30820077的博客 318

PostgreSQL HOT更新:频繁更新表的索引膨胀根因与fillfactor调优

PostgreSQL表没怎么变大,索引却膨胀了几倍——这是频繁更新的表最常见的性能退化模式。HOT(Heap-Only Tuple)更新是PostgreSQL针对这个问题的核心优化:当UPDATE不修改任何索引列,且新行能放在同一页时,可以跳过所有索引更新。但HOT有两个前提条件,任何一个被破坏,索引膨胀就会加速。本文从HOT的机制出发,拆解三个最常见的HOT失效场景,给出fillfactor调优策略和n_tup_hot_upd监控方法。

DBAxiaoye的博客 199

【项目实战-MATLAB】:基于机器学习的虹膜识别系统设计

虹膜识别主要包括虹膜定位、虹膜裁剪、极坐标变换、归一化、HOG特征提取、虹膜识别六个主要步骤。本系统采用MATLAB作为开发工具实现 主要算法:霍夫变换预处理 HOG特征提取 SVM进行识别

qq_45047246的博客 6538

使用MLP解决OCR问题(OpenCV)(下)

分类模型:        分类模型涉及的一个比较关键的问题就是输出的10维向量是如何与具体的类别挂钩的。实际上:10维向量的每一位都代表一类,在对于训练集的表达中,如果输入数据是0,则10维向量的第一位赋值为1,其余均为0。即0对应[1,0,0,0,0,0,0,0,0,0]。MLP模型训练完成后,就需要对用户输入的数据所属类别进行判定。这时得到的输出数据基本不可能是正好的所属类为1,其他位置为0

qq_18515405的专栏 1907

深入解析PostgreSQL Page结构及其优化实践

数据库存储引擎的核心在于其Page结构,它是数据持久化和高效访问的基础。PostgreSQL通过精心设计的Page布局实现了快速数据定位、事务隔离和崩溃恢复。Page内部的行指针数组和HeapTupleHeader结构共同支撑了MVCC机制,而WAL日志与Page的LSN协作确保了数据一致性。在实际应用中,理解Page填充率、TOAST存储策略和索引页分裂等机制,对于优化查询性能和存储效率至关重要。通过pageinspect工具和系统视图,开发者可以深入监控和调优Page状态,解决如热点Page、空间浪费等

weixin_33416697的博客 288

Copula函数实战:如何用MATLAB计算气象干旱与农业干旱的联合概率?

本文提供了一份基于MATLAB的Copula函数实战指南,详细阐述了如何利用该多变量联合分布函数计算气象干旱与农业干旱的联合概率及条件概率。通过从数据预处理、边缘分布拟合到Copula模型选择与检验的完整流程,帮助环境科学与农业气象研究者量化干旱事件的关联风险,为风险评估提供定量依据。

weixin_29213655的博客 431

PostgreSQL与MySQL核心差异深度解析:架构、特性与选型指南

数据库作为现代应用的核心组件,其底层架构与设计哲学决定了系统的扩展性、稳定性与适用场景。从技术原理层面看,数据库通过不同的并发控制、存储引擎和查询优化机制来保障数据一致性与访问效率。在众多开源数据库中,PostgreSQL和MySQL凭借各自鲜明的技术特点与活跃的生态,成为企业级应用中最常见的选择。PostgreSQL以其严格的SQL标准支持、丰富的内置数据类型和强大的扩展能力著称,尤其擅长处理复杂查询、地理空间数据和半结构化JSONB数据,其多进程架构为高稳定性与复杂分析场景提供了坚实基础。而MySQL则

weixin_34101229的博客 338

从原理到调试:一次搞懂Camera Sensor的曝光、增益与帧率三角关系

本文深入解析Camera Sensor中曝光、增益与帧率之间的动态平衡关系,揭示低光环境下帧率下降的根本原因。通过实际调试案例和底层公式分析,帮助开发者理解如何优化相机参数设置,实现画质与流畅度的最佳平衡。

weixin_31310913的博客 432

在Tricore上移植μC/OS-III——1.1 AURIX Development Studio的使用

本文主要介绍AURIX Development Studio软件的安装和使用方法。 英飞凌针对Tricore系列单片机推出了一款免费的软件——AURIX™ Development Studio,集成了iLLD库,内置Tasking compiler和Tasking Debugger,但这两个模块只能用于非商业用途,可供学习和试用。iLLD库是英飞凌为TC2XX和TC3XX Tricore系列单片机提供的底层驱动库,有完善的使用手册,和较好的移植性。 可从下面的链接获取软件安装包,软件安装好后,新建工程时会自

weixin_42967006的博客 2291

深入PostgreSQL内核算法:从MVCC到查询优化器的性能调优实战

数据库索引与查询优化是提升应用性能的核心技术。其原理在于通过高效的数据结构(如B-Tree、哈希表)组织数据,减少磁盘I/O,从而加速数据检索。理解这些底层机制的技术价值在于,开发者能够针对特定场景选择最优索引类型,并编写出更高效的SQL语句。在应用场景上,无论是高并发的在线事务处理(OLTP),还是复杂分析查询(OLAP),合理的索引设计和查询优化都能显著提升系统吞吐和响应速度。本文聚焦PostgreSQL的MVCC机制和查询优化器,通过分析多版本并发控制如何实现读写不阻塞,以及优化器如何基于统计信息选择

Eric.Net的专栏 332

Yolov5自定义图片训练测试及模型调优(详细过程)

Yolov5自定义数据训练及测试1. 图像数据获取、标注2. 数据集划分及准备3. 配置训练参数,准备训练:3.1 修改data/训练配置xxx.yaml文件:3.2 修改models模型配置xxx.yaml文件4. 开始训练5. 测试训练后的模型: 1. 图像数据获取、标注 如果自定义训练自己的数据集,首先需要数据标注,至于数据来源可以是下载现有的开发数据集、拍照、爬虫等等。 如果是拍照、爬虫等获取的数据图片,需要进行数据标注,这个过程可以使用工具来做,但也需要一些手动操作 。。并且图片越多越好,如果

HowieXue 薛永浩的博客 10万+

MySQL还是PostgreSQL?架构、特性与选型决策全解析

关系型数据库是现代应用的数据基石,选型直接影响架构演进与长期运维成本。MySQL与PostgreSQL作为两大主流开源数据库,其核心差异根植于底层架构与并发控制原理:InnoDB的插件式存储引擎与PG的多进程共享内存模型,决定了各自在MVCC实现、索引类型、扩展机制上的不同走向。理解这些原理,才能评估其技术价值:MySQL擅长标准OLTP与成熟的主从生态,PostgreSQL则在复杂查询、JSONB、GIS及向量检索等场景表现突出。从新手常见的mysql安装教程到postgresql安装教程的体验差异中,也

weixin_34221276的博客 420

计算机毕设项目一:基于卷积神经网络、resnet、VGG化妆品分类识别 完整代码数据可直接运行

计算机毕设项目一:基于卷积神经网络、resnet、VGG化妆品分类识别 完整代码数据可直接运行

656

【STM32+K210项目】基于K210智能人脸识别+车牌识别系统(完整工程资料源码)

随着时代的进步和科技的迅猛发展,人们对居住安全的重视程度不断提升,对安全防范系统的需求也日益增强。在安防系统中,门禁系统占据着重要的位置。传统的门禁控制方式主要依靠钥匙、密码和门禁卡等,这些方式易于被破解和复制,安全性较低。然而,近年来,生物识别技术的迅速发展和成熟,为解决这一问题提供了新的方案。生物识别技术具备便携性、非易失性、不易遗忘以及难以盗用等优点,逐渐被引入到门禁系统中。传统的小区门禁系统往往依赖人工管理,存在一定的局限性,难以满足用户多样化的需求,也浪费了人力资源。

嵌入式技术开发 1万+

PostgreSQL MVCC 机制实战:一次 UPDATE 为什么留下两个行版本

订单表逻辑行数没有增长,每天却执行数千万次状态刷新;一段时间后 Heap 和索引都变大,查询读到的仍然只有每个订单的最新一行。问题不在重复 INSERT,而在 PostgreSQL 的UPDATE会写入新行版本,旧版本要等到不再被任何相关快照需要后才能清理。MVCC 新版本解释表为什么变大;HOT 只决定这次更新能否少维护一轮索引,并不会把 UPDATE 变成原地覆盖。

weixin_42426485的博客 329

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

PostgreSQL 查询机制实战:Index Only Scan 为什么仍然回表一万次

慢 SQL 已经命中,需要的列也都放进了INCLUDE,但仍接近返回行数。问题不在“索引没有覆盖”,而在覆盖只完成了一半。索引决定值能否从索引取得,Visibility Map 决定 MVCC 可见性是否还要去 heap 验证。节点叫 Index Only Scan,不承诺实际零次 heap fetch。

weixin_42426485的博客 311

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

STM32F407中文手册

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

MySQL 2026技术栈解析与面试核心要点

关系型数据库作为数据存储的核心组件,其核心原理与优化策略始终是开发者关注的焦点。以MySQL为例,其B+树索引结构通过减少磁盘IO次数实现高效查询,MVCC机制则解决了并发事务的隔离问题。在分布式架构成为主流的今天,数据库性能优化需要系统化思维,包括索引设计、慢查询分析和事务隔离级别选择等关键技术。特别是在MySQL 8.4版本中,JSON处理能力增强和资源组管理等新特性,为高并发场景提供了更多解决方案。这些技术在企业级应用、电商系统和金融交易等场景中发挥着关键作用,也是技术面试中的高频考察点。

weixin_33795093的博客 423

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Rails Webpacker 深度解析:现代前端开发的最佳实践与迁移指南

在当今的Web开发领域,Rails应用与JavaScript生态系统的无缝集成已成为提升开发效率的关键。Webpacker作为Rails官方推荐的JavaScript模块管理工具,为开发者提供了强大的现代前端开发体验。虽然Webpacker已经退役,但对于仍在维护Rails 5.x或6.x版本的项目来说,它仍然是一个稳定可靠的解决方案。 ## 🔍 Webpacker的核心价值:解决传统Asse

gitblog_00160的博客 626

PostgreSQL行记录结构与MVCC机制深度解析

数据库存储引擎的行记录结构是数据管理的核心基础,PostgreSQL采用独特的Heap Tuple格式实现高效存储。其物理结构包含固定头部和可变字段,通过TOAST技术处理大对象存储,配合MVCC机制实现并发控制。在事务处理中,xmin/xmax字段构成版本链,支持多种隔离级别实现。这种设计在电商订单等高并发场景中展现出优势,但也需要注意行膨胀等问题。通过HOT优化、填充因子调优等技术,可以显著提升PostgreSQL在OLTP场景下的性能表现。

weixin_32667955的博客 28
上一篇: PostgreSQL笔记33: 索引底层原理、扫描类型与属性体系深度解析
下一篇: PostgreSQL笔记35:索引常见问题诊断与解决方案全景解析
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、付费专栏及课程。

余额充值