CodeGym /课程 /SQL SELF /索引太多带来的问题

索引太多带来的问题

SQL SELF
第 38 级, 课程 2
可用

索引绝对是让数据库变快的神器,但俗话说得好,“过犹不及”。不是每个索引都有用,索引太多反而会拖后腿。这听起来有点反直觉,但事实就是这样。咱们来仔细聊聊。

想象一下一个超大的图书馆,里面有好几个目录,比如按作者、按类型、按出版年份。每个目录都能帮你更快找到书。但如果目录太多,比如每个书名的单词、每个细节都建个目录,最后你会发现:查找反而更慢了,目录占了很多空间,管理员还得不停地维护这些列表。

数据库里的索引其实也差不多:它们能帮你快速查数据,但如果索引太多,每次插入或修改数据都得更新一堆索引,操作就变得很重。而且磁盘空间也会被吃掉。更坑的是,索引太多,数据库自己都可能搞不清到底该用哪个索引。

所以,和图书馆的目录一样,索引也不能乱加——有几个真正有用的比一堆没用的强多了。

来玩个“PostgreSQL侦探”小游戏。假设你给同一列加了三个索引,你觉得这样能让性能更好。可想想看:

  • 如果你的表是一个超大的学生名单,三个索引,每加一个学生就要更新三次索引。这哪是加速,简直是拖慢了吧?
  • 如果你有10个这样的表,每个都被索引压垮?整个数据库性能直接崩掉。

怎么判断你有没有索引太多的问题?

第一步,先看看你现在有多少索引。在PostgreSQL里可以用这个命令:

\d 表名

这个命令会显示表、字段和相关的索引。如果你发现某个表挂了一堆索引,那就要警惕了。

还有个很有用的工具——系统视图pg_stat_user_indexes。它能显示索引的使用频率,帮你找出“吃灰”的索引:

SELECT
    relname AS table_name,
    indexrelname AS index_name,
    idx_scan AS index_scans
FROM
    pg_stat_user_indexes
WHERE
    idx_scan = 0;

如果idx_scan等于0,说明这个索引一次都没被用过。这种索引就是删掉的最佳候选。

索引太多的例子

假设有个用户表:

CREATE TABLE users (
    user_id SERIAL PRIMARY KEY,
    email VARCHAR(255) UNIQUE,
    username VARCHAR(50),
    created_at TIMESTAMP DEFAULT NOW()
);

我们有三个索引:

-- email的索引
CREATE INDEX idx_users_email ON users (email);

-- username的索引
CREATE INDEX idx_users_username ON users (username);

-- created_at的索引
CREATE INDEX idx_users_created_at ON users (created_at);

现在来看下常见的查询:

  1. 按email查用户。
  2. 按username查用户。
  3. 按created_at排序用户。

看起来这些索引挺有用。但问题来了:如果这些查询很少发生(比如一周一次),那建索引就没啥意义。更糟的是,有些索引根本没被用过,只会拖慢插入和更新。

举个例子:假设users表里有如下数据:

user_id email username created_at
1 alex.lin@mail.com alexlin 2024-06-15 10:23:00
2 anna.min@mail.com annamin 2024-06-16 12:47:00
3 otto.song@mail.com ottosong 2024-06-17 08:30:00
4 maria.chi@mail.com mariachi 2024-06-18 14:10:00

如果几乎没人按username查,idx_users_username这个索引就一次都没用过(idx_scan = 0),完全可以删掉让性能更好。

所以说,索引是好东西,但要用得聪明。宁可有几个真正有用的,也别搞一堆没用的。

怎么避免索引太多

  1. 分析索引的使用情况。 刚才说过了,用pg_stat_user_indexes查查索引的使用频率。如果某个索引几乎没用过,基本可以删掉:
DROP INDEX IF EXISTS 索引名;
  1. 只给常用查询加索引。 加索引前先问自己几个问题:
  • 这个字段经常出现在WHERE、ORDER BY、GROUP BY里吗?
  • 表的数据量大吗?
  • 不加索引查询真的很慢吗?

只要有一个问题答案是“不是”,那这个索引大概率是多余的。

  1. 用组合索引。 如果你经常在一个查询里用到多个字段,不如建个组合索引,别每个字段都单独建:
CREATE INDEX idx_users_email_username ON users (email, username);

这样能加速同时用email 和 username过滤的查询。

  1. 定期检查现有索引。 随着数据库变大,你的查询也会变。以前有用的索引现在可能没用了。记得定期检查,把没用的索引删掉。

举例怎么精简索引

回到我们的users表。与其建三个单独的索引,不如这样优化:

  • 如果很少按created_at排序,就别给它单独建索引。
  • 把email和username的索引合成一个组合索引:
CREATE INDEX idx_users_email_username ON users (email, username);

总结:怎么找到平衡点?

和编程里的很多事一样,这里也讲究“少即是多”。别因为能加索引就给每个字段都加。想清楚你为什么要加索引,它到底能不能让查询变快。务实点,记住:好开发不是乱加索引,而是懂得它们的影响,能用得恰到好处。

现在你有了这个技能,就能避免索引太多导致的灾难,让你的数据库飞快,像猎豹一样,而不是背着一堆没用索引的乌龟。

2
任务
SQL SELF, 第 38 级, 课程 2
已锁定
查找未使用的索引
查找未使用的索引
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION