MongoDB物模型存储方案分析

MongoDB中物模型存储方案设计分析

1. MongoDB数据模型特点

1.1 文档型数据库优势

MongoDB作为文档型NoSQL数据库,在物模型存储方面具有以下天然优势:

  • 灵活的Schema设计:无需预定义严格的表结构
  • 原生JSON支持:天然适合存储物模型的JSON定义
  • 嵌套文档支持:支持复杂的嵌套数据结构
  • 动态字段:支持字段的动态添加和修改
  • 水平扩展:原生支持分片和集群

1.2 MongoDB vs 关系型数据库对比

特性MongoDBMySQLPostgreSQL
数据模型文档型关系型关系型(支持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 开发阶段
  1. 原型验证:先用嵌入式存储快速验证
  2. 性能测试:针对实际数据量进行性能测试
  3. 逐步优化:根据访问模式逐步优化存储结构
7.2.2 生产部署
  1. 分片策略:根据productKey进行分片
  2. 副本集:配置合适的副本集保证高可用
  3. 监控告警:建立完善的监控和告警机制
// 分片配置示例
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需要综合考虑团队技术栈、项目规模、性能要求等因素,在充分评估的基础上做出决策。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值