✍️【Node.js程序员】的数据库【索引优化】指南

🍭前言

你好,我是你的人类朋友

早上中午晚上好

如果你是个新手,看到高级索引技巧前即可!

如果你是个新手,看到高级索引技巧前即可!

如果你是个新手,看到高级索引技巧前即可!

ok,接着往下看吧!

1. 🔍索引的基础概念

什么是索引??

索引 是数据库中用于加速查询 的数据结构,类似于书籍的目录

它可以帮助数据库快速定位到特定数据,而不必扫描整个表。

看不懂也没事,后续会有示例,你包能看懂。

为什么需要索引?

  • 提高查询速度 ------对于入门来说,这一点是最重要的。一提起数据库性能优化,需要马上联想到索引
  • 加速排序和分组操作
  • 提高某些连接操作的性能

2. 🌰基础索引使用例子

假设我们有一个用户表,结构如下:

javascript 复制代码
// 用户表结构
const userSchema = new mongoose.Schema({
  username: String,
  email: String,
  age: Number,
  createdAt: Date,
  isActive: Boolean
});

2.1 创建简单索引

javascript 复制代码
// 为username字段创建索引
userSchema.index({ username: 1 }); // 1表示升序索引

// 为email字段创建唯一索引
userSchema.index({ email: 1 }, { unique: true });

// 为age字段创建索引
userSchema.index({ age: 1 });

2.2 复合索引

别被名字吓到了。其实就是将多个字段组合在一起创建的索引

javascript 复制代码
// 创建复合索引(username和age)
userSchema.index({ username: 1, age: 1 });

// 创建复合索引(isActive和createdAt)
userSchema.index({ isActive: 1, createdAt: -1 }); // -1表示降序索引

3. 😎索引优化实践

好记性不如烂键盘,开敲!

3.1 查询优化示例

没有索引的查询:

javascript 复制代码
// 这会执行全表扫描【后面会解释什么是[全表扫描]!】
const users = await User.find({ age: { $gt: 25 } });

补充:啥是全表扫描???? 我举个栗子🌰:

  1. 你有一本记录了1000个用户的笔记本(数据库表)
  2. 现在想找出所有年龄大于25岁的人
  3. 但笔记本既没有按年龄排序 ,也没有年龄索引目录
  4. 你只能从第一页开始,逐条检查每个人的年龄是否达标
  5. 即使最后只有10个人符合条件,你也必须看完1000条记录 这就是全表扫描,一个个找呗,其实还是很好理解的。

有索引的查询:

javascript 复制代码
// 先确保age字段有索引
userSchema.index({ age: 1 });

// 现在查询会使用索引
const users = await User.find({ age: { $gt: 25 } });

为啥加了索引就变快?还有王法吗?还有法律吗?(bushi)

因为是入门,所以此处只需记住下面这句话:

【🌟重点🌟】加索引 ≈ 数据库自动帮你排序,让查询像查字典一样快。

因为数据库帮你排序完了,那要找年龄大于25的不是很方便吗?其实大致就是这个道理。

3.2 排序优化栗子🌰

没有索引的排序:

javascript 复制代码
// 这会执行内存排序【数据库将查询结果的所有数据都加载到服务器的内存(RAM)中,然后在内存中进行排序操作。】,大数据集性能差
const users = await User.find().sort({ createdAt: -1 });// 从数据库查出所有用户,并按创建时间倒序排列(最新的在前面)

有索引的排序:

javascript 复制代码
// 为 createdAt 创建降序索引(-1),优化排序查询性能
userSchema.index({ createdAt: -1 });

// 最佳实践:
// 1. _id 已有索引,无需重复添加
// 2. 只为高频查询/排序的字段建索引
// 3. 索引方向应与查询排序方向一致

// 示例:使用索引快速获取最新100条记录
// 这个查询用到第二行处创建的 `createdAt` 降序索引
const users = await User.find().sort({ createdAt: -1 }).limit(100);

// 注意:createdAt/updatedAt 是 Mongoose 自动管理的时间戳

4. 🤔高级索引技巧

重要的事情说三遍

🌟其实前方的内容已经能够干很多事情了,优先将上方的内容看熟练即可!!感兴趣再看下方的内容!!!
🌟其实前方的内容已经能够干很多事情了,优先将上方的内容看熟练即可!!感兴趣再看下方的内容!!!
🌟其实前方的内容已经能够干很多事情了,优先将上方的内容看熟练即可!!感兴趣再看下方的内容!!!

4.1 覆盖查询

当查询只需要从索引中获取数据,而不需要访问实际文档时,性能最佳。

javascript 复制代码
// 创建复合索引覆盖查询
userSchema.index({ username: 1, age: 1 });

// 查询只返回索引中包含的字段【注:通过提前创建好索引并[只查询]索引包含的字段,从而让查询跑得更快】
const userData = await User.find({ username: 'john' }, { username: 1, age: 1, _id: 0 });

4.2 部分索引

只为满足【特定条件】的文档创建索引,减少索引大小。

javascript 复制代码
// 只为活跃用户创建索引
userSchema.index({ username: 1 }, { partialFilterExpression: { isActive: true } });

// 查询活跃用户会使用索引
const activeUsers = await User.find({ username: 'john', isActive: true });

4.3 多键索引(数组字段)

javascript 复制代码
// 假设用户有标签数组
const userSchema = new mongoose.Schema({
  username: String,
  tags: [String]
});

// 为tags数组创建多键索引
userSchema.index({ tags: 1 });

// 查询包含特定标签的用户
const taggedUsers = await User.find({ tags: 'developer' });

5. 🦉索引分析和优化

5.1 查看查询执行计划

javascript 复制代码
// 使用explain()分析查询
const explanation = await User.find({ age: { $gt: 25 } }).explain('executionStats');
console.log(explanation.executionStats);

5.2 常见执行计划解读

  • COLLSCAN: 全表扫描(通常需要优化)
  • IXSCAN: 索引扫描(好)
  • FETCH: 从索引指向的文档中获取数据
  • SORT: 内存排序(大数据集可能需要索引优化)

5.3 索引优化策略

  1. 为常用查询条件创建索引
  2. 为排序字段创建索引
  3. 使用复合索引而不是多个单字段索引
  4. 注意索引顺序:等值查询字段在前,范围查询字段在后
  5. 定期监控和删除未使用的索引

6. 👻实际Express应用中的索引管理

6.1 在Express应用中管理索引

javascript 复制代码
const express = require('express');
const mongoose = require('mongoose');
const app = express();

// 连接数据库
mongoose.connect('mongodb://localhost:27017/mydb');

// 定义模型和索引
const userSchema = new mongoose.Schema({
  username: { type: String, required: true },
  email: { type: String, unique: true },
  age: Number,
  createdAt: { type: Date, default: Date.now },
  isActive: Boolean
});

// 创建索引
userSchema.index({ username: 1 }); // 单字段索引
userSchema.index({ isActive: 1, createdAt: -1 }); // 复合索引
userSchema.index({ email: 1 }, { unique: true }); // 唯一索引

const User = mongoose.model('User', userSchema);

// 路由示例
app.get('/users/active', async (req, res) => {
  try {
    // 这个查询会使用我们创建的复合索引
    const activeUsers = await User.find({ isActive: true })
      .sort({ createdAt: -1 })
      .limit(10);
    res.json(activeUsers);
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});

// 启动服务器
app.listen(3000, () => {
  console.log('Server running on port 3000');
});

6.2 自动化索引管理

javascript 复制代码
// 在应用启动时确保索引存在
async function ensureIndexes() {
  try {
    await User.ensureIndexes();
    console.log('All indexes are ready');
  } catch (err) {
    console.error('Index creation failed:', err);
  }
}

ensureIndexes();

7. 常见索引陷阱

  1. 过度索引:每个索引都会【占用空间】并降低写入性能。其实就是告诉我们想好了再加索引,别疯狂加
  2. 索引顺序错误:复合索引中字段顺序很重要
  3. 索引未被使用:查询条件不符合索引结构
  4. 索引碎片化:长期使用后索引效率下降

8. 性能监控和维护

javascript 复制代码
// 获取集合的索引信息
async function getIndexInfo() {
  const indexes = await User.collection.getIndexes();
  console.log('Current indexes:', indexes);
}

// 删除不需要的索引
async function dropUnusedIndex() {
  await User.collection.dropIndex('age_1'); // 索引名称
  console.log('Index dropped');
}

// 定期重建索引(维护时)
async function rebuildIndexes() {
  await User.collection.reIndex();
  console.log('Indexes rebuilt');
}

最后

索引是一个好东西,学完之后快点在项目中应用吧!🐲

上方的内容,其实入门之后的内容可以慢慢看,或者说是开发中需要使用到了再去看。

入门之后,深入的过程中,一些东西自然就会接触到了。

怕的就是入门还没入进去,就被吓跑了,那还不如一开始先不看一些进阶内容,入门的内容先看熟练了再说呢。

下次见!

相关推荐
苏三说技术19 分钟前
xxl-job 和 elastic-job,哪个更好?
后端
xkxnq20 分钟前
第五阶段:Vue3核心深度深挖(第74天)(Vue3计算属性进阶)
前端·javascript·vue.js
三小河27 分钟前
Agent Skill与Rules的区别——以Cursor为例
前端·javascript·后端
Hilaku34 分钟前
不要在简历上写精通 Vue3?来自面试官的真实劝退
前端·javascript·vue.js
三小河40 分钟前
前端视角详解 Agent Skill
前端·javascript·后端
牛奔1 小时前
Go 是如何做抢占式调度的?
开发语言·后端·golang
Aniugel1 小时前
单点登录(SSO)系统
前端
颜酱1 小时前
二叉树遍历思维实战
javascript·后端·算法
鹏多多1 小时前
移动端H5项目,还需要react-fastclick解决300ms点击延迟吗?
前端·javascript·react.js
serioyaoyao1 小时前
上万级文件一起可视化,怎么办?答案是基于 ParaView 的远程可视化
前端