基礎概念題
1. 什么是MongoDB?它的主要特點是什么?
答案: MongoDB是一個開源的NoSQL文檔型數據庫,主要特點包括:
- 文檔存儲:使用BSON格式存儲數據,類似JSON結構
- 無Schema約束:靈活的數據結構,可以動態添加字段
- 高性能:支持索引,內存映射文件
- 高可用性:支持副本集(Replica Set)
- 水平擴展:支持分片(Sharding)
- 豐富的查詢語言:支持復雜的查詢操作
2. MongoDB中的文檔、集合、數據庫分別對應關系型數據庫中的什么?
答案:
- 文檔(Document) → 行(Row/Record)
- 集合(Collection) → 表(Table)
- 數據庫(Database) → 數據庫(Database)
Java驅動相關
3. 在Java中如何連接MongoDB?
答案:
// 使用MongoDB Java驅動
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoDatabase;// 方式1:簡單連接
MongoClient mongoClient = MongoClients.create("mongodb://localhost:27017");
MongoDatabase database = mongoClient.getDatabase("testdb");// 方式2:帶認證的連接
MongoClient mongoClient = MongoClients.create("mongodb://username:password@localhost:27017/admin");// 方式3:連接字符串配置
String uri = "mongodb://localhost:27017/?maxPoolSize=20&w=majority";
MongoClient mongoClient = MongoClients.create(uri);
4. 如何在Java中進行CRUD操作?
答案:
import com.mongodb.client.*;
import org.bson.Document;
import org.bson.types.ObjectId;
import static com.mongodb.client.model.Filters.*;MongoCollection<Document> collection = database.getCollection("users");// Create - 插入文檔
Document user = new Document("name", "張三").append("age", 25).append("email", "zhangsan@example.com");
collection.insertOne(user);// Read - 查詢文檔
Document found = collection.find(eq("name", "張三")).first();// Update - 更新文檔
collection.updateOne(eq("name", "張三"), new Document("$set", new Document("age", 26)));// Delete - 刪除文檔
collection.deleteOne(eq("name", "張三"));
Spring Data MongoDB
5. 如何在Spring Boot中集成MongoDB?
答案:
// 1. 添加依賴 (pom.xml)
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>// 2. 配置文件 (application.yml)
spring:data:mongodb:uri: mongodb://localhost:27017/testdb// 3. 實體類
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;@Document(collection = "users")
public class User {@Idprivate String id;private String name;private Integer age;private String email;// constructors, getters, setters
}// 4. Repository接口
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;public interface UserRepository extends MongoRepository<User, String> {User findByName(String name);List<User> findByAgeGreaterThan(int age);@Query("{'age': {'$gte': ?0, '$lte': ?1}}")List<User> findByAgeBetween(int minAge, int maxAge);
}
6. MongoTemplate和MongoRepository的區別?
答案:
MongoRepository:
- 基于Spring Data的Repository模式
- 提供CRUD的基本操作方法
- 支持方法命名查詢
- 適合簡單的數據庫操作
MongoTemplate:
- 更底層的API,提供更靈活的操作
- 支持復雜的查詢和聚合操作
- 可以直接使用MongoDB的原生查詢語法
- 適合復雜的數據庫操作
// MongoTemplate示例
@Autowired
private MongoTemplate mongoTemplate;// 復雜查詢
Query query = new Query(Criteria.where("age").gte(18).lte(65).and("status").is("active"));
List<User> users = mongoTemplate.find(query, User.class);// 聚合操作
Aggregation agg = Aggregation.newAggregation(Aggregation.match(Criteria.where("age").gte(18)),Aggregation.group("department").count().as("count"),Aggregation.sort(Sort.Direction.DESC, "count")
);
List<Document> results = mongoTemplate.aggregate(agg, "users", Document.class).getMappedResults();
性能優化
7. MongoDB索引有哪些類型?如何在Java中創建索引?
答案:
索引類型:
- 單字段索引:最基本的索引類型
- 復合索引:多個字段組合的索引
- 多鍵索引:數組字段上的索引
- 文本索引:全文搜索索引
- 地理空間索引:地理位置查詢
- 哈希索引:用于分片鍵
Java中創建索引:
// 使用MongoTemplate創建索引
mongoTemplate.getCollection("users").createIndex(Indexes.ascending("name"));// 創建復合索引
mongoTemplate.getCollection("users").createIndex(Indexes.compoundIndex(Indexes.ascending("name"),Indexes.descending("age"))
);// 使用注解創建索引
@Document(collection = "users")
@CompoundIndex(name = "name_age_idx", def = "{'name': 1, 'age': -1}")
public class User {@Idprivate String id;@Indexed(unique = true)private String email;private String name;private Integer age;
}
8. MongoDB查詢優化有哪些策略?
答案:
合理使用索引
// 確保查詢字段有索引 collection.find(eq("email", "test@example.com")); // email字段需要索引
使用投影減少數據傳輸
// 只查詢需要的字段 collection.find(eq("status", "active")).projection(fields(include("name", "email"), excludeId()));
分頁查詢優化
// 使用skip和limit,但skip值不宜過大 collection.find().skip(page * size).limit(size);// 大數據量分頁使用范圍查詢 collection.find(gt("_id", lastId)).limit(size);
批量操作
// 批量插入 List<Document> documents = Arrays.asList(doc1, doc2, doc3); collection.insertMany(documents);// 批量更新 List<WriteModel<Document>> updates = new ArrayList<>(); updates.add(new UpdateOneModel<>(eq("_id", id1), update1)); updates.add(new UpdateOneModel<>(eq("_id", id2), update2)); collection.bulkWrite(updates);
高級特性
9. MongoDB的副本集是什么?如何在Java中配置?
答案:
**副本集(Replica Set)**是MongoDB的高可用解決方案:
- Primary節點:處理所有寫操作
- Secondary節點:從Primary同步數據,可處理讀操作
- 仲裁節點(Arbiter):參與選舉但不存儲數據
Java配置:
// 連接副本集
String uri = "mongodb://mongo1:27017,mongo2:27017,mongo3:27017/?replicaSet=rs0";
MongoClient mongoClient = MongoClients.create(uri);// 讀偏好設置
MongoClient mongoClient = MongoClients.create(MongoClientSettings.builder().applyConnectionString(new ConnectionString(uri)).readPreference(ReadPreference.secondaryPreferred()).build()
);
10. MongoDB事務如何使用?
答案:
// MongoDB 4.0+支持多文檔事務
try (ClientSession session = mongoClient.startSession()) {session.withTransaction(() -> {collection1.insertOne(session, document1);collection2.updateOne(session, filter, update);collection3.deleteOne(session, filter);return null;});
}// Spring Data MongoDB事務
@Transactional
public void transferMoney(String fromAccount, String toAccount, double amount) {accountRepository.updateBalance(fromAccount, -amount);accountRepository.updateBalance(toAccount, amount);
}
聚合框架
11. MongoDB聚合管道有哪些常用階段?
答案:
常用聚合階段:
- $match:過濾文檔
- $group:分組聚合
- $sort:排序
- $project:字段投影
- $limit/$skip:限制和跳過
- $lookup:關聯查詢
- $unwind:展開數組
// Java聚合示例:按部門統計平均工資
Aggregation agg = Aggregation.newAggregation(Aggregation.match(Criteria.where("status").is("active")),Aggregation.group("department").avg("salary").as("avgSalary").count().as("employeeCount"),Aggregation.sort(Sort.Direction.DESC, "avgSalary"),Aggregation.limit(10)
);List<Document> results = mongoTemplate.aggregate(agg, "employees", Document.class).getMappedResults();
數據建模
12. MongoDB中的嵌入式文檔和引用文檔如何選擇?
答案:
嵌入式文檔(Embedding):
// 適合一對一或一對少量的關系
{"_id": ObjectId("..."),"name": "用戶名","address": {"street": "某某街道","city": "北京","zipCode": "100000"}
}
引用文檔(Referencing):
// 適合一對多或多對多關系
// 用戶文檔
{"_id": ObjectId("user123"),"name": "用戶名"
}// 訂單文檔
{"_id": ObjectId("order456"),"userId": ObjectId("user123"),"amount": 100.0
}
選擇原則:
- 數據經常一起查詢 → 嵌入
- 數據獨立更新頻繁 → 引用
- 子文檔大小有限 → 嵌入
- 關系復雜 → 引用
常見問題和錯誤
13. MongoDB連接池如何配置?
答案:
// 連接池配置
MongoClientSettings settings = MongoClientSettings.builder().applyConnectionString(new ConnectionString("mongodb://localhost:27017")).applyToConnectionPoolSettings(builder ->builder.maxSize(100) // 最大連接數.minSize(10) // 最小連接數.maxWaitTime(2, TimeUnit.MINUTES) // 最大等待時間.maxConnectionLifeTime(30, TimeUnit.MINUTES) // 連接最大生命周期.maxConnectionIdleTime(10, TimeUnit.MINUTES) // 連接最大空閑時間).build();MongoClient mongoClient = MongoClients.create(settings);
14. ObjectId是什么?如何在Java中使用?
答案:
ObjectId是MongoDB的默認主鍵類型,12字節組成:
- 4字節時間戳
- 5字節隨機值(機器標識+進程ID)
- 3字節遞增計數器
import org.bson.types.ObjectId;// 創建ObjectId
ObjectId id = new ObjectId();
System.out.println(id.toHexString());// 從字符串創建
ObjectId id2 = new ObjectId("64a7b8c9d1e2f3a4b5c6d7e8");// 獲取時間戳
Date timestamp = id.getDate();// 在查詢中使用
Document doc = collection.find(eq("_id", new ObjectId(idString))).first();
15. 如何處理MongoDB中的大數據量查詢?
答案:
// 1. 使用游標遍歷大結果集
FindIterable<Document> iterable = collection.find();
try (MongoCursor<Document> cursor = iterable.iterator()) {while (cursor.hasNext()) {Document doc = cursor.next();// 處理文檔}
}// 2. 分批處理
int batchSize = 1000;
int skip = 0;
while (true) {List<Document> batch = collection.find().skip(skip).limit(batchSize).into(new ArrayList<>());if (batch.isEmpty()) break;// 處理批次數據processBatch(batch);skip += batchSize;
}// 3. 使用Stream API
collection.find().forEach(doc -> {// 處理每個文檔
});
復雜查詢
16. 如何實現MongoDB的模糊查詢?
答案:
// 1. 正則表達式查詢
collection.find(regex("name", ".*張.*", "i")); // 不區分大小寫// 2. 使用$text索引進行全文搜索
// 首先創建文本索引
collection.createIndex(Indexes.text("title", "content"));// 全文搜索
collection.find(text("java mongodb"));// 3. Spring Data MongoDB模糊查詢
public interface UserRepository extends MongoRepository<User, String> {@Query("{'name': {'$regex': ?0, '$options': 'i'}}")List<User> findByNameContaining(String name);// 或使用方法命名List<User> findByNameContainingIgnoreCase(String name);
}
17. MongoDB聚合查詢中$lookup如何使用?
答案:
// 類似SQL的JOIN操作
// 查詢用戶及其訂單信息
Aggregation agg = Aggregation.newAggregation(Aggregation.lookup("orders", "_id", "userId", "userOrders"),Aggregation.match(Criteria.where("status").is("active")),Aggregation.project("name", "email", "userOrders")
);List<Document> results = mongoTemplate.aggregate(agg, "users", Document.class).getMappedResults();// 對應的MongoDB原生查詢:
/*
db.users.aggregate([{$lookup: {from: "orders",localField: "_id",foreignField: "userId",as: "userOrders"}},{ $match: { "status": "active" } }
])
*/
性能調優
18. 如何分析MongoDB查詢性能?
答案:
// 1. 使用explain()分析查詢計劃
FindIterable<Document> iterable = collection.find(eq("status", "active"));
ExplainVerbosity verbosity = ExplainVerbosity.EXECUTION_STATS;
Document explanation = iterable.explain(verbosity);
System.out.println(explanation.toJson());// 2. 監控慢查詢
// 在MongoDB中啟用慢查詢日志
// db.setProfilingLevel(1, { slowms: 100 })// 3. Spring Data MongoDB性能監控
@Component
public class MongoEventListener extends AbstractMongoEventListener<Object> {@EventListenerpublic void onBeforeConvert(BeforeConvertEvent<Object> event) {// 記錄操作開始時間}@EventListener public void onAfterSave(AfterSaveEvent<Object> event) {// 記錄操作完成時間}
}
19. MongoDB分片的原理和Java配置?
答案:
分片原理:
- Config Server:存儲元數據
- Query Router(mongos):路由查詢請求
- Shard:實際存儲數據的分片
// Java連接分片集群
String uri = "mongodb://mongos1:27017,mongos2:27017/testdb";
MongoClient mongoClient = MongoClients.create(uri);// 分片鍵選擇原則:
// 1. 高基數(cardinality)
// 2. 低頻率(frequency)
// 3. 非單調遞增// 在Spring Data中指定分片鍵
@Document(collection = "orders")
@Sharded(shardKey = {"customerId", "orderDate"})
public class Order {@Idprivate String id;private String customerId; // 分片鍵private Date orderDate; // 分片鍵private Double amount;
}
錯誤處理和最佳實踐
20. MongoDB常見異常如何處理?
答案:
import com.mongodb.MongoWriteException;
import com.mongodb.MongoTimeoutException;
import com.mongodb.DuplicateKeyException;try {collection.insertOne(document);
} catch (DuplicateKeyException e) {// 處理重復鍵異常log.warn("文檔已存在: {}", e.getMessage());
} catch (MongoTimeoutException e) {// 處理超時異常log.error("MongoDB操作超時: {}", e.getMessage());throw new ServiceException("數據庫操作超時,請稍后重試");
} catch (MongoWriteException e) {// 處理寫入異常log.error("寫入失敗: {}", e.getError());throw new ServiceException("數據保存失敗");
} catch (Exception e) {// 處理其他異常log.error("MongoDB操作異常", e);throw new ServiceException("數據庫操作失敗");
}
21. MongoDB連接最佳實踐有哪些?
答案:
@Configuration
public class MongoConfig {@Beanpublic MongoClient mongoClient() {MongoClientSettings settings = MongoClientSettings.builder().applyConnectionString(new ConnectionString(mongoUri)).applyToConnectionPoolSettings(builder ->builder.maxSize(100).minSize(10).maxConnectionIdleTime(30, TimeUnit.SECONDS)).applyToSocketSettings(builder ->builder.connectTimeout(2, TimeUnit.SECONDS).readTimeout(5, TimeUnit.SECONDS)).retryWrites(true).retryReads(true).build();return MongoClients.create(settings);}
}// 連接池最佳實踐:
// 1. 單例模式使用MongoClient
// 2. 合理配置連接池大小
// 3. 設置合理的超時時間
// 4. 啟用重試機制
// 5. 在應用關閉時正確關閉連接
22. 如何實現MongoDB的數據驗證?
答案:
// 1. 使用Bean Validation注解
@Document(collection = "users")
public class User {@Idprivate String id;@NotBlank(message = "姓名不能為空")@Size(min = 2, max = 50, message = "姓名長度必須在2-50之間")private String name;@Min(value = 0, message = "年齡不能為負數")@Max(value = 150, message = "年齡不能超過150")private Integer age;@Email(message = "郵箱格式不正確")private String email;
}// 2. 自定義驗證器
@Component
public class UserValidator {public void validate(User user) {if (user.getAge() < 18 && user.getRole().equals("ADMIN")) {throw new ValidationException("未成年人不能成為管理員");}}
}// 3. MongoDB Schema Validation(服務器端)
Document validationSchema = Document.parse("""
{$jsonSchema: {bsonType: "object",required: ["name", "age", "email"],properties: {name: {bsonType: "string",description: "姓名必須是字符串且為必填項"},age: {bsonType: "int",minimum: 0,maximum: 150}}}
}
""");
面試加分題
23. MongoDB與關系型數據庫的區別和適用場景?
答案:
特性 | MongoDB | 關系型數據庫 |
---|---|---|
數據模型 | 文檔型(BSON) | 關系型(表) |
Schema | 動態Schema | 固定Schema |
查詢語言 | MongoDB查詢語言 | SQL |
事務支持 | 4.0+支持多文檔事務 | 完整的ACID事務 |
擴展性 | 水平擴展友好 | 垂直擴展為主 |
適用場景:
- MongoDB適合:內容管理、實時分析、IoT數據、敏捷開發
- 關系型數據庫適合:金融系統、ERP系統、需要復雜事務的場景
24. 如何設計高效的MongoDB數據模型?
答案:
設計原則:
- 根據應用查詢模式設計
- 合理使用嵌入和引用
- 避免過深的嵌套
- 考慮文檔大小限制(16MB)
// 好的設計示例:博客系統
@Document(collection = "articles")
public class Article {@Idprivate String id;private String title;private String content;private String authorId; // 引用作者private List<String> tags; // 嵌入標簽private List<Comment> comments; // 嵌入評論(數量有限時)private Date createdAt;// 嵌入式評論public static class Comment {private String content;private String authorName;private Date createdAt;}
}// 如果評論數量很大,應該分離:
@Document(collection = "comments")
public class Comment {@Idprivate String id;private String articleId; // 引用文章private String content;private String authorId;private Date createdAt;
}
這些面試題涵蓋了MongoDB在Java開發中的核心概念、實際應用和最佳實踐。準備時建議:
- 動手實踐:親自編寫代碼驗證這些概念
- 理解原理:不只記住用法,要理解背后的原理
- 項目經驗:準備具體的項目使用案例
- 性能調優:了解常見的性能問題和解決方案