今天我们要更深入地聊聊索引的架构,看看它们底层到底是怎么运作的。其实,了解索引的结构不仅能让你明白为什么查询会变快,还能帮你为不同的任务选出最合适的索引。
说到索引结构,其实就是指索引内部数据是怎么组织的,这样才能保证查找速度快。想象一下有个文件柜。如果文件都乱堆在一起,找东西肯定很难。但如果柜子是按字母顺序排好的,查找就简单多了。索引就是这么回事:它们把数据排好队,让你能飞快地找到想要的信息。
B-TREE索引结构
B-TREE(balanced tree —— 平衡树)是PostgreSQL里最常用的索引类型。其实它就是一种树形结构,数据被分成节点,查找的时候会从树的根节点一路导航到叶子节点。
大致长这样:
根节点
/ | \
节点1 节点2 节点3
/ \ | / \
叶子1 叶子2 叶子3 叶子4 叶子5
每个节点里都有关键值,用来引导查找。比如,假如根节点里有[10, 20, 30]:
- 所有小于
10的数据都在叶子1。 - 所有在
10和20之间的数据在叶子2,依此类推。
B-TREE索引的优点:
- 查找速度快:查找复杂度是
O(log n),比线性查找快多了。 - 适合区间查找(比如找所有在
10和50之间的值)。
举个例子: 假设我们有个students表,里面有个age列。当我们在这个列上建B-TREE索引:
CREATE INDEX age_idx ON students (age);
PostgreSQL会为年龄值建一个平衡树,这样就能很快查到某个年龄或某个年龄段的学生。
B-TREE里的查找算法
当你执行查询时,PostgreSQL会这样用索引查找数据:
- 确定查找的key(比如年龄
25)。 - 从根节点开始。
- 把key和节点里的区间比对,然后跳到对应的子节点。
- 重复第3步,直到到达叶子节点。
- 从叶子节点返回和key匹配的数据。
查询例子:
SELECT * FROM students WHERE age = 25;
索引能减少需要扫描的数据量,让查找变得很快。
查找算法和性能
索引能加速查找,是因为它们减少了需要扫描的行数。没有索引时,PostgreSQL会扫描整张表(这叫顺序扫描,也就是Seq Scan)。有了索引,就会用索引扫描(Index Scan),速度快多了。
顺序扫描和索引扫描对比
顺序扫描(
Seq Scan):- PostgreSQL会读表里的每一行,检查查询条件,返回符合的行。
- 如果没有索引,或者查询覆盖了几乎所有行,就会用这个。
索引扫描(
Index Scan):- PostgreSQL用索引先找到符合条件的行,然后只去表里查这些行。
- 对于大表,如果只查一小部分数据,这种方式快很多。
举个例子: 没有索引时查找年龄
SELECT * FROM students WHERE age = 25;
可能要读100万行。有了B-TREE索引,比如只需要读100行。
索引结构对性能的影响
索引之所以快,是因为它们减少了需要扫描的数据量。比如表里有几百万行,索引能把它们组织起来,让查询只需要读几个节点,而不是全表。
理解索引结构很重要。知道索引怎么工作,能帮你搞清楚为什么有些查询慢,以及怎么让它们变快。
还有,选对索引类型也很关键。查区间用B-TREE。查数组或JSONB用GIN。选错索引反而会拖慢数据库。
实际例子
来看看索引在实际工作中怎么帮我们。
排序用的索引
CREATE INDEX salary_idx ON employees (salary);
SELECT * FROM employees ORDER BY salary;
有了B-TREE索引,PostgreSQL可以直接从索引里返回排好序的数据,不用再额外排序。
区间用的索引
CREATE INDEX price_idx ON products (price);
SELECT * FROM products WHERE price BETWEEN 100 AND 500;
B-TREE索引能很快找到落在指定区间的行。
常见问题和坑
为什么不是所有地方都该用索引? 索引会占用磁盘空间,而且会拖慢插入、更新和删除操作,因为每次都要更新索引结构。所以只给常用的列建索引才划算。
什么时候索引没用? 如果查询覆盖了表的大部分(比如WHERE true),PostgreSQL会选Seq Scan,因为读索引节点没啥优势。
GO TO FULL VERSION