MongoDB中物模型存储方案设计分析
1. MongoDB数据模型特点
1.1 文档型数据库优势
MongoDB作为文档型NoSQL数据库,在物模型存储方面具有以下天然优势:
- 灵活的Schema设计:无需预定义严格的表结构
- 原生JSON支持:天然适合存储物模型的JSON定义
- 嵌套文档支持:支持复杂的嵌套数据结构
- 动态字段:支持字段的动态添加和修改
- 水平扩展:原生支持分片和集群
1.2 MongoDB vs 关系型数据库对比
| 特性 | MongoDB | MySQL | PostgreSQL |
|---|---|---|---|
| 数据模型 | 文档型 | 关系型 | 关系型(支持JSON) |
| Schema灵活性 | 极高 | 低 | 中等 |
| JSON支持 | 原生 | 支持 | 强支持 |
| 复杂查询 | 强 | 强 | 强 |
| 事务支持 | 支持 | 强 | 强 |
| 水平扩展 | 原生支持 | 复杂 | 复杂 |
2. MongoDB物模型存储方案设计
2.1 方案一:完全嵌入式存储(Embedded Document)
2.1.1 数据结构设计
// thingModel collection
{
"_id": ObjectId("..."),
"productKey": "smart_sensor_v1",
"version": "1.0.0",
"modelName": "智能传感器物模型",
"description": "温湿度传感器物模型定义",
"status": "published", // draft, published, deprecated
"properties": [
{
"identifier": "temperature",
"name": "温度",
"dataType": "FLOAT",
"unit": "°C",
"accessMode": "r",
"required": true,
"range": {
"min": -40,
"max": 80
},
"description": "环境温度值"
},
{
"identifier": "humidity",
"name": "湿度",
"dataType": "FLOAT",
"unit": "%",
"accessMode": "r",
"required": true,
"range": {
"min": 0,
"max": 100
}
}
],
"events": [
{
"identifier": "temperature_alarm",
"name": "温度告警",
"type": "alert",
"required": false,
"description": "温度超出正常范围时触发",
"parameters": [
{
"identifier": "current_temp",
"name": "当前温度",
"dataType": "FLOAT",
"unit": "°C"
},
{
"identifier": "threshold",
"name": "阈值",
"dataType": "FLOAT",
"unit": "°C"
},
{
"identifier": "alarm_level",
"name": "告警级别",
"dataType": "ENUM",
"enumValues": ["low", "medium", "high", "critical"]
}
]
},
{
"identifier": "device_online",
"name": "设备上线",
"type": "info",
"required": false,
"parameters": [
{
"identifier": "timestamp",
"name": "上線時間",
"dataType": "TIMESTAMP"
},
{
"identifier": "ip_address",
"name": "IP地址",
"dataType": "STRING"
}
]
}
],
"services": [
{
"identifier": "restart",
"name": "重启设备",
"callType": "async",
"required": false,
"description": "远程重启设备",
"inputParams": [
{
"identifier": "delay",
"name": "延迟时间",
"dataType": "INT32",
"unit": "秒",
"required": false,
"defaultValue": 0
}
],
"outputParams": [
{
"identifier": "result",
"name": "执行结果",
"dataType": "BOOLEAN"
},
{
"identifier": "message",
"name": "结果消息",
"dataType": "STRING"
}
]
},
{
"identifier": "config_update",
"name": "配置更新",
"callType": "sync",
"required": false,
"inputParams": [
{
"identifier": "config_data",
"name": "配置数据",
"dataType": "STRUCT",
"specs": {
"report_interval": {
"dataType": "INT32",
"unit": "秒"
},
"threshold_temp": {
"dataType": "FLOAT",
"unit": "°C"
}
}
}
],
"outputParams": [
{
"identifier": "success",
"name": "更新结果",
"dataType": "BOOLEAN"
}
]
}
],
"metadata": {
"createdBy": "admin",
"createdAt": ISODate("2024-01-01T00:00:00Z"),
"updatedBy": "admin",
"updatedAt": ISODate("2024-01-15T10:30:00Z"),
"tags": ["sensor", "temperature", "humidity"],
"category": "environmental_monitoring"
}
}
2.1.2 优点分析
查询性能优异:
- 单次查询获取完整物模型定义
- 无需JOIN操作,减少网络往返
- 利用MongoDB的文档索引优化
数据一致性强:
- 原子性操作,要么全部成功要么全部失败
- 避免多文档事务的复杂性
- 版本控制简单直观
开发效率高:
- 数据结构直观,与应用模型匹配
- 无需复杂的ORM映射
- 支持富查询语法
2.1.3 缺点分析
文档大小限制:
- MongoDB单文档最大16MB限制
- 复杂物模型可能接近限制
- 大文档更新性能影响
部分更新复杂:
- 更新单个事件需要操作整个数组
- 并发更新可能导致冲突
- 无法对子元素进行细粒度权限控制
2.2 方案二:混合引用式存储(Referenced Documents)
2.2.1 数据结构设计
// thingModel collection - 主文档
{
"_id": ObjectId("..."),
"productKey": "smart_sensor_v1",
"version": "1.0.0",
"modelName": "智能传感器物模型",
"status": "published",
"properties": [...], // 属性仍保持嵌入式
"eventRefs": [
ObjectId("event1_id"),
ObjectId("event2_id")
],
"serviceRefs": [
ObjectId("service1_id"),
ObjectId("service2_id")
],
"metadata": {...}
}
// thingModelEvent collection - 事件文档
{
"_id": ObjectId("event1_id"),
"modelId": ObjectId("model_id"),
"productKey": "smart_sensor_v1",
"version": "1.0.0",
"identifier": "temperature_alarm",
"name": "温度告警",
"type": "alert",
"required": false,
"description": "温度超出正常范围时触发",
"parameters": [
{
"identifier": "current_temp",
"name": "当前温度",
"dataType": "FLOAT",
"unit": "°C"
}
],
"createdAt": ISODate("..."),
"updatedAt": ISODate("...")
}
// thingModelService collection - 服务文档
{
"_id": ObjectId("service1_id"),
"modelId": ObjectId("model_id"),
"productKey": "smart_sensor_v1",
"version": "1.0.0",
"identifier": "restart",
"name": "重启设备",
"callType": "async",
"required": false,
"inputParams": [...],
"outputParams": [...],
"createdAt": ISODate("..."),
"updatedAt": ISODate("...")
}
2.2.2 优点分析
灵活的更新操作:
- 可以独立更新单个事件或服务
- 减少文档锁竞争
- 支持细粒度的权限控制
更好的查询性能(特定场景):
- 支持事件、服务的独立查询和聚合
- 可以为不同类型建立专门索引
- 适合复杂的分析查询
存储效率:
- 避免大文档的存储开销
- 支持更精细的数据分片策略
2.2.3 缺点分析
查询复杂性:
- 需要多次查询或使用$lookup聚合
- 网络往返次数增加
- 应用逻辑复杂度提升
数据一致性挑战:
- 需要多文档事务保证一致性
- 引用完整性需要应用层维护
- 版本管理复杂化
2.3 方案三:分层混合式存储(Hybrid Approach)
2.3.1 设计思路
根据访问模式和数据特征,采用分层存储策略:
- 属性(Properties):访问频率高,结构相对简单 → 嵌入式存储
- 事件(Events):需要独立查询和分析 → 引用式存储
- 服务(Services):更新频率高,需要版本管理 → 引用式存储
2.3.2 数据结构设计
// thingModel collection - 核心定义
{
"_id": ObjectId("..."),
"productKey": "smart_sensor_v1",
"version": "1.0.0",
"modelName": "智能传感器物模型",
"status": "published",
// 属性:嵌入式存储(访问频率高,相对稳定)
"properties": [
{
"identifier": "temperature",
"name": "温度",
"dataType": "FLOAT",
"accessMode": "r",
"required": true,
"range": {"min": -40, "max": 80},
"unit": "°C"
}
],
// 事件和服务:引用式存储
"eventCount": 5,
"serviceCount": 3,
// 快速访问的元数据
"summary": {
"eventTypes": ["info", "alert", "error"],
"serviceTypes": ["sync", "async"],
"lastModified": ISODate("...")
},
"metadata": {
"createdAt": ISODate("..."),
"updatedAt": ISODate("..."),
"tags": ["sensor", "environmental"]
}
}
// 事件专用集合
// thingModelEvents collection
{
"_id": ObjectId("..."),
"modelId": ObjectId("model_id"),
"productKey": "smart_sensor_v1",
"modelVersion": "1.0.0",
"identifier": "temperature_alarm",
"name": "温度告警",
"type": "alert",
"severity": "high",
"category": "environmental",
"parameters": [...],
"triggers": {
"conditions": ["temperature > threshold"],
"frequency": "immediate"
},
"createdAt": ISODate("..."),
"updatedAt": ISODate("...")
}
// 服务专用集合
// thingModelServices collection
{
"_id": ObjectId("..."),
"modelId": ObjectId("model_id"),
"productKey": "smart_sensor_v1",
"modelVersion": "1.0.0",
"identifier": "restart",
"name": "重启设备",
"callType": "async",
"timeout": 30000,
"retryPolicy": {
"maxRetries": 3,
"backoffStrategy": "exponential"
},
"inputParams": [...],
"outputParams": [...],
"permissions": ["admin", "operator"],
"createdAt": ISODate("..."),
"updatedAt": ISODate("...")
}
3. 查询性能和索引策略
3.1 索引设计策略
3.1.1 物模型主集合索引
// thingModel collection 索引
db.thingModel.createIndex({"productKey": 1, "version": 1}, {unique: true})
db.thingModel.createIndex({"status": 1, "updatedAt": -1})
db.thingModel.createIndex({"metadata.tags": 1})
db.thingModel.createIndex({"metadata.category": 1})
// 属性字段的部分索引
db.thingModel.createIndex(
{"properties.identifier": 1},
{partialFilterExpression: {"properties": {$exists: true, $ne: []}}}
)
db.thingModel.createIndex({"properties.dataType": 1, "properties.accessMode": 1})
3.1.2 事件集合索引
// thingModelEvents collection 索引
db.thingModelEvents.createIndex({"modelId": 1, "identifier": 1}, {unique: true})
db.thingModelEvents.createIndex({"productKey": 1, "type": 1})
db.thingModelEvents.createIndex({"type": 1, "severity": 1, "category": 1})
db.thingModelEvents.createIndex({"createdAt": -1})
// 参数查询索引
db.thingModelEvents.createIndex({"parameters.dataType": 1})
db.thingModelEvents.createIndex({"triggers.conditions": 1})
3.1.3 服务集合索引
// thingModelServices collection 索引
db.thingModelServices.createIndex({"modelId": 1, "identifier": 1}, {unique: true})
db.thingModelServices.createIndex({"productKey": 1, "callType": 1})
db.thingModelServices.createIndex({"permissions": 1})
db.thingModelServices.createIndex({"timeout": 1, "callType": 1})
3.2 查询性能优化
3.2.1 常见查询模式优化
// 1. 获取完整物模型(优化后)
// 使用聚合管道一次性获取所有相关数据
db.thingModel.aggregate([
{
$match: {
"productKey": "smart_sensor_v1",
"version": "1.0.0"
}
},
{
$lookup: {
from: "thingModelEvents",
localField: "_id",
foreignField: "modelId",
as: "events"
}
},
{
$lookup: {
from: "thingModelServices",
localField: "_id",
foreignField: "modelId",
as: "services"
}
}
])
// 2. 查询特定类型事件(高效查询)
db.thingModelEvents.find({
"type": "alert",
"severity": "high",
"productKey": {$in: ["sensor_v1", "sensor_v2"]}
}).hint({"type": 1, "severity": 1, "category": 1})
// 3. 复杂条件查询(利用复合索引)
db.thingModelServices.find({
"callType": "async",
"timeout": {$gte: 10000},
"permissions": "admin"
}).hint({"callType": 1, "timeout": 1, "permissions": 1})
3.2.2 聚合查询优化
// 统计分析查询
db.thingModelEvents.aggregate([
{
$match: {
"createdAt": {
$gte: ISODate("2024-01-01"),
$lt: ISODate("2024-02-01")
}
}
},
{
$group: {
_id: {
type: "$type",
severity: "$severity",
productKey: "$productKey"
},
count: {$sum: 1},
avgParamCount: {$avg: {$size: "$parameters"}}
}
},
{
$sort: {"count": -1}
}
])
4. 数据一致性和事务策略
4.1 MongoDB事务支持
4.1.1 多文档事务示例
// 使用事务更新物模型及其关联数据
async function updateThingModelWithTransaction(modelData) {
const session = client.startSession();
try {
await session.withTransaction(async () => {
// 1. 更新主物模型
await db.thingModel.updateOne(
{"_id": modelData._id},
{
$set: {
"properties": modelData.properties,
"metadata.updatedAt": new Date(),
"eventCount": modelData.events.length,
"serviceCount": modelData.services.length
}
},
{session}
);
// 2. 删除旧的事件
await db.thingModelEvents.deleteMany(
{"modelId": modelData._id},
{session}
);
// 3. 插入新的事件
if (modelData.events.length > 0) {
await db.thingModelEvents.insertMany(
modelData.events.map(event => ({
...event,
modelId: modelData._id,
createdAt: new Date()
})),
{session}
);
}
// 4. 更新服务(类似处理)
await updateServices(modelData._id, modelData.services, session);
});
console.log("物模型更新成功");
} catch (error) {
console.error("事务失败:", error);
throw error;
} finally {
await session.endSession();
}
}
4.1.2 乐观锁机制
// 使用版本号实现乐观锁
{
"_id": ObjectId("..."),
"productKey": "smart_sensor_v1",
"version": "1.0.0",
"modelVersion": 1, // 内部版本号,用于乐观锁
"properties": [...],
"updatedAt": ISODate("...")
}
// 更新时检查版本号
async function updateWithOptimisticLock(modelId, updates, expectedVersion) {
const result = await db.thingModel.updateOne(
{
"_id": modelId,
"modelVersion": expectedVersion
},
{
$set: updates,
$inc: {"modelVersion": 1},
$currentDate: {"updatedAt": true}
}
);
if (result.matchedCount === 0) {
throw new Error("乐观锁冲突:数据已被其他操作修改");
}
return result;
}
4.2 数据完整性保证
4.2.1 应用层约束检查
// 物模型验证器
class ThingModelValidator {
static validate(modelData) {
// 1. 标识符唯一性检查
const identifiers = new Set();
// 检查属性标识符
modelData.properties.forEach(prop => {
if (identifiers.has(prop.identifier)) {
throw new Error(`属性标识符重复: ${prop.identifier}`);
}
identifiers.add(prop.identifier);
});
// 检查事件标识符
modelData.events.forEach(event => {
if (identifiers.has(event.identifier)) {
throw new Error(`事件标识符重复: ${event.identifier}`);
}
identifiers.add(event.identifier);
});
// 检查服务标识符
modelData.services.forEach(service => {
if (identifiers.has(service.identifier)) {
throw new Error(`服务标识符重复: ${service.identifier}`);
}
identifiers.add(service.identifier);
});
// 2. 数据类型验证
this.validateDataTypes(modelData);
// 3. 业务规则验证
this.validateBusinessRules(modelData);
}
static validateDataTypes(modelData) {
const validDataTypes = ['BOOLEAN', 'INT32', 'INT64', 'FLOAT', 'DOUBLE', 'STRING', 'ENUM', 'STRUCT', 'ARRAY'];
// 验证属性数据类型
modelData.properties.forEach(prop => {
if (!validDataTypes.includes(prop.dataType)) {
throw new Error(`无效的数据类型: ${prop.dataType}`);
}
});
}
}
4.2.2 数据库级约束
// 创建唯一性约束和验证规则
db.createCollection("thingModel", {
validator: {
$jsonSchema: {
bsonType: "object",
required: ["productKey", "version", "status"],
properties: {
productKey: {
bsonType: "string",
minLength: 1,
maxLength: 50
},
version: {
bsonType: "string",
pattern: "^\\d+\\.\\d+\\.\\d+$"
},
status: {
enum: ["draft", "published", "deprecated"]
},
properties: {
bsonType: "array",
items: {
bsonType: "object",
required: ["identifier", "name", "dataType"],
properties: {
identifier: {
bsonType: "string",
pattern: "^[a-zA-Z][a-zA-Z0-9_]*$"
},
dataType: {
enum: ["BOOLEAN", "INT32", "INT64", "FLOAT", "DOUBLE", "STRING", "ENUM", "STRUCT", "ARRAY"]
}
}
}
}
}
}
}
});
5. 具体实现示例
5.1 Java Spring Boot实现
5.1.1 实体类设计
// 物模型主实体
@Document(collection = "thingModel")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ThingModel {
@Id
private String id;
@Indexed(unique = true)
private String productKey;
private String version;
private String modelName;
@Indexed
private ModelStatus status;
// 属性:嵌入式文档
private List<Property> properties;
// 事件和服务数量(用于快速统计)
private Integer eventCount = 0;
private Integer serviceCount = 0;
// 快速查询摘要
private ModelSummary summary;
private ModelMetadata metadata;
@Data
public static class Property {
private String identifier;
private String name;
private DataType dataType;
private String unit;
private AccessMode accessMode;
private Boolean required;
private Object defaultValue;
private PropertyRange range;
private String description;
}
@Data
public static class ModelSummary {
private List<String> eventTypes;
private List<String> serviceTypes;
private Date lastModified;
}
}
// 事件实体
@Document(collection = "thingModelEvents")
@Data
public class ThingModelEvent {
@Id
private String id;
@Indexed
private String modelId;
@Indexed
private String productKey;
private String modelVersion;
@Indexed
private String identifier;
private String name;
@Indexed
private EventType type;
@Indexed
private String severity;
@Indexed
private String category;
private Boolean required;
private List<Parameter> parameters;
private EventTrigger triggers;
private Date createdAt;
private Date updatedAt;
}
// 服务实体
@Document(collection = "thingModelServices")
@Data
public class ThingModelService {
@Id
private String id;
@Indexed
private String modelId;
@Indexed
private String productKey;
private String modelVersion;
@Indexed
private String identifier;
private String name;
@Indexed
private CallType callType;
private Integer timeout;
private RetryPolicy retryPolicy;
private List<Parameter> inputParams;
private List<Parameter> outputParams;
@Indexed
private List<String> permissions;
private Date createdAt;
private Date updatedAt;
}
5.1.2 Repository层实现
// 物模型Repository
@Repository
public interface ThingModelRepository extends MongoRepository<ThingModel, String> {
Optional<ThingModel> findByProductKeyAndVersion(String productKey, String version);
List<ThingModel> findByStatus(ModelStatus status);
@Query("{'metadata.tags': ?0}")
List<ThingModel> findByTag(String tag);
@Query("{'properties.dataType': ?0}")
List<ThingModel> findByPropertyDataType(DataType dataType);
}
// 事件Repository
@Repository
public interface ThingModelEventRepository extends MongoRepository<ThingModelEvent, String> {
List<ThingModelEvent> findByModelId(String modelId);
List<ThingModelEvent> findByProductKeyAndType(String productKey, EventType type);
@Query("{'type': ?0, 'severity': ?1}")
List<ThingModelEvent> findByTypeAndSeverity(EventType type, String severity);
@Aggregation(pipeline = {
"{ $match: { type: ?0 } }",
"{ $group: { _id: '$productKey', count: { $sum: 1 } } }",
"{ $sort: { count: -1 } }"
})
List<EventCountByProduct> countEventsByTypeGroupByProduct(EventType type);
}
// 服务Repository
@Repository
public interface ThingModelServiceRepository extends MongoRepository<ThingModelService, String> {
List<ThingModelService> findByModelId(String modelId);
List<ThingModelService> findByCallType(CallType callType);
@Query("{'permissions': ?0}")
List<ThingModelService> findByPermission(String permission);
}
5.1.3 服务层实现
@Service
@Transactional
@Slf4j
public class ThingModelService {
@Autowired
private ThingModelRepository thingModelRepository;
@Autowired
private ThingModelEventRepository eventRepository;
@Autowired
private ThingModelServiceRepository serviceRepository;
@Autowired
private MongoTemplate mongoTemplate;
// 创建完整物模型
public ThingModel createThingModel(ThingModelDto dto) {
// 1. 验证数据
ThingModelValidator.validate(dto);
// 2. 创建主文档
ThingModel model = new ThingModel();
model.setProductKey(dto.getProductKey());
model.setVersion(dto.getVersion());
model.setModelName(dto.getModelName());
model.setStatus(ModelStatus.DRAFT);
model.setProperties(dto.getProperties());
model.setEventCount(dto.getEvents().size());
model.setServiceCount(dto.getServices().size());
// 设置摘要信息
ModelSummary summary = new ModelSummary();
summary.setEventTypes(dto.getEvents().stream()
.map(e -> e.getType().name())
.distinct()
.collect(Collectors.toList()));
summary.setServiceTypes(dto.getServices().stream()
.map(s -> s.getCallType().name())
.distinct()
.collect(Collectors.toList()));
summary.setLastModified(new Date());
model.setSummary(summary);
// 3. 保存主文档
model = thingModelRepository.save(model);
// 4. 保存事件文档
if (!dto.getEvents().isEmpty()) {
List<ThingModelEvent> events = dto.getEvents().stream()
.map(eventDto -> convertToEventEntity(eventDto, model.getId(), dto.getProductKey(), dto.getVersion()))
.collect(Collectors.toList());
eventRepository.saveAll(events);
}
// 5. 保存服务文档
if (!dto.getServices().isEmpty()) {
List<ThingModelService> services = dto.getServices().stream()
.map(serviceDto -> convertToServiceEntity(serviceDto, model.getId(), dto.getProductKey(), dto.getVersion()))
.collect(Collectors.toList());
serviceRepository.saveAll(services);
}
log.info("物模型创建成功: productKey={}, version={}", dto.getProductKey(), dto.getVersion());
return model;
}
// 获取完整物模型
public ThingModelDto getCompleteThingModel(String productKey, String version) {
// 使用聚合查询一次性获取所有数据
Aggregation aggregation = Aggregation.newAggregation(
Aggregation.match(Criteria.where("productKey").is(productKey)
.and("version").is(version)),
Aggregation.lookup("thingModelEvents", "_id", "modelId", "events"),
Aggregation.lookup("thingModelServices", "_id", "modelId", "services")
);
AggregationResults<Document> results = mongoTemplate.aggregate(
aggregation, "thingModel", Document.class);
if (results.getMappedResults().isEmpty()) {
throw new EntityNotFoundException("物模型不存在: " + productKey + ":" + version);
}
Document doc = results.getMappedResults().get(0);
return convertToThingModelDto(doc);
}
// 更新事件(独立更新)
public void updateEvent(String productKey, String version, String eventIdentifier,
ThingModelEventDto eventDto) {
// 1. 查找物模型
ThingModel model = thingModelRepository.findByProductKeyAndVersion(productKey, version)
.orElseThrow(() -> new EntityNotFoundException("物模型不存在"));
// 2. 更新事件
Query query = Query.query(Criteria.where("modelId").is(model.getId())
.and("identifier").is(eventIdentifier));
Update update = new Update()
.set("name", eventDto.getName())
.set("type", eventDto.getType())
.set("severity", eventDto.getSeverity())
.set("parameters", eventDto.getParameters())
.set("updatedAt", new Date());
UpdateResult result = mongoTemplate.updateFirst(query, update, ThingModelEvent.class);
if (result.getMatchedCount() == 0) {
throw new EntityNotFoundException("事件不存在: " + eventIdentifier);
}
// 3. 更新主文档的摘要信息
updateModelSummary(model.getId());
log.info("事件更新成功: productKey={}, eventId={}", productKey, eventIdentifier);
}
// 复杂查询示例
public List<ThingModelEvent> findEventsByComplexCriteria(EventQueryCriteria criteria) {
Query query = new Query();
if (criteria.getProductKeys() != null && !criteria.getProductKeys().isEmpty()) {
query.addCriteria(Criteria.where("productKey").in(criteria.getProductKeys()));
}
if (criteria.getEventType() != null) {
query.addCriteria(Criteria.where("type").is(criteria.getEventType()));
}
if (criteria.getSeverity() != null) {
query.addCriteria(Criteria.where("severity").is(criteria.getSeverity()));
}
if (criteria.getDateRange() != null) {
query.addCriteria(Criteria.where("createdAt")
.gte(criteria.getDateRange().getStart())
.lte(criteria.getDateRange().getEnd()));
}
// 添加排序
query.with(Sort.by(Sort.Direction.DESC, "createdAt"));
// 添加分页
if (criteria.getPageable() != null) {
query.with(criteria.getPageable());
}
return mongoTemplate.find(query, ThingModelEvent.class);
}
private void updateModelSummary(String modelId) {
// 重新计算并更新摘要信息
List<ThingModelEvent> events = eventRepository.findByModelId(modelId);
List<ThingModelService> services = serviceRepository.findByModelId(modelId);
ModelSummary summary = new ModelSummary();
summary.setEventTypes(events.stream()
.map(e -> e.getType().name())
.distinct()
.collect(Collectors.toList()));
summary.setServiceTypes(services.stream()
.map(s -> s.getCallType().name())
.distinct()
.collect(Collectors.toList()));
summary.setLastModified(new Date());
Query query = Query.query(Criteria.where("id").is(modelId));
Update update = new Update()
.set("summary", summary)
.set("eventCount", events.size())
.set("serviceCount", services.size())
.currentDate("metadata.updatedAt");
mongoTemplate.updateFirst(query, update, ThingModel.class);
}
}
6. MongoDB与关系型数据库方案对比
6.1 详细对比分析
| 维度 | MongoDB方案 | MySQL方案 | PostgreSQL+JSON方案 |
|---|---|---|---|
| 模式灵活性 | ★★★★★ | ★★☆☆☆ | ★★★★☆ |
| 查询性能 | ★★★★☆ | ★★★★★ | ★★★★☆ |
| 事务支持 | ★★★★☆ | ★★★★★ | ★★★★★ |
| 水平扩展 | ★★★★★ | ★★☆☆☆ | ★★☆☆☆ |
| 运维复杂度 | ★★★☆☆ | ★★★★☆ | ★★★☆☆ |
| 开发效率 | ★★★★★ | ★★★☆☆ | ★★★★☆ |
| 数据一致性 | ★★★★☆ | ★★★★★ | ★★★★★ |
| 学习成本 | ★★★☆☆ | ★★★★★ | ★★★☆☆ |
6.2 迁移策略
6.2.1 从MySQL到MongoDB迁移
// 迁移脚本示例
const migrationScript = {
// 1. 导出MySQL数据
async exportFromMySQL() {
const connection = mysql.createConnection(mysqlConfig);
// 导出物模型主数据
const models = await connection.query(`
SELECT tm.*,
JSON_ARRAYAGG(
JSON_OBJECT(
'identifier', tme.identifier,
'name', tme.name,
'type', tme.event_type,
'parameters', tme.parameters
)
) as events,
JSON_ARRAYAGG(
JSON_OBJECT(
'identifier', tms.identifier,
'name', tms.name,
'callType', tms.call_type,
'inputParams', tms.input_params,
'outputParams', tms.output_params
)
) as services
FROM thing_model tm
LEFT JOIN thing_model_event tme ON tm.id = tme.model_id
LEFT JOIN thing_model_service tms ON tm.id = tms.model_id
GROUP BY tm.id
`);
return models;
},
// 2. 转换并导入MongoDB
async importToMongoDB(mysqlData) {
const mongoClient = new MongoClient(mongoConfig);
await mongoClient.connect();
const db = mongoClient.db('iot_platform');
const modelsCollection = db.collection('thingModel');
const eventsCollection = db.collection('thingModelEvents');
const servicesCollection = db.collection('thingModelServices');
for (const mysqlModel of mysqlData) {
// 转换主文档
const mongoModel = {
productKey: mysqlModel.product_key,
version: mysqlModel.version,
modelName: mysqlModel.model_name,
status: mysqlModel.status,
properties: JSON.parse(mysqlModel.properties || '[]'),
eventCount: mysqlModel.events?.length || 0,
serviceCount: mysqlModel.services?.length || 0,
metadata: {
createdAt: mysqlModel.created_time,
updatedAt: mysqlModel.updated_time
}
};
// 插入主文档
const result = await modelsCollection.insertOne(mongoModel);
const modelId = result.insertedId;
// 转换并插入事件
if (mysqlModel.events) {
const events = mysqlModel.events.map(event => ({
...event,
modelId: modelId,
productKey: mysqlModel.product_key,
modelVersion: mysqlModel.version,
createdAt: new Date()
}));
await eventsCollection.insertMany(events);
}
// 转换并插入服务
if (mysqlModel.services) {
const services = mysqlModel.services.map(service => ({
...service,
modelId: modelId,
productKey: mysqlModel.product_key,
modelVersion: mysqlModel.version,
createdAt: new Date()
}));
await servicesCollection.insertMany(services);
}
}
await mongoClient.close();
}
};
7. 最佳实践建议
7.1 选择建议
7.1.1 优先选择MongoDB的场景
- 敏捷开发:需要快速迭代,物模型结构变化频繁
- 复杂嵌套:物模型包含大量嵌套结构和动态字段
- 水平扩展:预期数据量大,需要分片和集群支持
- JSON原生:应用大量使用JSON格式,减少转换开销
- NoSQL生态:团队熟悉NoSQL技术栈
7.1.2 谨慎选择MongoDB的场景
- 强一致性要求:对数据一致性要求极高的金融等场景
- 复杂关联查询:需要大量跨表JOIN操作
- 传统团队:团队对关系型数据库更熟悉
- 严格ACID:需要严格的ACID事务保证
7.2 实施建议
7.2.1 开发阶段
- 原型验证:先用嵌入式存储快速验证
- 性能测试:针对实际数据量进行性能测试
- 逐步优化:根据访问模式逐步优化存储结构
7.2.2 生产部署
- 分片策略:根据productKey进行分片
- 副本集:配置合适的副本集保证高可用
- 监控告警:建立完善的监控和告警机制
// 分片配置示例
sh.enableSharding("iot_platform")
sh.shardCollection("iot_platform.thingModel", {"productKey": 1})
sh.shardCollection("iot_platform.thingModelEvents", {"productKey": 1, "modelId": 1})
sh.shardCollection("iot_platform.thingModelServices", {"productKey": 1, "modelId": 1})
7.3 性能优化建议
7.3.1 查询优化
- 合理使用索引,避免全表扫描
- 利用聚合管道减少网络往返
- 使用投影减少返回数据量
- 考虑读写分离和缓存策略
7.3.2 存储优化
- 控制文档大小,避免超出16MB限制
- 合理设计分片键,保证数据均匀分布
- 定期清理历史数据和无用索引
- 使用压缩减少存储空间
8. 总结
MongoDB在物模型存储方面具有显著优势,特别适合以下场景:
核心优势:
- 灵活的Schema设计,适应物模型的多样性
- 原生JSON支持,减少数据转换开销
- 强大的查询和聚合能力
- 出色的水平扩展能力
推荐方案:
- 小型项目:完全嵌入式存储
- 中型项目:分层混合式存储
- 大型项目:引用式存储 + 性能优化
技术建议:
- 合理设计索引策略,优化查询性能
- 使用事务保证数据一致性
- 建立完善的数据验证和约束机制
- 考虑与关系型数据库的混合架构
选择MongoDB需要综合考虑团队技术栈、项目规模、性能要求等因素,在充分评估的基础上做出决策。
1219

被折叠的 条评论
为什么被折叠?



