MongoDB分片技術實現

MongoDB分片技術實現

概述

MongoDB分片(Sharding)是MongoDB的水平擴展解決方案,通過將數據分布到多個分片(shard)上來處理大數據量和高吞吐量的需求。

MongoDB分片架構

1. 分片集群組件

# MongoDB分片集群架構
version:?'3.8'
services:# Config Server副本集config1:image:?mongo:5.0command:?mongod?--configsvr?--replSet?configReplSet?--port?27019ports:-?"27019:27019"volumes:-?config1_data:/data/dbconfig2:image:?mongo:5.0command:?mongod?--configsvr?--replSet?configReplSet?--port?27019ports:-?"27020:27019"volumes:-?config2_data:/data/dbconfig3:image:?mongo:5.0command:?mongod?--configsvr?--replSet?configReplSet?--port?27019ports:-?"27021:27019"volumes:-?config3_data:/data/db# 分片1副本集shard1_replica1:image:?mongo:5.0command:?mongod?--shardsvr?--replSet?shard1ReplSet?--port?27018ports:-?"27022:27018"volumes:-?shard1_replica1_data:/data/dbshard1_replica2:image:?mongo:5.0command:?mongod?--shardsvr?--replSet?shard1ReplSet?--port?27018ports:-?"27023:27018"volumes:-?shard1_replica2_data:/data/db# 分片2副本集shard2_replica1:image:?mongo:5.0command:?mongod?--shardsvr?--replSet?shard2ReplSet?--port?27018ports:-?"27024:27018"volumes:-?shard2_replica1_data:/data/dbshard2_replica2:image:?mongo:5.0command:?mongod?--shardsvr?--replSet?shard2ReplSet?--port?27018ports:-?"27025:27018"volumes:-?shard2_replica2_data:/data/db# mongos路由服務mongos1:image:?mongo:5.0command:?mongos?--configdb?configReplSet/config1:27019,config2:27019,config3:27019?--port?27017ports:-?"27017:27017"depends_on:-?config1-?config2-?config3mongos2:image:?mongo:5.0command:?mongos?--configdb?configReplSet/config1:27019,config2:27019,config3:27019?--port?27017ports:-?"27026:27017"depends_on:-?config1-?config2-?config3volumes:config1_data:config2_data:config3_data:shard1_replica1_data:shard1_replica2_data:shard2_replica1_data:shard2_replica2_data:

2. 分片集群初始化腳本

#!/bin/bash
# mongodb-cluster-init.shecho?"初始化MongoDB分片集群..."# 等待服務啟動
sleep 30# 初始化Config Server副本集
echo?"初始化Config Server副本集..."
mongo --host config1:27019 --eval?'
rs.initiate({_id: "configReplSet",configsvr: true,members: [{ _id: 0, host: "config1:27019" },{ _id: 1, host: "config2:27019" },{ _id: 2, host: "config3:27019" }]
})'# 等待副本集初始化完成
sleep 20# 初始化分片1副本集
echo?"初始化分片1副本集..."
mongo --host shard1_replica1:27018 --eval?'
rs.initiate({_id: "shard1ReplSet",members: [{ _id: 0, host: "shard1_replica1:27018" },{ _id: 1, host: "shard1_replica2:27018" }]
})'# 初始化分片2副本集
echo?"初始化分片2副本集..."
mongo --host shard2_replica1:27018 --eval?'
rs.initiate({_id: "shard2ReplSet",members: [{ _id: 0, host: "shard2_replica1:27018" },{ _id: 1, host: "shard2_replica2:27018" }]
})'# 等待分片副本集初始化完成
sleep 30# 添加分片到集群
echo?"添加分片到集群..."
mongo --host mongos1:27017 --eval?'
sh.addShard("shard1ReplSet/shard1_replica1:27018,shard1_replica2:27018")
sh.addShard("shard2ReplSet/shard2_replica1:27018,shard2_replica2:27018")
'echo?"MongoDB分片集群初始化完成!"

Java應用集成

1. Spring Boot配置

@Configuration
public?class?MongoShardingConfig?{@Value("${spring.data.mongodb.uri}")private?String mongoUri;@Beanpublic?MongoClient?mongoClient()?{// 連接到mongos路由服務ConnectionString connectionString =?new?ConnectionString(mongoUri);MongoClientSettings settings = MongoClientSettings.builder().applyConnectionString(connectionString).readPreference(ReadPreference.secondaryPreferred())?// 讀寫分離.writeConcern(WriteConcern.MAJORITY)?// 寫關注.readConcern(ReadConcern.MAJORITY)?// 讀關注.retryWrites(true)?// 重試寫入.retryReads(true)?// 重試讀取.applyToConnectionPoolSettings(builder -> {builder.maxSize(100)?// 最大連接數.minSize(10)?// 最小連接數.maxWaitTime(30, TimeUnit.SECONDS)?// 最大等待時間.maxConnectionIdleTime(60, TimeUnit.SECONDS);?// 連接空閑時間}).build();return?MongoClients.create(settings);}@Beanpublic?MongoTemplate?mongoTemplate()?{return?new?MongoTemplate(mongoClient(),?"sharded_database");}
}

2. 分片鍵設計

@Document(collection =?"users")
public?class?User?{@Idprivate?String id;@Indexedprivate?String userId;?// 分片鍵private?String username;private?String email;private?Date createTime;private?String region;?// 地理位置// 構造函數、getter、setter
}@Document(collection =?"orders")
public?class?Order?{@Idprivate?String id;@Indexedprivate?String customerId;?// 分片鍵private?String orderId;private?BigDecimal amount;private?Date orderTime;private?String status;// 構造函數、getter、setter
}@Document(collection =?"products")
public?class?Product?{@Idprivate?String id;@Indexedprivate?String categoryId;?// 分片鍵private?String productName;private?BigDecimal price;private?String description;// 構造函數、getter、setter
}

3. 分片管理服務

@Service
public?class?MongoShardingService?{@Autowiredprivate?MongoTemplate mongoTemplate;/*** 啟用數據庫分片*/public?void?enableSharding(String database)?{Document command =?new?Document("enableSharding", database);mongoTemplate.getDb().runCommand(command);log.info("已啟用數據庫分片: {}", database);}/*** 對集合進行分片*/public?void?shardCollection(String database, String collection, String shardKey)?{Document command =?new?Document("shardCollection", database +?"."?+ collection).append("key",?new?Document(shardKey,?1));mongoTemplate.getDb().runCommand(command);log.info("已對集合進行分片: {}.{}, 分片鍵: {}", database, collection, shardKey);}/*** 創建哈希分片*/public?void?createHashedSharding(String database, String collection, String shardKey)?{Document command =?new?Document("shardCollection", database +?"."?+ collection).append("key",?new?Document(shardKey,?"hashed"));mongoTemplate.getDb().runCommand(command);log.info("已創建哈希分片: {}.{}, 分片鍵: {}", database, collection, shardKey);}/*** 創建范圍分片*/public?void?createRangeSharding(String database, String collection, String shardKey)?{Document command =?new?Document("shardCollection", database +?"."?+ collection).append("key",?new?Document(shardKey,?1));mongoTemplate.getDb().runCommand(command);log.info("已創建范圍分片: {}.{}, 分片鍵: {}", database, collection, shardKey);}/*** 創建復合分片鍵*/public?void?createCompoundSharding(String database, String collection,?Map<String, Object> shardKeys)?{Document keyDoc =?new?Document();shardKeys.forEach(keyDoc::append);Document command =?new?Document("shardCollection", database +?"."?+ collection).append("key", keyDoc);mongoTemplate.getDb().runCommand(command);log.info("已創建復合分片: {}.{}, 分片鍵: {}", database, collection, shardKeys);}/*** 查看分片狀態*/public?Document?getShardingStatus()?{return?mongoTemplate.getDb().runCommand(new?Document("sh.status",?1));}/*** 查看集合分片信息*/public?Document?getCollectionShardInfo(String database, String collection)?{Document command =?new?Document("collStats", collection).append("verbose",?true);return?mongoTemplate.getDb(database).runCommand(command);}
}

4. 分片初始化配置

@Component
public?class?ShardingInitializer?{@Autowiredprivate?MongoShardingService shardingService;@EventListener(ApplicationReadyEvent.class)public?void?initializeSharding()?{try?{// 啟用數據庫分片shardingService.enableSharding("sharded_database");// 用戶集合 - 使用userId哈希分片shardingService.createHashedSharding("sharded_database",?"users",?"userId");// 訂單集合 - 使用customerId范圍分片shardingService.createRangeSharding("sharded_database",?"orders",?"customerId");// 產品集合 - 使用復合分片鍵Map<String, Object> productShardKeys =?new?HashMap<>();productShardKeys.put("categoryId",?1);productShardKeys.put("productId",?1);shardingService.createCompoundSharding("sharded_database",?"products", productShardKeys);log.info("MongoDB分片初始化完成");}?catch?(Exception e) {log.error("MongoDB分片初始化失敗", e);}}
}

分片策略優化

1. 智能分片鍵選擇

@Service
public?class?ShardKeyOptimizer?{@Autowiredprivate?MongoTemplate mongoTemplate;/*** 分析集合的查詢模式*/public?ShardKeyRecommendation?analyzeQueryPatterns(String collection)?{// 分析查詢日志List<Document> queryLogs = getQueryLogs(collection);Map<String, Integer> fieldUsageCount =?new?HashMap<>();Map<String, Double> fieldSelectivity =?new?HashMap<>();for?(Document log : queryLogs) {Document query = log.get("command", Document.class);if?(query !=?null?&& query.containsKey("find")) {Document filter = query.get("filter", Document.class);if?(filter !=?null) {analyzeFilterFields(filter, fieldUsageCount);}}}// 計算字段選擇性for?(String field : fieldUsageCount.keySet()) {double?selectivity = calculateFieldSelectivity(collection, field);fieldSelectivity.put(field, selectivity);}return?recommendShardKey(fieldUsageCount, fieldSelectivity);}private?void?analyzeFilterFields(Document filter, Map<String, Integer> fieldUsageCount)?{for?(String field : filter.keySet()) {fieldUsageCount.merge(field,?1, Integer::sum);}}private?double?calculateFieldSelectivity(String collection, String field)?{// 計算字段的選擇性(不重復值的比例)Aggregation aggregation = Aggregation.newAggregation(Aggregation.group(field),Aggregation.count().as("distinctCount"));AggregationResults<Document> results = mongoTemplate.aggregate(aggregation, collection, Document.class);long?distinctCount = results.getMappedResults().size();long?totalCount = mongoTemplate.count(new?Query(), collection);return?totalCount >?0?? (double) distinctCount / totalCount :?0;}private?ShardKeyRecommendation?recommendShardKey(Map<String, Integer> fieldUsageCount,?Map<String, Double> fieldSelectivity)?{// 綜合考慮使用頻率和選擇性String recommendedField = fieldUsageCount.entrySet().stream().max((e1, e2) -> {double?score1 = e1.getValue() * fieldSelectivity.getOrDefault(e1.getKey(),?0.0);double?score2 = e2.getValue() * fieldSelectivity.getOrDefault(e2.getKey(),?0.0);return?Double.compare(score1, score2);}).map(Map.Entry::getKey).orElse("_id");return?new?ShardKeyRecommendation(recommendedField,?fieldSelectivity.getOrDefault(recommendedField,?0.0));}private?List<Document>?getQueryLogs(String collection)?{// 從MongoDB profiler獲取查詢日志Query query =?new?Query(Criteria.where("ns").is("sharded_database."?+ collection).and("ts").gte(new?Date(System.currentTimeMillis() -?24?*?60?*?60?*?1000)));?// 最近24小時return?mongoTemplate.find(query, Document.class, "system.profile");}public?static?class?ShardKeyRecommendation?{private?String field;private?double?selectivity;public?ShardKeyRecommendation(String field,?double?selectivity)?{this.field = field;this.selectivity = selectivity;}// getter和setter}
}

2. 數據平衡監控

@Service
public?class?ShardBalanceMonitor?{@Autowiredprivate?MongoTemplate mongoTemplate;@Autowiredprivate?MeterRegistry meterRegistry;/*** 監控分片數據分布*/@Scheduled(fixedRate =?300000)?// 5分鐘檢查一次public?void?monitorShardDistribution()?{try?{Document shardStats = mongoTemplate.getDb().runCommand(new?Document("shardDistribution",?1));analyzeShardBalance(shardStats);}?catch?(Exception e) {log.error("分片分布監控失敗", e);}}private?void?analyzeShardBalance(Document shardStats)?{Document shards = shardStats.get("shards", Document.class);if?(shards ==?null)?return;Map<String, Long> shardSizes =?new?HashMap<>();long?totalSize =?0;for?(String shardName : shards.keySet()) {Document shardInfo = shards.get(shardName, Document.class);long?size = shardInfo.getLong("size");shardSizes.put(shardName, size);totalSize += size;}// 計算分布不均衡度double?imbalanceRatio = calculateImbalanceRatio(shardSizes, totalSize);// 記錄指標Gauge.builder("mongodb.shard.imbalance.ratio").register(meterRegistry, imbalanceRatio);// 如果不均衡度超過閾值,觸發重新平衡if?(imbalanceRatio >?0.3) {?// 30%的不均衡度log.warn("檢測到分片數據不均衡,不均衡度: {:.2f}", imbalanceRatio);triggerRebalance();}}private?double?calculateImbalanceRatio(Map<String, Long> shardSizes,?long?totalSize)?{if?(shardSizes.isEmpty() || totalSize ==?0)?return?0;double?avgSize = (double) totalSize / shardSizes.size();double?maxDeviation = shardSizes.values().stream().mapToDouble(size -> Math.abs(size - avgSize) / avgSize).max().orElse(0);return?maxDeviation;}private?void?triggerRebalance()?{try?{// 啟動平衡器mongoTemplate.getDb().runCommand(new?Document("balancerStart",?1));log.info("已啟動分片重新平衡");}?catch?(Exception e) {log.error("啟動分片重新平衡失敗", e);}}/*** 監控chunk分布*/@Scheduled(fixedRate =?600000)?// 10分鐘檢查一次public?void?monitorChunkDistribution()?{try?{// 查詢chunks集合Query query =?new?Query();List<Document> chunks = mongoTemplate.find(query, Document.class, "chunks");Map<String, Integer> shardChunkCount =?new?HashMap<>();for?(Document chunk : chunks) {String shard = chunk.getString("shard");shardChunkCount.merge(shard,?1, Integer::sum);}// 記錄每個分片的chunk數量for?(Map.Entry<String, Integer> entry : shardChunkCount.entrySet()) {Gauge.builder("mongodb.shard.chunk.count").tag("shard", entry.getKey()).register(meterRegistry, entry.getValue());}}?catch?(Exception e) {log.error("Chunk分布監控失敗", e);}}
}

3. 查詢路由優化

@Service
public?class?QueryRoutingOptimizer?{@Autowiredprivate?MongoTemplate mongoTemplate;/*** 優化查詢以避免跨分片操作*/public?<T>?List<T>?optimizedFind(Query query, Class<T> entityClass, String collection)?{// 分析查詢是否包含分片鍵if?(containsShardKey(query, collection)) {// 包含分片鍵,可以路由到特定分片return?mongoTemplate.find(query, entityClass, collection);}?else?{// 不包含分片鍵,需要廣播查詢log.warn("查詢不包含分片鍵,將執行跨分片查詢: {}", query);return?mongoTemplate.find(query, entityClass, collection);}}/*** 批量查詢優化*/public?<T>?List<T>?optimizedBatchFind(List<String> shardKeyValues,?String shardKeyField,?Class<T> entityClass,?String collection)?{// 按分片鍵分組Map<String, List<String>> shardGroups = groupByShardKey(shardKeyValues, shardKeyField);List<T> results =?new?ArrayList<>();// 并行查詢各分片shardGroups.entrySet().parallelStream().forEach(entry -> {Query query =?new?Query(Criteria.where(shardKeyField).in(entry.getValue()));List<T> shardResults = mongoTemplate.find(query, entityClass, collection);synchronized?(results) {results.addAll(shardResults);}});return?results;}/*** 聚合查詢優化*/public?<T>?AggregationResults<T>?optimizedAggregate(Aggregation aggregation,?String collection,?Class<T> outputType)?{// 檢查聚合管道是否可以下推到分片if?(canPushDownToShards(aggregation)) {return?mongoTemplate.aggregate(aggregation, collection, outputType);}?else?{// 需要在mongos層進行聚合log.warn("聚合操作需要在mongos層執行,可能影響性能");return?mongoTemplate.aggregate(aggregation, collection, outputType);}}private?boolean?containsShardKey(Query query, String collection)?{// 獲取集合的分片鍵信息String shardKey = getShardKey(collection);if?(shardKey ==?null)?return?false;// 檢查查詢條件是否包含分片鍵Document queryDoc = query.getQueryObject();return?queryDoc.containsKey(shardKey);}private?String?getShardKey(String collection)?{try?{// 從config.collections獲取分片鍵信息Query query =?new?Query(Criteria.where("_id").is("sharded_database."?+ collection));Document collectionInfo = mongoTemplate.findOne(query, Document.class, "collections");if?(collectionInfo !=?null) {Document key = collectionInfo.get("key", Document.class);if?(key !=?null?&& !key.isEmpty()) {return?key.keySet().iterator().next();}}}?catch?(Exception e) {log.error("獲取分片鍵失敗", e);}return?null;}private?Map<String, List<String>> groupByShardKey(List<String> values, String shardKeyField) {// 根據分片鍵值計算目標分片Map<String, List<String>> groups =?new?HashMap<>();for?(String value : values) {String targetShard = calculateTargetShard(value, shardKeyField);groups.computeIfAbsent(targetShard, k ->?new?ArrayList<>()).add(value);}return?groups;}private?String?calculateTargetShard(String shardKeyValue, String shardKeyField)?{// 簡化的分片計算邏輯int?hash = shardKeyValue.hashCode();int?shardCount = getShardCount();int?shardIndex = Math.abs(hash) % shardCount;return?"shard"?+ shardIndex;}private?int?getShardCount()?{try?{Document listShards = mongoTemplate.getDb().runCommand(new?Document("listShards",?1));List<Document> shards = listShards.getList("shards", Document.class);return?shards !=?null?? shards.size() :?1;}?catch?(Exception e) {log.error("獲取分片數量失敗", e);return?1;}}private?boolean?canPushDownToShards(Aggregation aggregation)?{// 檢查聚合管道是否包含可以下推到分片的操作List<AggregationOperation> operations = aggregation.getOperations();for?(AggregationOperation operation : operations) {if?(operation?instanceof?GroupOperation ||?operation?instanceof?SortOperation ||operation?instanceof?LimitOperation) {// 這些操作通常需要在mongos層執行return?false;}}return?true;}
}

性能優化

1. 連接池優化

@Configuration
public?class?MongoConnectionOptimization?{@Beanpublic?MongoClientSettings?mongoClientSettings()?{return?MongoClientSettings.builder().applyToConnectionPoolSettings(builder -> {builder.maxSize(200) ? ? ? ? ? ? ? ? ? ?// 最大連接數.minSize(20) ? ? ? ? ? ? ? ? ? ? ? ?// 最小連接數.maxWaitTime(30, TimeUnit.SECONDS) ?// 最大等待時間.maxConnectionLifeTime(60, TimeUnit.MINUTES)?// 連接最大生存時間.maxConnectionIdleTime(30, TimeUnit.MINUTES)?// 連接最大空閑時間.maintenanceInitialDelay(0, TimeUnit.SECONDS).maintenanceFrequency(30, TimeUnit.SECONDS);?// 維護頻率}).applyToSocketSettings(builder -> {builder.connectTimeout(10, TimeUnit.SECONDS) ? ?// 連接超時.readTimeout(30, TimeUnit.SECONDS); ? ? ? ??// 讀取超時}).applyToServerSettings(builder -> {builder.heartbeatFrequency(10, TimeUnit.SECONDS) ??// 心跳頻率.minHeartbeatFrequency(500, TimeUnit.MILLISECONDS);?// 最小心跳頻率}).build();}
}

2. 批量操作優化

@Service
public?class?MongoBatchOptimization?{@Autowiredprivate?MongoTemplate mongoTemplate;/*** 批量插入優化*/public?<T>?void?optimizedBatchInsert(List<T> documents, String collection)?{if?(documents.isEmpty())?return;// 按分片鍵分組Map<String, List<T>> shardGroups = groupDocumentsByShardKey(documents, collection);// 并行插入各分片shardGroups.entrySet().parallelStream().forEach(entry -> {List<T> shardDocuments = entry.getValue();// 分批插入,避免單次操作過大int?batchSize =?1000;for?(int?i =?0; i < shardDocuments.size(); i += batchSize) {int?endIndex = Math.min(i + batchSize, shardDocuments.size());List<T> batch = shardDocuments.subList(i, endIndex);mongoTemplate.insert(batch, collection);}});}/*** 批量更新優化*/public?void?optimizedBatchUpdate(List<UpdateRequest> updateRequests, String collection)?{BulkOperations bulkOps = mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, collection);for?(UpdateRequest request : updateRequests) {bulkOps.updateOne(request.getQuery(), request.getUpdate());}BulkWriteResult result = bulkOps.execute();log.info("批量更新完成,匹配: {}, 修改: {}",?result.getMatchedCount(), result.getModifiedCount());}/*** 批量刪除優化*/public?void?optimizedBatchDelete(List<Query> deleteQueries, String collection)?{BulkOperations bulkOps = mongoTemplate.bulkOps(BulkOperations.BulkMode.UNORDERED, collection);for?(Query query : deleteQueries) {bulkOps.remove(query);}BulkWriteResult result = bulkOps.execute();log.info("批量刪除完成,刪除數量: {}", result.getDeletedCount());}private?<T> Map<String, List<T>> groupDocumentsByShardKey(List<T> documents, String collection) {// 根據分片鍵對文檔進行分組Map<String, List<T>> groups =?new?HashMap<>();String shardKey = getShardKey(collection);if?(shardKey ==?null) {groups.put("default", documents);return?groups;}for?(T document : documents) {String shardKeyValue = extractShardKeyValue(document, shardKey);String targetShard = calculateTargetShard(shardKeyValue);groups.computeIfAbsent(targetShard, k ->?new?ArrayList<>()).add(document);}return?groups;}private?String?extractShardKeyValue(Object document, String shardKey)?{// 使用反射或其他方式提取分片鍵值try?{Field field = document.getClass().getDeclaredField(shardKey);field.setAccessible(true);Object value = field.get(document);return?value !=?null?? value.toString() :?"";}?catch?(Exception e) {log.error("提取分片鍵值失敗", e);return?"";}}private?String?getShardKey(String collection)?{// 獲取集合的分片鍵return?"userId";?// 簡化實現}private?String?calculateTargetShard(String shardKeyValue)?{// 計算目標分片return?"shard0";?// 簡化實現}public?static?class?UpdateRequest?{private?Query query;private?Update update;public?UpdateRequest(Query query, Update update)?{this.query = query;this.update = update;}// getter和setter}
}

3. 索引優化

@Service
public?class?MongoIndexOptimization?{@Autowiredprivate?MongoTemplate mongoTemplate;/*** 創建分片友好的索引*/public?void?createShardFriendlyIndexes(String collection)?{// 1. 分片鍵索引(自動創建)// 2. 復合索引(包含分片鍵)Index compoundIndex =?new?Index().on("userId", Sort.Direction.ASC) ?// 分片鍵.on("createTime", Sort.Direction.DESC).on("status", Sort.Direction.ASC);mongoTemplate.indexOps(collection).ensureIndex(compoundIndex);// 3. 查詢優化索引Index queryIndex =?new?Index().on("userId", Sort.Direction.ASC) ?// 分片鍵.on("email", Sort.Direction.ASC).sparse();?// 稀疏索引mongoTemplate.indexOps(collection).ensureIndex(queryIndex);// 4. 地理位置索引Index geoIndex =?new?Index().on("userId", Sort.Direction.ASC) ?// 分片鍵.on("location",?"2dsphere");mongoTemplate.indexOps(collection).ensureIndex(geoIndex);}/*** 監控索引使用情況*/@Scheduled(fixedRate =?3600000)?// 1小時檢查一次public?void?monitorIndexUsage()?{List<String> collections = Arrays.asList("users",?"orders",?"products");for?(String collection : collections) {try?{// 獲取索引統計信息Document indexStats = mongoTemplate.getDb().getCollection(collection).aggregate(Arrays.asList(new?Document("$indexStats",?new?Document()))).first();if?(indexStats !=?null) {analyzeIndexUsage(collection, indexStats);}}?catch?(Exception e) {log.error("監控索引使用情況失敗: {}", collection, e);}}}private?void?analyzeIndexUsage(String collection, Document indexStats)?{Document accesses = indexStats.get("accesses", Document.class);if?(accesses !=?null) {long?ops = accesses.getLong("ops");Date since = accesses.getDate("since");if?(ops ==?0?&& since !=?null) {long?daysSinceLastUse = (System.currentTimeMillis() - since.getTime()) / (24?*?60?*?60?*?1000);if?(daysSinceLastUse >?30) {log.warn("索引 {} 在集合 {} 中超過30天未使用,考慮刪除",?indexStats.getString("name"), collection);}}}}/*** 自動創建查詢優化索引*/public?void?autoCreateQueryIndexes(String collection, List<Document> queryPatterns)?{Map<String, Integer> fieldUsageCount =?new?HashMap<>();// 分析查詢模式for?(Document query : queryPatterns) {analyzeQueryFields(query, fieldUsageCount);}// 創建高頻查詢字段的索引fieldUsageCount.entrySet().stream().filter(entry -> entry.getValue() >?100)?// 使用次數超過100.forEach(entry -> {String field = entry.getKey();if?(!field.equals("_id")) {?// 跳過默認索引Index index =?new?Index().on(field, Sort.Direction.ASC);mongoTemplate.indexOps(collection).ensureIndex(index);log.info("為字段 {} 創建索引,使用頻率: {}", field, entry.getValue());}});}private?void?analyzeQueryFields(Document query, Map<String, Integer> fieldUsageCount)?{for?(String field : query.keySet()) {if?(!field.startsWith("$")) {?// 跳過操作符fieldUsageCount.merge(field,?1, Integer::sum);}}}
}

監控與運維

1. 分片集群監控

@Component
public?class?MongoShardingMonitor?{@Autowiredprivate?MongoTemplate mongoTemplate;@Autowiredprivate?MeterRegistry meterRegistry;/*** 監控分片集群健康狀態*/@Scheduled(fixedRate =?30000)public?void?monitorClusterHealth()?{try?{// 檢查mongos狀態Document isMaster = mongoTemplate.getDb().runCommand(new?Document("isMaster",?1));boolean?isMongos = isMaster.getBoolean("ismaster",?false);// 檢查分片狀態Document listShards = mongoTemplate.getDb().runCommand(new?Document("listShards",?1));List<Document> shards = listShards.getList("shards", Document.class);int?healthyShards =?0;int?totalShards = shards.size();for?(Document shard : shards) {String state = shard.getString("state");if?("1".equals(state)) {healthyShards++;}}// 記錄指標Gauge.builder("mongodb.cluster.shards.total").register(meterRegistry, totalShards);Gauge.builder("mongodb.cluster.shards.healthy").register(meterRegistry, healthyShards);// 檢查平衡器狀態Document balancerStatus = mongoTemplate.getDb().runCommand(new?Document("balancerStatus",?1));boolean?balancerEnabled = balancerStatus.getBoolean("mode",?false);Gauge.builder("mongodb.cluster.balancer.enabled").register(meterRegistry, balancerEnabled ??1?:?0);}?catch?(Exception e) {log.error("MongoDB集群健康監控失敗", e);}}/*** 監控分片性能指標*/@Scheduled(fixedRate =?60000)public?void?monitorShardPerformance()?{try?{Document serverStatus = mongoTemplate.getDb().runCommand(new?Document("serverStatus",?1));// 連接數Document connections = serverStatus.get("connections", Document.class);if?(connections !=?null) {int?current = connections.getInteger("current",?0);int?available = connections.getInteger("available",?0);Gauge.builder("mongodb.connections.current").register(meterRegistry, current);Gauge.builder("mongodb.connections.available").register(meterRegistry, available);}// 操作計數Document opcounters = serverStatus.get("opcounters", Document.class);if?(opcounters !=?null) {long?insert = opcounters.getLong("insert");long?query = opcounters.getLong("query");long?update = opcounters.getLong("update");long?delete = opcounters.getLong("delete");Counter.builder("mongodb.operations.insert").register(meterRegistry).increment(insert);Counter.builder("mongodb.operations.query").register(meterRegistry).increment(query);Counter.builder("mongodb.operations.update").register(meterRegistry).increment(update);Counter.builder("mongodb.operations.delete").register(meterRegistry).increment(delete);}}?catch?(Exception e) {log.error("MongoDB性能監控失敗", e);}}
}

2. 自動故障恢復

@Service
public?class?MongoFailoverService?{@Autowiredprivate?MongoTemplate mongoTemplate;@Autowiredprivate?NotificationService notificationService;/*** 檢測并處理分片故障*/@Scheduled(fixedRate =?15000)public?void?detectAndHandleFailures()?{try?{Document listShards = mongoTemplate.getDb().runCommand(new?Document("listShards",?1));List<Document> shards = listShards.getList("shards", Document.class);for?(Document shard : shards) {String shardId = shard.getString("_id");String host = shard.getString("host");String state = shard.getString("state");if?(!"1".equals(state)) {handleShardFailure(shardId, host);}}}?catch?(Exception e) {log.error("分片故障檢測失敗", e);}}private?void?handleShardFailure(String shardId, String host)?{log.error("檢測到分片故障: {} ({})", shardId, host);// 發送告警notificationService.sendAlert("MongoDB分片故障",String.format("分片 %s (%s) 發生故障,請及時處理", shardId, host));// 嘗試自動恢復attemptAutoRecovery(shardId, host);}private?void?attemptAutoRecovery(String shardId, String host)?{try?{// 檢查副本集狀態if?(host.contains("/")) {String[] parts = host.split("/");String replSetName = parts[0];String[] hosts = parts[1].split(",");// 嘗試連接副本集的其他成員for?(String memberHost : hosts) {if?(testConnection(memberHost)) {log.info("副本集 {} 的成員 {} 仍然可用", replSetName, memberHost);return;}}}// 如果所有成員都不可用,嘗試重啟服務log.warn("分片 {} 的所有成員都不可用,需要手動干預", shardId);}?catch?(Exception e) {log.error("自動恢復失敗", e);}}private?boolean?testConnection(String host)?{try?{MongoClient testClient = MongoClients.create("mongodb://"?+ host);testClient.getDatabase("admin").runCommand(new?Document("ping",?1));testClient.close();return?true;}?catch?(Exception e) {return?false;}}
}

總結

MongoDB分片技術是處理大規模數據的重要解決方案。成功實施需要考慮:

  1. 分片鍵設計:選擇合適的分片鍵是關鍵,需要平衡查詢性能和數據分布

  2. 架構規劃:合理規劃Config Server、分片和mongos的部署

  3. 查詢優化:盡量包含分片鍵以避免跨分片查詢

  4. 監控運維:建立完善的監控體系,及時發現和處理問題

最佳實踐:

  • 選擇高基數、查詢頻繁的字段作為分片鍵

  • 使用復合分片鍵提高查詢效率

  • 定期監控數據分布和性能指標

  • 建立自動化的故障檢測和恢復機制

通過合理的設計和實施,MongoDB分片可以為應用提供優秀的水平擴展能力。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/92118.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/92118.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/92118.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python開發環境PyCharm下載與安裝

python下載 python下載地址&#xff1a; Download Python | Python.org 上面的下載速度慢的話&#xff0c;用下面的地址下載&#xff08;window&#xff09;&#xff1a; https://download.csdn.net/download/liangmengbk/91580033 PyCharm下載 PyCharm下載地址&#xff1a…

汽車供應鏈PPAP自動化審核指南:如何用AI實現規則精準匹配與文件智能校驗

在汽車行業質量管理的核心環節&#xff0c;PPAP&#xff08;生產件批準程序&#xff09;審核長期困擾著供應商與主機廠。 隨著IATF 16949等標準持續升級、新能源零件復雜度激增&#xff0c;傳統人工審核模式正面臨系統性挑戰。 行業數據顯示&#xff0c;超過70%的SQE&#xf…

正則表達式在js中的應用

正則表達式在 JavaScript 中的應用非常廣泛&#xff0c;尤其是在字符串處理和驗證方面。以下是一些常見的正則表達式方法及其應用示例&#xff0c;包括 .test() 方法。 1. .test() 方法 .test() 方法用于測試一個字符串是否匹配正則表達式。如果匹配&#xff0c;返回 true&…

Rust視頻處理開源項目精選

Rust視頻處理開源項目精選 基于Rust實現的視頻處理示例 以下是一些基于Rust實現的視頻處理或多媒體相關的開源項目或示例,涵蓋編解碼、流媒體、分析工具等方向,可作為實際開發參考: 視頻編解碼與處理 rav1e:Rust編寫的AV1視頻編碼器,高性能且內存安全,適合研究視頻壓縮…

Python爬蟲實戰:研究pycrumbs庫,構建豆瓣讀書數據采集系統

1. 引言 1.1 研究背景 在大數據與人工智能技術快速發展的背景下,互聯網作為全球最大的信息載體,蘊含著海量結構化與非結構化數據。高效、合規地獲取這些數據成為數據分析、業務決策的前提。網絡爬蟲作為自動化數據采集工具,通過模擬人類瀏覽行為遍歷網頁并提取信息,已成為…

linux的用戶操作(詳細介紹)

在 Linux 系統中&#xff0c;用戶管理是系統管理員的核心工作之一&#xff0c;涉及用戶賬號的創建、修改、刪除、權限分配等操作。Linux 采用多用戶多任務機制&#xff0c;通過嚴格的用戶和組管理確保系統安全性和資源分配合理性。以下是 Linux 用戶操作的詳細介紹&#xff1a;…

k8s常見問題

以下是 Kubernetes 常見問題&#xff08;FAQ&#xff09;的整理&#xff0c;涵蓋了初學者和運維人員常遇到的痛點&#xff1a; ?一、部署與安裝問題? ?安裝太復雜&#xff1f;?? 解決方案&#xff1a;使用 ?kubeadm?&#xff08;官方工具&#xff09;、Minikube?&#…

RK Android14 新建分區恢復出廠設置分區數據不擦除及開機動畫自定義(一)

文章目錄 前言 一、分區創建與參數配置 二、分區掛載配置 三、SELinux 安全策略 四、系統初始化配置 五、開機動畫路徑重定向 總結 前言 本方案通過在 RK3568 Android 14 系統中創建一個獨立的 rk_partition 分區(128MB),實現以下核心功能: 出廠設置保護:該分區在恢復出廠…

如何快速給PDF加書簽--保姆級教程

買的電子書沒有目錄書簽看著不舒服&#xff0c;手動加書簽加到想吐。想有沒有辦法快速加書簽。這要分為PDF目錄部分可以被復制和不可被復制兩種情況。不可復制時&#xff0c;要用到工具把目錄提取出來&#xff0c;變成文字。 工具&#xff1a;Foxit Phantom福昕閱讀器&#xff…

Redis面試精講 Day 9:Redis模塊開發與擴展

【Redis面試精講 Day 9】Redis模塊開發與擴展 文章標簽 Redis,模塊開發,擴展機制,面試技巧,Redis模塊,Redis插件 文章簡述 本文是"Redis面試精講"系列第9天&#xff0c;聚焦Redis模塊開發與擴展機制。文章詳細解析Redis模塊系統的架構設計&#xff0c;包括模塊加…

八股訓練--Spring

目錄 一、引言 二、Spring 1.Spring框架的特性 2.介紹一下IOC和AOP 3.IOC和AOP都是如何實現的 4.怎么實現依賴注入 5.為什么AOP不用靜態代理 6.介紹一下反射 7.Spring如何解決循環依賴問題 8.Spring常用注解 9.Spring事務什么情況會失效 10.Bean的生命周期 11.Bean…

無公網環境下在centos7.9上使用kk工具部署k8s平臺(amd64架構)

文章目錄前言一、環境列表二、思路三、環境準備四、有網環境下準備文件1.下載所需的rpm包2.準備harbor需要用到的鏡像3. k8s的鏡像文件4、 生成離線安裝包5、harbor創建項目腳本五、無公網環境部署單點集群1、基礎環境安裝2、安裝harbor3 、 準備k8s鏡像4、安裝k8s六、無公網環…

Objective-C中非傳統設計模式的探索與實踐

本文還有配套的精品資源&#xff0c;點擊獲取 簡介&#xff1a;Objective-C的設計模式不僅僅局限于經典模式&#xff0c;還可以利用其動態特性實現一些非傳統的模式。本文介紹了一系列基于Objective-C動態特性的設計模式&#xff0c;包括使用協議代替類繼承、通過分類擴展類…

【筆記】重學單片機(51)(下)

中斷系統 正常運行過程中&#xff0c;被打斷進行另外工作&#xff0c;結束后回到原有進程。 5個中斷源 外部中斷源&#xff08;2個&#xff09;&#xff1a;INT0——由P3.2端口線引入&#xff0c;低電平或下降沿引起。INT1——由P3.3端口線引入&#xff0c;低電平或下降沿引起。…

Go實現程序啟動器進而實現隱藏真實內容

注意&#xff1a; 本文內容于 2025-08-03 01:10:35 創建&#xff0c;可能不會在此平臺上進行更新。如果您希望查看最新版本或更多相關內容&#xff0c;請訪問原文地址&#xff1a;Go實現程序啟動器進而實現隱藏真實內容。感謝您的關注與支持&#xff01; 突發奇想&#xff0c;…

Fiddler 中文版怎么用 實現接口抓包調試與前后端聯調閉環

API調試在現代開發流程中的地位愈發重要&#xff1a;接口數量激增、請求邏輯復雜、數據結構多變、安全校驗機制加嚴……一個小小的參數錯誤、一次隱蔽的跨域問題、一個環境配置疏漏&#xff0c;都可能導致長時間的排查成本。而擁有一款既強大又易用的調試工具&#xff0c;尤其是…

ollama 多實例部署

如果我們需要在一臺服務器上使用多個ollama服務&#xff0c;那么我們需要進行將ollama前端和ollama后端對應連接的操作&#xff0c;否則就會出現如下場景&#xff1a;我們可以在當前端口設置&#xff0c;這句話就是指明當前ollama實例使用哪個后端進行請求&#xff1a;export O…

orchestrator部署

場景&#xff1a; 用于管理MySQL高可用 下載jq包 每臺orchestrator集群機器上都進行下載。 # wget http://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm # rpm -ivh epel-release-latest-7.noarch.rpm # yum repolist ###檢查是否已經添加到源列表 # yum i…

CentOS 6.4 上安裝 Oracle 10.2.0.1 并升級到 10.2.0.4

目錄 一、系統檢查與設置 1. 檢查系統版本與磁盤空間 2. 修改系統參數 3. 創建組和用戶 4. 設置主機名 5. 檢查安裝軟件包 6. 設置 oracle 用戶環境變量 二、安裝 Oracle 軟件包 1. 安裝 10.2.0.1 安裝包 2. 安裝 10.2.0.4 補丁 三、建庫 四、配置監聽器 1. 編輯配…

【基于C# + HALCON的工業視系統開發實戰】二十六、車規級PCB全自動質檢:3D SPI+AI光學檢測融合方案

摘要&#xff1a;本文詳細闡述基于C# .NET Core 6與HALCON 24.11開發的車規級PCB板AOI智能檢測系統&#xff0c;提出3D SPI與AI光學檢測融合方案。系統通過結構光3D測量技術實現錫膏印刷質量檢測&#xff0c;結合多算法融合的自動光學檢測完成元件缺陷識別&#xff0c;構建SPI與…