Day28-11|6.3.4 逻辑结构设计:把概念模型转换成可约束、可查询、可演进的结构

本文对应《系统架构设计师教程(第2版)》第6章“数据库设计基础知识”6.3.4“逻辑结构设计”。逻辑结构设计是在概念结构设计完成后,按照选定的数据模型把实体、属性、联系和业务规则转换为逻辑模式。对于关系数据库,它通常表现为表、列、主键、外键、约束、视图和事务边界;对于其他数据库,则表现为文档聚合、图节点和边、键值结构或列族。逻辑设计要保持业务语义,同时为物理设计和应用架构提供稳定、清晰的输入。

一、学习目标与考试定位

完成本节后,应能够说明逻辑结构设计的目标、输入和输出;掌握 E-R 模型向关系模型转换的基本规则;能够处理一对一、一对多、多对多、弱实体、泛化、递归联系和多值属性;能够设计主键、候选键、外键、约束、视图和逻辑事务;能够结合范式和访问模式进行模型优化;能够在案例题中分析逻辑结构与业务边界、接口契约和物理实现之间的冲突。

软考高级题目可能给出一张 E-R 图,要求转换为关系模式;也可能给出关系表,让考生判断主键和外键是否正确,指出是否存在冗余、更新异常或多对多关系处理错误。答题时要写出转换规则和理由,不能只画几张表。工程题还需补充约束、数据历史、服务边界、读模型、模式兼容和迁移方案。

二、逻辑结构设计的定位

逻辑模型位于概念模型和物理模型之间。概念模型关注现实业务的对象和关系,不依赖数据库产品;逻辑模型选择某一种数据组织方式,明确结构、键、约束、视图和逻辑操作;物理模型再决定索引、分区、分片、副本和存储参数。

逻辑结构设计既不能停留在业务名词层面,也不能过早陷入磁盘页、硬件型号和厂商参数。它要表达足够精确的数据语义,使开发、测试、数据治理和运维能够依据同一模型工作,同时为不同的物理实现保留空间。

三、E-R 模型向关系模型转换

1. 强实体转换

一个强实体通常转换为一个关系,实体属性转换为列,实体标识转换为候选键或主键。复合属性可以按业务查询和原子性要求拆分;派生属性如果可稳定计算,可以不持久化,但若为了审计和性能保存,必须明确来源和更新规则。

2. 一对一联系转换

一对一联系可以把一方主键作为另一方外键,也可以把两个实体合并。选择时要考虑生命周期、访问频率、可选性、权限和变化独立性。如果两个实体总是同时创建、同时访问且生命周期一致,合并可能简单;如果敏感字段、可选信息或独立生命周期明显,拆分并用外键关联更合适。

3. 一对多联系转换

一对多联系通常把“一”方的主键放到“多”方作为外键。例如一个客户有多个订单,订单关系保存 CustomerId。若每个订单必须属于一个客户,外键应非空;若订单允许匿名购买,则需要明确匿名标识和审计规则,不能只把外键设为空而不定义语义。

4. 多对多联系转换

多对多联系要转换为独立关系,至少包含双方主键作为外键,并根据联系属性确定主键。例如订单和商品转换为 OrderItem(OrderId, ProductId, Quantity, Price)。若同一商品可以在一个订单中出现多行,则需要明细序号、批次或其他业务标识,联合主键不能机械套用。

5. 弱实体转换

弱实体需要包含所属强实体的标识,并把部分标识和强实体标识共同组成主键,或引入稳定代理键同时保留业务唯一性。例如发票明细依赖发票存在,关系中必须保留 InvoiceId 和明细序号。弱实体不能脱离拥有者独立存在,删除策略也要随生命周期明确。

四、特殊结构的逻辑转换

1. 多值属性

多值属性不能简单放在一个逗号分隔字段中。可以建立独立关系,例如 CustomerPhone(CustomerId, Phone, Type, IsPrimary),并对客户、号码和主号码规则设置约束。是否允许同一号码被多个客户使用,要由业务需求决定。

2. 递归联系

实体与自身的联系要用自引用外键或独立联系关系表示。例如员工汇报给员工,Employee(EmployeeId, ManagerId) 可以表达简单层级;若要保存任职时间、角色和历史,则应建立 EmployeeAssignment 或管理关系表,并检查循环和层级深度。

3. 泛化与特化

泛化和特化可以采用单表继承、父表加子表、具体类表等方式。单表结构简单但可能有大量空列;父子表约束清晰但查询需要连接;具体类表减少连接但共同属性重复。选择要结合子类型数量、查询方式、变化频率和完整性需求,不能仅凭面向对象习惯决定。

4. 多元联系

供应商、商品和合同之间的三元联系不能总是拆成多个二元联系,否则可能丢失“三者共同成立”的语义。转换前要验证拆分后是否会产生错误组合和伪元组。对联系有独立属性或生命周期的情况,通常应建立联系实体。

五、主键、候选键和业务唯一性

逻辑设计要识别所有候选键并选择主键。主键应稳定、唯一、非空、短小且适合关联;可变姓名、地址和展示编号不宜直接作为长期主键。代理键可以解决技术稳定性,但不能替代业务唯一约束。例如用户内部 Id 是主键,登录名仍应根据业务规则设置唯一约束和历史策略。

联合主键适合表达联系实体和天然复合唯一性,但字段增加会扩大索引和外键;代理键简化引用,却需要额外唯一约束。对租户系统还要明确唯一性范围,是全局唯一还是租户内唯一,约束和索引必须体现这一点。

六、完整性约束与逻辑规则

逻辑模型应明确实体完整性、参照完整性、域完整性和用户定义完整性。主键约束保证实体可识别,外键约束保证引用存在,非空和检查约束限制域,状态和跨记录规则则可能需要触发器、服务逻辑、状态机或事务。

约束不是越多越好,而是要放在能够稳定执行且责任清晰的层次。数据库内的不变量要尽量由数据库保证;跨服务、跨库和外部系统规则要通过接口、消息、幂等、补偿和对账实现。每条约束都应有来源、验证方法和异常处理。

七、规范化与逻辑模型优化

逻辑设计通常以 3NF 或 BCNF 作为减少冗余和异常的参考。设计者要根据函数依赖分解关系,检查主键、依赖和联系是否合理,并验证无损连接与依赖保持。对核心交易事实,规范化通常有助于保持一致性。

但规范化不是脱离场景的机械目标。查询模型如果频繁连接大表、需要复杂聚合,可以建立局部反规范化结构、物化视图、缓存或读模型。逻辑层要明确哪些是权威事实,哪些是派生投影,记录刷新、延迟、重建和对账机制,避免读模型被误当作主数据。

八、视图与逻辑外模式

视图可以为不同角色提供稳定的逻辑接口,隐藏内部表结构和敏感字段,限制行列范围,简化复杂查询。例如财务视图提供已结算订单,客服视图屏蔽支付卡号,运营视图提供聚合指标。视图的定义、权限、性能、可更新性和版本都要纳入管理。

在服务化架构中,数据服务或 API 可以承担比数据库视图更强的行为和权限控制。服务消费者不应直接依赖其他服务的内部表;若多个服务共享数据库,至少要明确允许访问的逻辑视图和变更契约,否则逻辑模式会成为跨团队耦合中心。

九、逻辑模型与事务边界

逻辑结构要支持业务事务,但不能把所有相关数据都塞进一个数据库事务。先根据业务不变量确定必须原子完成的操作,再确定数据边界。例如订单创建和订单明细写入可以在一个事务中,支付渠道调用不应长时间占用数据库事务;库存预占、支付确认和订单完成可以使用状态机和消息协调。

逻辑模型应记录状态、事件、幂等键和补偿关系。对“扣款成功但订单更新失败”“消息重复”“请求超时后客户端重试”等场景,模型要能表达处理中、成功、失败、待确认和已补偿等状态,不能只设置一个简单的成功/失败字段。

十、逻辑模式演进

逻辑模式变化要考虑旧应用、接口、报表、同步任务和历史数据。常用策略是向后兼容:先增加可选结构,再部署能够同时读写旧新结构的应用,完成回填和校验后切换读取,最后停止旧结构并清理。删除和重命名字段要设置弃用期、调用方通知和使用监控。

逻辑模型、数据字典、迁移脚本、约束和视图都应进入配置管理。每次变更记录原因、影响、版本、测试、批准人和回滚条件。对不能回滚的数据转换,应先备份、演练和保留映射,避免结构变更成为不可逆风险。

十一、案例分析:把 E-R 图转换成订单逻辑模型

某系统的概念模型包含客户、订单、商品、支付和物流。一个客户可以有多个订单,一个订单包含多个商品,一个订单可有多次支付尝试,物流会产生多个轨迹事件。初版设计把商品编号列表放在订单表,把支付状态直接作为订单字段,把物流最近状态覆盖写入订单。

问题分析

商品列表是多对多联系,应有订单明细及数量、成交价等联系属性;支付存在多次尝试,单一支付状态无法保留失败、重试和待确认过程;物流轨迹是追加事件,覆盖最近状态会丢失历史和审计证据。初版逻辑模型没有正确转换联系和生命周期。

改进方案

建立 Customer、Order、OrderItem、PaymentAttempt 和 LogisticsEvent 等逻辑关系。OrderItem 通过 OrderId、ProductId 或明细序号标识,保存成交快照;PaymentAttempt 保存渠道、请求号、金额、状态、时间和响应,利用幂等键防止重复;LogisticsEvent 保存事件类型、时间、地点和来源,Order 可保留当前物流状态作为派生字段,但明确其来源和重建机制。订单、明细和核心状态在合理事务内更新,支付和物流通过消息、状态机、重试和补偿协同。

十二、综合知识练习与详细答案

练习一

一个客户可以有多个地址,每个订单必须保留下单时使用的地址。请设计逻辑结构。

参考答案:客户地址应作为独立关系,如 CustomerAddress(AddressId, CustomerId, AddressContent, Type, ValidFrom, ValidTo, Status);订单可以引用下单时地址的 AddressId,同时保存订单级地址快照字段,或建立 OrderAddressSnapshot 独立关系。不能只通过当前地址关联订单,否则客户修改地址会改变历史订单。需要明确地址删除、归档、权限和快照校验策略。

练习二

为什么“订单表中保存商品编号列表”不适合作为逻辑模型?

参考答案:它无法表达数量、成交价、优惠、批次和状态等联系属性,不能有效建立外键、唯一性和查询索引,也不便于统计和并发更新。订单与商品是多对多联系,应转换为订单明细关系;订单明细保存双方标识和联系属性,并根据业务决定主键和重复规则。

练习三

逻辑模型已经达到 3NF,为什么仍可能需要建立读模型?

参考答案:3NF主要减少冗余和更新异常,不保证所有访问场景的查询性能。高频详情、报表、搜索或跨服务查询可能需要多个连接和聚合,达到性能目标时可以建立物化视图、缓存或事件驱动读模型。必须区分权威事实和派生投影,明确同步延迟、失败补偿、对账、重建和权限,不能让冗余结构无控制地成为第二个事实源。

十三、论文写作素材

论文中可以这样描述逻辑结构设计:在概念模型确认后,我依据实体、联系、基数、生命周期和访问场景完成逻辑转换。强实体转换为关系,一对多联系在多端保存外键,多对多联系转换为联系关系并保存联系属性,弱实体使用拥有者标识和局部标识共同确定;对递归、泛化、多值和时态关系分别选择自引用、父子关系、独立关系和历史结构。

我为核心实体选择稳定主键,为可变业务属性设置独立唯一约束,使用非空、检查、外键和状态规则保证完整性;通过函数依赖分析和规范化减少冗余,对高频读取在测量基础上建立局部读模型,并明确权威来源、同步、对账和重建。将视图、数据服务和权限作为逻辑外模式,避免应用直接依赖内部表结构。

逻辑模型与事务边界、接口契约和模式演进协同设计。对跨服务一致性使用消息、幂等、状态机、补偿和人工对账;对结构变更采用兼容字段、回填、灰度、版本和回滚策略。逻辑模型、数据字典、约束和迁移脚本进入配置管理,经过评审、测试和审计后实施。

十四、今日复盘

逻辑结构设计的核心是把概念语义落成可实现的数据结构。实体转换为关系,联系根据基数转换为外键或联系表,联系属性不能丢失;弱实体、递归、多值、泛化和时态结构要单独处理。主键负责稳定识别,业务唯一性要独立表达,约束负责保护不变量,规范化减少冗余,读模型在有测量依据时承担查询性能。

记忆主线:

概念定对象与关系,逻辑定表、键、约束和事务;先保持事实正确,再按访问模式优化,并为兼容、同步、回滚和重建留下路径。

下一篇进入 6.3.5 物理设计,重点学习存储结构、索引、分区、分片、聚簇、容量规划和性能验证。