MST
星途 面试题库

面试题:MongoDB地理空间索引之专家难度复杂位置查询与索引策略题

假设你正在处理一个全球范围内的共享单车位置跟踪项目,MongoDB集合存储单车信息,包括单车ID(bike_id)、状态(status)、当前位置(location,使用GeoJSON格式)以及历史位置轨迹(一个包含多个GeoJSON格式位置点的数组)。现在有需求:1. 查询某个城市区域内(多边形区域)目前可用且过去1小时内移动距离超过1公里的单车;2. 优化针对这类复杂查询的地理空间索引策略,以应对高并发查询请求。请阐述详细的实现方案,包括索引创建、查询语句编写以及可能涉及到的性能优化手段。
42.1万 热度难度
数据库MongoDB

知识考点

AI 面试

面试题答案

一键面试

1. 创建索引

为了优化查询性能,我们需要在 location历史位置轨迹 字段上创建地理空间索引。

// 在 location 字段上创建 2dsphere 索引
db.bikes.createIndex({ location: "2dsphere" });
// 在历史位置轨迹数组字段上创建 2dsphere 索引,假设历史位置轨迹字段名为 historyLocations
db.bikes.createIndex({ historyLocations: "2dsphere" }); 

2. 查询语句编写

要查询某个城市区域内(多边形区域)目前可用且过去1小时内移动距离超过1公里的单车,我们可以按以下步骤编写查询语句:

// 定义多边形区域,假设多边形区域的 GeoJSON 格式如下
const polygon = {
    type: "Polygon",
    coordinates: [
        [
            [lng1, lat1],
            [lng2, lat2],
            [lng3, lat3],
            // 更多坐标点
            [lng1, lat1]
        ]
    ]
};

// 计算1小时前的时间
const oneHourAgo = new Date(new Date().getTime() - 60 * 60 * 1000);

db.bikes.find({
    status: "可用",
    location: {
        $geoWithin: {
            $geometry: polygon
        }
    },
    historyLocations: {
        $elemMatch: {
            timestamp: { $gte: oneHourAgo },
            location: {
                $near: {
                    $geometry: polygon,
                    $maxDistance: 1000 // 1公里,单位为米
                }
            }
        }
    }
});

3. 性能优化手段

  • 复合索引:如果查询还涉及其他字段(例如 status),可以考虑创建复合索引。例如:
db.bikes.createIndex({ status: 1, location: "2dsphere", historyLocations: "2dsphere" });
  • 批量读取:如果查询结果集较大,可以使用批量读取方式,减少网络传输压力。例如在Node.js中:
const cursor = db.bikes.find({...查询条件... });
cursor.batchSize(100).forEach((doc) => {
    // 处理文档
});
  • 缓存:对于频繁查询的结果,可以使用缓存机制(如Redis),减少直接查询MongoDB的次数。
  • 分片:如果数据量巨大,可以对MongoDB进行分片,将数据分布到多个服务器上,提高并发处理能力。
// 在分片集群中创建索引
sh.enableSharding("yourDatabase");
sh.shardCollection("yourDatabase.bikes", { location: "2dsphere" });
  • 优化查询逻辑:确保查询语句尽可能简洁高效,避免不必要的嵌套和复杂逻辑。例如,尽量减少使用 $or 操作符,因为它可能会影响索引的使用效率。如果确实需要使用 $or,可以考虑对每个条件分别创建索引。