✍️【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');
}

最后

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

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

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

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

下次见!

相关推荐
神仙别闹8 分钟前
基于ASP.NET+ SQL Server实现(Web)医院信息管理系统
前端·后端·asp.net
程序员爱钓鱼11 分钟前
Go语言同步原语与数据竞争:WaitGroup
后端·google·go
重庆小透明4 小时前
【从零开始学习JVM | 第六篇】运行时数据区
java·jvm·后端·学习
步行cgn5 小时前
Vue 中的数据代理机制
前端·javascript·vue.js
GH小杨5 小时前
JS之Dom模型和Bom模型
前端·javascript·html
星月心城6 小时前
JS深入之从原型到原型链
前端·javascript
MessiGo6 小时前
Javascript 编程基础(5)面向对象 | 5.2、原型系统
开发语言·javascript·原型模式
你的人类朋友6 小时前
🤔Token 存储方案有哪些
前端·javascript·后端
烛阴6 小时前
从零开始:使用Node.js和Cheerio进行轻量级网页数据提取
前端·javascript·后端
liuyang___6 小时前
日期的数据格式转换
前端·后端·学习·node.js·node