索引介绍
索引是数据库中的一种快速查询数据的方法。索引中记录了表中的一列或多列值与其物理位置之间的对应关系,就好比是一本书前面的目录,通过目录中页码就能快速定位到我们需要查询的内容。
索引的代价
- 增加了数据库的存储空间
- 在插入和修改数据时要花费较多的时间,因为索引也要随之更新
索引其他用途
除了加快查询以外,比如唯一索引还可以起到唯一约束的作用。
索引的分类
- BTree:最常用的索引,适合用于处理等值查询和范围查询。
- HASH:只能处理简单的等值查询。
- GiST:不是单独一种索引类型,而是一种架构,可以在这种架构上实现很多不同的索引策略。
- SP-GiST:即空间分区 GiST 索引,PostgreSQL 9.2 引入。
- GIN:反转索引,可以处理包含多个键的值。
创建索引
案例表结构
sql
CREATE TABLE contacts (
id int PRIMARY KEY,
name varchar(40),
phone varchar(32)[],
address text
);
1. 简单 BTree 索引
为了实现按姓名 name 快速查询,可以在字段 name 上建一个 BTree 索引:
sql
CREATE INDEX idx_contacts_name ON contacts(name);
2. GIN 索引(用于数组字段)
如果想按电话号码 phone 字段做快速查询(例如查询某个电话号码是谁的),由于此字段是一个数组,BTree 索引不再适用,可以建一个 GIN 索引:
sql
CREATE INDEX idx_contacts_phone ON contacts USING gin(phone);
查询示例:
sql
SELECT * FROM contacts WHERE phone @> ARRAY['13422334455'::varchar(32)];
解释:
@>是数组操作符,表示"包含"的意思,GIN 索引能在@>上起作用。
创建索引时带参数
sql
CREATE INDEX idx_contacts_name ON contacts(name) WITH (FILLFACTOR=50);
按降序创建索引
sql
CREATE INDEX idx_contacts_name ON contacts(name DESC);
指定空值排序
sql
-- 空值排在非空值前面
CREATE INDEX idx_contacts_name ON contacts(name DESC NULLS FIRST);
-- 空值排在非空值后面
CREATE INDEX idx_contacts_name ON contacts(name DESC NULLS LAST);
并发创建索引
默认方式的问题
默认情况下,创建索引时会锁定表以防止写入,然后对表做全表扫描。在此过程中:
- 其他用户可以读取表
- 插入、更新、删除等操作将一直被阻塞,直到索引创建完毕
示例
sql
CREATE TABLE testtab01 (id int PRIMARY KEY, note int);
INSERT INTO testtab01 SELECT generate_series(1,5000000), generate_series(1,5000000);
在一个窗口中创建索引:
sql
CREATE INDEX idx_testtab01_note ON testtab01(note);
在另一个窗口中执行删除操作将被阻塞:
sql
DELETE FROM testtab01 WHERE id=1;
使用 CONCURRENTLY 选项创建索引
这种方式不会长时间阻塞 DML 操作,但耗时更长,因为需要两次扫描表。
示例
sql
DROP INDEX IF EXISTS idx_testtab01_note;
CREATE INDEX CONCURRENTLY idx_testtab01_note ON testtab01(note);
另一个窗口的删除语句可以正常执行:
sql
DELETE FROM testtab01 WHERE id=2;
DELETE FROM testtab01 WHERE id=3;
并发创建索引的"坑"
如果在并发创建索引过程中取消操作,可能会导致索引残留(状态为 INVALID)。
处理方法
sql
-- 查看表结构,确认无效索引
\d testtab01
-- 手动删除无效索引
DROP INDEX idx_testtab01_note;
修改索引
重命名索引
sql
ALTER INDEX idx_contacts_name RENAME TO idx_contacts_name_old;
移动索引到指定表空间
sql
ALTER INDEX idx_contacts_name_old SET TABLESPACE tbs_data01;
修改填充因子
sql
ALTER INDEX idx_contacts_name_old SET (fillfactor = 75);
查看索引信息
sql
\d+ idx_contacts_name_old
删除索引
安全删除(存在才删除)
sql
DROP INDEX IF EXISTS idx_contacts_name_old;
默认 RESTRICT 行为
如果有对象依赖该索引,删除会失败。
示例
sql
CREATE TABLE class (
class_no int,
class_name varchar(40)
);
CREATE UNIQUE INDEX index_unique_class_no ON class(class_no);
CREATE TABLE student (
student_no int PRIMARY KEY,
student_name varchar(40),
age int,
class_no int REFERENCES class(class_no)
);
-- 删除索引会失败,因为有外键依赖
DROP INDEX index_unique_class_no;
使用 CASCADE 强制删除
sql
DROP INDEX index_unique_class_no CASCADE;
注意:使用
CASCADE会同时删除依赖该索引的外键约束。