默认索引类型
执行 CREATE INDEX idx ON table (col); 时,默认创建的就是 B-Tree 索引
原理
基于平衡多路搜索树,所有叶子节点处于同一层
支持高效查找、范围扫描、排序
适用场景
等值查询:WHERE col = 'value'
范围查询:WHERE col BETWEEN 10 AND 100、col > 50
排序:ORDER BY col
前缀匹配(仅限左前缀):WHERE col LIKE 'abc%'(若为文本)
不适用
右模糊或全模糊:LIKE '%abc'、LIKE '%abc%'
数组/JSON/全文搜索等复杂类型(虽可建但效率低)
2)GIN(Generalized Inverted Index,广义倒排索引)
原理
为「一个值对应多个键」的场景设计(如数组、JSON、全文检索)
结构:键 → 行 ID 列表(Posting List)
适合「包含」类查询(如「文档是否包含词 X」)
不适用
简单等值/范围、排序(用 B-Tree 更合适)
写多读少且对写入延迟敏感(GIN 写成本较高)
适用场景
数据类型
常用操作符
示例
jsonb
@>, ?, `?
, ?&`
array
&&, @>, <@
ids && ARRAY[1,2,3]
全文检索
@@ 等
tsv @@ to_tsquery('word')
3)GiST(Generalized Search Tree)
原理
通用搜索树,可承载多种「近似/范围」语义
写入通常比 GIN 轻,查询可能需更多回表或二次过滤
适用场景
地理空间:点、线、面、范围(常与 PostGIS 配合)
全文检索:写多读少时,可作为 GIN 的替代(索引更小、写更快)
范围类型(range)、网络类型(inet/cidr)等
4)BRIN(Block Range INdex)
原理
按连续物理块存储「最小/最大」等摘要,不存每个键
适合物理存储与键值强相关(如按时间顺序写入的时间列)
适用场景
超大表 + 列在物理上有序(如按时间递增插入的 created_at)
范围查询可「跳过」大量数据块,显著减少 I/O
不适用
数据无序(如 UUID 主键)
高频点查(WHERE id = 123)
小表(索引收益 < 维护成本)
3.2 四种索引对比总结
索引类型
最佳场景
写入性能
查询性能
内存/磁盘占用
是否支持排序
B-Tree
等值、范围、排序
中
极快
中~高
✅
GIN
JSON/数组/全文(包含查询)
慢
快(过滤)
高
❌
GiST
空间、全文(写多)
快
中(常需回表)
中
❌
BRIN
超大表 + 物理有序列
极快
快(范围跳过)
极低
❌
3.3 如何选择?
普通字段(int、text、timestamp)→ B-Tree
JSONB / 数组 / 全文检索(读多)→ GIN
地理空间 / 全文检索(写多)→ GiST
TB 级日志表、按时间查询 → BRIN
3.4 EXPLAIN 怎么看(排查思路)
PostgreSQL
使用 EXPLAIN (ANALYZE, BUFFERS) 查看实际执行计划和 I/O
关注:Actual Rows、Loops、Buffers、Index Only Scan
MySQL
使用 EXPLAIN 或 EXPLAIN ANALYZE 查看执行计划
关注:type(ALL最差)、rows、Extra(Using index表示覆盖索引)
3.5 SQL 对照示例
覆盖索引 / Index Only Scan
MySQL:
-- 二级索引包含所有查询列,避免回表CREATE INDEX idx_email_status ON users(email,status);
EXPLAIN FORMAT=JSON SELECT email,status FROM users WHERE email='[email protected]';
PostgreSQL:
-- Index Only Scan 需要可见性图 (Visibility Map)CREATE INDEX idx_email_status ON users(email,status);
EXPLAIN(ANALYZE, BUFFERS) SELECT email,status FROM users WHERE email='[email protected]';
表达式 / 生成列索引
MySQL:
ALTER TABLE users ADDCOLUMN email_lower VARCHAR(255) GENERATED ALWAYS AS(LOWER(email)) STORED;
CREATE INDEX idx_email_lower ON users(email_lower);
SELECT*FROM users WHERE email_lower='[email protected]';
PostgreSQL:
CREATE INDEX idx_email_lower ON users(LOWER(email));
SELECT*FROM users WHERELOWER(email)='[email protected]';
大分页优化 (Keyset Pagination)
MySQL / PostgreSQL 通用:
SELECT*FROM users WHERE id <100000ORDERBY id DESC LIMIT 20;
-- 假设有表 events(id bigint, payload jsonb)CREATE TABLE events ( id BIGSERIAL PRIMARY KEY,
payload jsonb NOT NULL);
-- GIN 索引(适合 @> 包含查询等)CREATE INDEX idx_events_payload_gin ON events USING gin (payload);
-- 查询:payload 包含 {"type":"pay"}
EXPLAIN(ANALYZE, BUFFERS) SELECT*FROM events WHERE payload @>'{"type":"pay"}';
-- 取字段文本SELECT payload->>'type'FROM events WHERE id =1;
MySQL:
-- 假设有表 events(id bigint primary key auto_increment, payload json)CREATE TABLE events ( id BIGINTPRIMARY KEY AUTO_INCREMENT,
payload JSON NOT NULL,
-- 生成列抽取 JSON key
type VARCHAR(32) GENERATED ALWAYS AS(JSON_UNQUOTE(JSON_EXTRACT(payload,'$.type'))) STORED,
INDEX idx_events_type(type));
EXPLAIN SELECT*FROM events WHERE type='pay';
全文检索
PostgreSQL:
-- 简化示例:title + body 做全文CREATE TABLE docs ( id BIGSERIAL PRIMARY KEY,
title text,
body text,
tsv tsvector );
-- 维护 tsv(实际生产通常用触发器或生成列策略)UPDATE docs SET tsv = to_tsvector('simple',coalesce(title,'')||' '||coalesce(body,''));
CREATE INDEX idx_docs_tsv_gin ON docs USING gin(tsv);
SELECT*FROM docs WHERE tsv @@ plainto_tsquery('simple','postgres');
MySQL:
CREATE TABLE docs ( id BIGINTPRIMARY KEY AUTO_INCREMENT,
title text,
body text,
FULLTEXT KEY ft_title_body(title, body));
SELECT*FROM docs WHEREMATCH(title, body) AGAINST('postgres'INNATURALLANGUAGE MODE);