你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
当数据的格式不适合执行所需的查询操作时,可在一个或多个数据存储中为这些数据生成预先填充的视图。 此方法可以支持高效的查询和数据提取,并提高应用程序性能。
上下文和问题
存储数据时,开发人员和数据管理员通常优先考虑数据的存储方式,而不是读取数据的方式。 所选的存储格式通常反映数据的格式、管理数据大小和数据完整性的要求以及正在使用的存储类型。 例如,使用NoSQL文档存储时,通常将数据表示为一系列聚合,每个聚合都包含该实体的所有信息。
但是,此方法可能对查询产生负面影响。 如果查询仅需要某些实体的数据子集(如多个客户的订单摘要,而不需要订单的所有详细信息),它必须从相关实体中提取所有数据,以便获取所需信息。
在读取时添加索引或重塑查询并不总是解决这种效率低下的问题。 许多存储系统无法在不影响写入性能的情况下,针对任意读模式重建索引。 跨实体聚合在查询时仍然很昂贵。 某些存储的查询能力受到设计上的限制。 由于这些约束,仅优化源存储中的读取路径通常是不够的。
解决方案
为了支持高效的查询,常见的解决方案是提前生成一个视图,该视图以适合所需结果集的格式具体化数据。 具体化视图模式介绍如何在以下环境中生成预填充的数据视图:源数据的格式不适合进行查询、难以生成适合的查询或数据或数据存储的性质导致查询性能较差。
在此模式中, 具体化视图 是一个读取模型或投影,用于保存从一个或多个源存储派生的数据。 专用应用组件或数据管道可以维护该投影,甚至跨存储边界也不例外。 查询方将投影视为只读对象。 此体系结构概念比数据库本机具体化视图对象更广,数据库引擎根据自己的特征约束定义、存储和刷新该对象。
这些具体化视图(仅包含查询所需的数据)允许应用程序快速获取所需的信息。 除了联接表格或合并数据实体外,具体化视图还可以包含计算列或数据项当前的值、对数据项合并值或执行转换的结果以及指定为查询的一部分的值。 甚至可以针对单个查询优化具体化视图。
关键的一点是,物化视图及其包含的数据都是可以完全舍弃的,因为它们都可以根据源数据存储中的数据完全重建。 查询使用者不会直接更新视图。 相反,专用组件、数据管道或数据库引擎维护它,因此它是专用缓存。
当视图的源数据发生更改时,视图必须更新才能包含新信息。 可以将此更新计划为自动进行,或者当系统检测到对原始数据的更改时。 在某些情况下,可能需要手动重新生成视图。 下图显示了如何使用具体化视图模式的示例。
问题和注意事项
在决定如何实现此模式时,请考虑以下几点:
查看刷新策略。 理想情况下,视图会重新生成以响应指示对源数据的更改的事件,尽管如果源数据快速更改,此方法可能会导致过多的开销。 或者,可以考虑使用计划的任务、外部触发器或通过手动操作重新生成视图。
刷新行为。 确定实现是执行完全重新生成还是以增量方式应用更改。 还需要确定刷新操作是否阻止读取。
这些决策确定查询是否返回可能过时的具体化数据,将具体化数据与未处理的源更改组合在一起以返回当前结果,或继续为最后一个完整版本提供服务,直到刷新完成。
刷新信号可靠性。 如果触发视图重新生成的激活信号丢失或延迟(例如,错过的更改源事件或失败的计划任务),则视图会无提示地提供过时的结果。 监控刷新时效性,并在视图未刷新的时长超过可接受的陈旧时间窗口时发出警报。
刷新计算成本。 重新生成视图会消耗与源数据量和转换复杂性成正比的计算资源。 对于快速更改源数据的事件驱动刷新,或要完全重新生成大型分析视图,刷新的计算成本可能是一个显著的成本驱动因素。 适当调整刷新频率和范围,以根据计算支出平衡数据新鲜度。
事件溯源依赖 在某些系统中,例如,使用 事件溯源模式 仅保留修改数据的事件的存储时,通常需要具体化视图。 通过检查所有事件确定当前状态来预填充视图,这可能是从事件存储获取信息的唯一方法。 如果不使用事件溯源,可考虑物化视图是否会有帮助。 具体化视图往往专门针对一个或多个查询量身定做。 如果使用了大量查询,具体化视图会产生不可接受的存储容量需求和存储成本。
数据一致性。 考虑在生成视图时对数据一致性的影响,以及当此过程按计划进行时更新视图时。 如果源数据在生成视图的同时发生更改,则视图中的数据副本与原始数据不完全一致。 最大过期窗口是刷新间隔或事件处理滞后的直接后果,因此在选择事件驱动、计划刷新或手动刷新之前,请定义可接受的过期时间。
查看存储位置。 视图所在的存储或分区无需与原始数据的相同。 可以合并几个不同分区中的子集。
发生丢失后重建。 如果视图丢失,则可以重新生成视图。 因此,如果视图是暂时性的,并且仅用于通过反映数据的当前状态来提高查询性能,或者为了提高可伸缩性,则可以将其存储在缓存或不太可靠的位置。
但是,如果刷新过程本身在中途失败——例如,计划执行的重新生成任务崩溃——则需要确定工作负载是应提供之前的完整视图、部分更新的视图,还是在重新生成成功之前完全不提供任何视图。
最安全的方法通常是采用原子发布或带版本的替换:在构建和验证新视图的同时,您的工作负载会继续提供服务,并使用上一个完整视图。 验证完成后切换到新视图。
计算列。 定义具体化视图时,请根据现有数据项的计算或转换、查询中传递的值或适当时基于这些值的组合添加数据项或列来最大化其值。
查看索引。 如果存储机制支持,请考虑将具体化视图编制索引,进一步提高性能。 许多关系数据库支持为视图编制索引。 但是,视图上的索引维护会在每次刷新周期中带来写入路径上的额外开销,因此需要权衡读取性能收益与额外的刷新时间和计算成本。
对视图的访问控制。 当具体化视图用于限制某些使用者可见的数据子集(例如出于安全或隐私原因),视图存储必须强制实施与源数据相同的或更严格的访问控制。 刷新管道必须排除非预期的列或行,因为一旦视图无意中包含了超出其预期范围的数据,就可能暴露受保护的数据。
视图上的数据生命周期。 将源数据的保留和删除要求应用于每个具体化视图。 在规定期限内同步源端删除和遮盖操作,并将每个视图纳入合规性监控。 有关详细信息,请参阅使用 Microsoft Purview 进行数据管理和安全基线。
查看生命周期管理。 将视图定义视为通过源代码管理和 CI/CD 管道管理的可部署项目,尤其是在以声明方式定义视图时。 如果没有生命周期管理,视图定义可以在环境之间偏移,从而导致开发、过渡和生产中的查询行为不一致。
何时使用此模式
在以下情况下使用此模式:
- 需要针对难以直接查询的数据创建视图,或者查询必须非常复杂,才能提取以规范化、半结构化或非结构化方式存储的数据。
- 你想要创建可重新生成或暂时性缓存的投影,以提高查询性能,或者创建用于构造 UI、报表或显示的数据传输对象的形状数据。
- 你需要支持偶尔联网或离线的场景,在这些场景中,无法始终连接到数据存储。 在这种情况下,可以在本地缓存视图。
- 你希望简化查询并公开用于试验的数据,但不需要了解源数据格式。 例如,通过在一个或多个数据库或 NoSQL 存储中的一个或多个域中联接不同的表格,然后设置数据格式以满足其最终用途。
- 你希望提供对源数据中特定子集的访问权限,而出于安全或隐私原因,这些数据通常不应被普遍访问、允许修改或完全向用户暴露。
- 你希望桥接不同的数据存储,以利用其单个功能。 例如,可以将写入效率高的云存储用作参考数据存储,并使用提供良好查询和读取性能的关系数据库来保存物化视图。
- 使用微服务时,请将它们松散耦合,包括它们的数据存储。 具体化视图可帮助你合并服务中的数据。 如果具体化视图在微服务体系结构或特定方案中不适用,请考虑具有与 域驱动设计(DDD) 相符的明确边界,并按需聚合其数据。
在以下情况下,此模式可能不适用:
- 源数据十分简单且易于查询。
- 源数据更改速度很快,或可在不使用视图的情况下访问源数据。 在这些情况下,请避免因创建视图而产生的处理开销。
- 一致性是重中之重。 视图可能无法始终与原始数据完全一致。
工作负载设计
架构师应评估如何在其工作负载的设计中使用“具体化视图模式”,以解决 Azure Well-Architected Framework 支柱中涵盖的目标和原则。 例如:
| 支柱 | 此模式如何支持支柱目标 |
|---|---|
| 通过缩放、数据和代码的优化,性能效率可帮助工作负荷高效地满足需求。 | 具体化视图存储复杂计算或查询的结果,而不需要数据库引擎或客户端为每个请求重新计算。 这种设计减少了总体资源消耗。 - PE:08 数据性能 |
如果此模式在某个支柱中引入权衡取舍,请将它们与其他支柱的目标进行对比。
Example
假设有一个销售应用程序,将 Order、OrderItem 和 Customer 实体存储在 Azure 表存储 中。 订单按客户 ID 进行分区,按订单 ID 订购项目,按区域对客户进行分区。 这些密钥支持应用程序的操作访问模式,但按产品分组的销售报表必须跨分区读取数据,并将其合并到应用程序代码中。
下图显示了一个具体化视图,用于存储电子类别中每个产品的总销售额和不同购买客户的数量。 源数据行和汇总值仅作说明,并非构成所示总计的完整输入数据集。
后台进程读取所需的源实体,将订单项关联到相应的订单和客户,并按产品汇总销售数据。 即使某位客户有多个订单或多个订单行,对于每个产品,也仅将该客户计数一次。 该过程将结果写入一个独立的摘要表,其中产品类别为 PartitionKey,产品 ID 为 RowKey。 此摘要表是由应用程序维护的投影,而不是数据库原生的物化视图。
然后,仪表板可以查询电子分区,而不是针对每个请求重复跨分区读取和聚合。 查找一个产品会同时返回两个键。 有关这些键的查询性能影响,请参阅 设计查询。
按符合报表的可接受过期窗口的计划刷新摘要。 在发布新版本之前生成并验证新版本,以便读者在重新生成期间继续使用以前的完整版本。 刷新仍会产生跨分区读取和聚合成本,但重复的报表查询会重复使用结果。 只有在后续刷新包含这些源更改后,这些更改才会显示出来。
后续步骤
- 创建索引视图 描述 SQL 数据库如何通过对视图创建索引来持久保存计算视图结果。
- Azure Cosmos DB for NoSQL 中的变更馈送设计模式介绍了变更馈送使用者如何维护物化视图。
- 具体化视图概述介绍了Azure 数据资源管理器中的具体化视图。
- Azure 托管的 Redis 可以将预先计算的查询结果缓存起来,作为位于持久性数据存储之前的一层读取优化层。
- Azure 表存储可以存储应用程序逻辑或后台进程生成的预计算视图数据。
相关资源
实现此模式时,以下模式也可能有用:
- 命令和查询责任分离 (CQRS) 模式。 用于在基础数据值更改时对发生的事件作出响应,从而更新具体化视图中的信息。
- 事件溯源模式。 与 CQRS 模式一起使用,以在具体化视图中维护信息。 当物化视图所依据的数据值发生变化时,系统可以触发描述这些变化的事件,并将其保存在事件存储中。
- 索引表模式。 通常由主键整理具体化视图中的数据,但查询可能需要通过检查其他字段中的数据来从此视图检索信息。 使用此模式可为不支持原生辅助索引的数据存储中的数据集创建辅助索引。