目录

1. 不加索引:像一页一页翻书

2. 加了索引:像先查目录再定位

3. 和“直接查找”的区别到底是什么

没有索引

有索引

4. 为什么索引会快

5. 用你前面那几个索引举例

例子 1:messages(conversation_id)

例子 2:tasks(status)

例子 3:memories(user_id)

6. 索引不是白送的,它有代价

代价 1:占空间

代价 2:写入变慢

代价 3:维护复杂度上升

7. 什么情况下特别值得加索引

8. 什么情况下索引帮助不大

第一种:表很小

第二种:字段区分度太低

第三种:你查的是大部分数据

9. 一个非常直观的对比

没有索引

有索引

10. 这和 Python 里的查找也有点像

列表查找

字典查找

11. 一句最务实的话

12. 你做 Agent 项目时该怎么判断要不要加索引

13. 你现在先记住这条经验就够用了


索引的作用,本质上就是:

让数据库不用每次都把整张表从头翻到尾。

你可以把它理解成书的目录。


1. 不加索引:像一页一页翻书

假设有一张 messages 表,里面有 1000 万条消息。

你执行:

SELECT * FROM messages WHERE conversation_id = 123;

如果 conversation_id 没有索引,数据库通常只能干一件很朴素的事:

  • 从第一行开始看

  • 这一行是不是 conversation_id = 123

  • 不是就继续看下一行

  • 一直扫完整张表

这叫:

全表扫描(Full Table Scan)

就像你在一本没有目录的厚书里找“第 8 章”,只能从第一页开始翻。很原始,很诚实,也很慢。


2. 加了索引:像先查目录再定位

如果你建了这个索引:

CREATE INDEX idx_messages_conversation_id ON messages(conversation_id);

数据库就会额外维护一份“按 conversation_id 排好序的查找结构”。

当你再查:

SELECT * FROM messages WHERE conversation_id = 123;

数据库就可以:

  • 先去索引里找 123

  • 快速定位到对应记录大概在哪

  • 再去表里拿完整数据

这就像:

  • 先翻目录

  • 看到“第 8 章在 213 页”

  • 直接跳过去

这通常会快很多,尤其是数据量大的时候。


3. 和“直接查找”的区别到底是什么

你说的“直接查找”,在数据库里其实通常不是“瞬间找到”,而是:

没有索引时,数据库只能直接扫表。

所以区别是:

没有索引

查找方式是:

  • 扫全表

  • 时间成本随着数据量线性增长

数据越大越慢。


有索引

查找方式是:

  • 先查索引结构

  • 再定位到目标数据

速度通常快得多,尤其是筛选条件比较明确时。


4. 为什么索引会快

大多数关系型数据库里的普通索引,底层常见是 B-Tree / B+Tree 这一类结构。

你不用先把名字背成咒语,先抓住效果:

它不是无序乱放的。
它会让数据库能更快地缩小搜索范围,而不是把每条数据都看一遍。

所以:

  • 没索引:像在一堆乱文件里瞎翻

  • 有索引:像文件柜已经按字母和编号排好


5. 用你前面那几个索引举例

例子 1:messages(conversation_id)

CREATE INDEX idx_messages_conversation_id ON messages(conversation_id);

适合这种查询:

SELECT * FROM messages WHERE conversation_id = 100;

因为你在 Agent 项目里经常要查:

  • 某个会话下的所有消息

如果不加索引,每次查一个会话历史都可能扫整张 messages 表。


例子 2:tasks(status)

CREATE INDEX idx_tasks_status ON tasks(status);

适合这种查询:

SELECT * FROM tasks WHERE status = 'pending';
SELECT * FROM tasks WHERE status = 'failed';

比如你想找:

  • 所有待处理任务

  • 所有失败任务

这在任务调度、重试系统里很常见。

不过这里有个小提醒:
status 这种字段取值通常很少,比如就 4 个状态,所以它的索引效果有时不如高区分度字段明显。数据库优化器有时甚至会判断“扫表反而更划算”。

也就是说:

不是建了索引就一定用。

数据库优化器会自己算账,这家伙很现实。


例子 3:memories(user_id)

CREATE INDEX idx_memories_user_id ON memories(user_id);

适合这种查询:

SELECT * FROM memories WHERE user_id = 42;

因为你经常要按用户查记忆。

如果一个系统有几十万用户、几千万条 memory,没有这个索引,每次查用户记忆都像在仓库里徒手考古。


6. 索引不是白送的,它有代价

这里很关键。索引不是越多越好,不然数据库会被你堆成一只盔甲过厚的乌龟。

代价 1:占空间

索引本身也要存储。

你建一个索引,就相当于额外维护一套查找结构。


代价 2:写入变慢

你插入、更新、删除数据时,不仅要改表本身,还要改索引。

例如插入一条新消息:

  • 表里加一行

  • conversation_id 索引也要更新

  • 其他相关索引也可能要更新

所以:

  • 索引提高查询速度

  • 但会降低写入速度

这是典型的工程权衡。


代价 3:维护复杂度上升

索引建太多,后面排查性能问题、迁移、维护都更麻烦。


7. 什么情况下特别值得加索引

一般这些字段很适合加索引:

  • 经常出现在 WHERE 条件里的字段

  • 经常用来 JOIN 的字段

  • 经常用来排序 ORDER BY 的字段

  • 经常用来分页、过滤的字段

  • 外键字段

比如 Agent 项目里常见的:

  • conversation_id

  • user_id

  • task_id

  • created_at

  • document_id

这些都很典型。


8. 什么情况下索引帮助不大

有些场景索引不一定划算。

第一种:表很小

如果一张表就几百行、几千行,全表扫一下很快,索引意义不大。


第二种:字段区分度太低

比如:

  • 性别字段只有男/女

  • 状态字段只有 pending/running/completed/failed

这种字段的索引有时效果一般,因为一次查出来的数据还是很多。

这叫“选择性不高”。


第三种:你查的是大部分数据

比如:

SELECT * FROM messages;

或者查出来 80% 的表数据。

那数据库可能会觉得:

“都快全拿了,还走什么索引,直接扫表吧。”


9. 一个非常直观的对比

假设 messages 表有 1000 万行。

你要找 conversation_id = 123

没有索引

数据库可能要看 1000 万行。

有索引

数据库先在索引里定位到 123,然后只取对应那一小部分记录。

差别就像:

  • 没索引:挨家挨户敲门找张三

  • 有索引:先查户籍系统,再直接去张三家


10. 这和 Python 里的查找也有点像

你可以类比一下:

列表查找

x in my_list

通常要一个个比。

字典查找

x in my_dict

通常更快,因为它有专门的查找结构。

数据库索引和这个类比不完全一样,但感觉上很接近:

  • 全表扫描像 list 查找

  • 索引查找像“有额外辅助结构的快速定位”


11. 一句最务实的话

索引不是为了“让数据库更高级”,而是为了:

把高频查询从“遍历所有数据”变成“快速定位目标数据”。


12. 你做 Agent 项目时该怎么判断要不要加索引

你可以先问自己三个问题:

  1. 这个字段会不会经常拿来查?

  2. 这个字段会不会经常拿来关联别的表?

  3. 这张表未来会不会变大?

如果三个问题里有两个答案是“会”,那这个字段通常就值得认真考虑索引。


13. 你现在先记住这条经验就够用了

对于你这种 Agent 项目,优先关注这些索引位点:

  • messages.conversation_id

  • conversations.user_id

  • tasks.conversation_id

  • tool_calls.task_id

  • memories.user_id

  • document_chunks.document_id

因为这些字段几乎天然就是“查找入口”。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐