面试题答案
一键面试正则表达式查询性能优化方面
- 优化正则表达式:
- 减少通配符使用:尽量避免以通配符开头的正则表达式,如
^.*pattern
,因为这种方式MongoDB无法利用索引。如果可能,改为pattern.*
形式,当pattern
足够长且有区分度时,MongoDB可以利用索引。 - 简化表达式:去除不必要的复杂分组和字符类,使正则表达式更简洁,减少计算量。例如,
(a|b|c)
可以简化为[abc]
。
- 减少通配符使用:尽量避免以通配符开头的正则表达式,如
- 索引优化:
- 创建合适索引:针对
description
字段创建文本索引。文本索引支持更复杂的文本查询,包括正则表达式,并且性能优于普通索引。例如,在Node.js中可以使用如下代码创建文本索引:
- 创建合适索引:针对
db.collection('yourCollection').createIndex({description: 'text'});
- **复合索引**:如果查询除了`description`字段正则匹配外,还涉及其他字段的过滤条件,可以创建复合索引。例如,若还需根据`createdAt`字段过滤,可创建复合索引`{description: 1, createdAt: 1}`,但要注意索引顺序需根据查询频率和过滤条件的选择性来确定。
3. 查询优化:
- 批量查询:将多个小的正则查询合并为一个批量查询,减少数据库交互次数。在驱动中一般都有批量查询的方法,如在Python的pymongo
库中,可以使用find
方法一次性获取多个满足条件的文档。
- 限制返回字段:只返回需要的字段,减少数据传输量。例如,在pymongo
中可以使用projection
参数指定返回字段:
result = collection.find({'description': {'$regex': 'pattern'}}, {'_id': 0, 'description': 1})
高并发场景下避免性能瓶颈和数据一致性问题
- 避免性能瓶颈:
- 缓存机制:采用缓存(如Redis)存储频繁查询的正则表达式结果。当有相同的正则查询时,先从缓存中获取数据,若缓存中没有再查询MongoDB,并将结果存入缓存。在Node.js中可以使用
ioredis
库实现缓存功能:
- 缓存机制:采用缓存(如Redis)存储频繁查询的正则表达式结果。当有相同的正则查询时,先从缓存中获取数据,若缓存中没有再查询MongoDB,并将结果存入缓存。在Node.js中可以使用
const Redis = require('ioredis');
const redis = new Redis();
async function getRegexResult() {
const cacheResult = await redis.get('regex_pattern');
if (cacheResult) {
return JSON.parse(cacheResult);
}
const mongoResult = await db.collection('yourCollection').find({'description': {'$regex': 'pattern'}}).toArray();
await redis.set('regex_pattern', JSON.stringify(mongoResult));
return mongoResult;
}
- **读写分离**:在高并发读场景下,使用MongoDB的副本集进行读写分离,主节点处理写操作,从节点处理读操作。通过驱动配置读写偏好为`secondaryPreferred`,使读操作优先从从节点读取数据,减轻主节点压力。例如在`pymongo`中:
from pymongo import MongoClient, ReadPreference
client = MongoClient('mongodb://host:port', read_preference=ReadPreference.SECONDARY_PREFERRED)
- 保证数据一致性:
- 事务机制:如果涉及到写操作和正则查询在逻辑上需要保证一致性,可使用MongoDB的多文档事务。例如在Node.js中:
const {MongoClient} = require('mongodb');
const uri = "mongodb://host:port";
const client = new MongoClient(uri, {useNewUrlParser: true, useUnifiedTopology: true});
async function runTransaction() {
try {
await client.connect();
const session = client.startSession();
session.startTransaction();
// 写操作
await db.collection('yourCollection').updateOne({_id: docId}, {$set: {description: newDescription}}, {session});
// 正则查询
const result = await db.collection('yourCollection').find({'description': {'$regex': 'pattern'}}, {session}).toArray();
await session.commitTransaction();
return result;
} catch (e) {
console.error(e);
} finally {
await client.close();
}
}
- **版本控制**:在文档中添加版本号字段,每次写操作增加版本号。读操作时可以根据版本号判断数据是否是最新的。例如在更新文档时,同时更新`version`字段:
result = collection.update_one({'_id': doc_id}, {'$set': {'description': new_description,'version': existing_version + 1}})
查询时可以带上版本号条件,确保数据一致性。