🍭前言
你好,我是你的人类朋友
早上中午晚上好
如果你是个新手,看到高级索引技巧前即可!
如果你是个新手,看到高级索引技巧前即可!
如果你是个新手,看到高级索引技巧前即可!
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 } });
补充:啥是全表扫描???? 我举个栗子🌰:
- 你有一本记录了1000个用户的笔记本(数据库表)
- 现在想找出所有年龄大于25岁的人
- 但笔记本既没有按年龄排序 ,也没有年龄索引目录
- 你只能从第一页开始,逐条检查每个人的年龄是否达标
- 即使最后只有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 索引优化策略
- 为常用查询条件创建索引
- 为排序字段创建索引
- 使用复合索引而不是多个单字段索引
- 注意索引顺序:等值查询字段在前,范围查询字段在后
- 定期监控和删除未使用的索引
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. 常见索引陷阱
- 过度索引:每个索引都会【占用空间】并降低写入性能。其实就是告诉我们想好了再加索引,别疯狂加
- 索引顺序错误:复合索引中字段顺序很重要
- 索引未被使用:查询条件不符合索引结构
- 索引碎片化:长期使用后索引效率下降
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');
}
最后
索引是一个好东西,学完之后快点在项目中应用吧!🐲
上方的内容,其实入门之后的内容可以慢慢看,或者说是开发中需要使用到了再去看。
入门之后,深入的过程中,一些东西自然就会接触到了。
怕的就是入门还没入进去,就被吓跑了,那还不如一开始先不看一些进阶内容,入门的内容先看熟练了再说呢。
下次见!