ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MikroORM 物化视图(Materialized Views)实战指南:定义、刷新与 Schema 生成

MikroORM 物化视图(Materialized Views)实战指南:定义、刷新与 Schema 生成 后端【免费下载链接】mikro-ormTypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, MariaDB, MS SQL Server, PostgreSQL and SQLite/libSQL databases.项目地址https://gitcode.com/gh_mirrors/mi/mikro-orm点击查看免费下载物化视图Materialized View将查询结果物理存储下来以数据新鲜度为代价换取更快的读取性能与普通视图不同物化视图必须显式刷新才能反映底层数据的变化。本文基于 MikroORM 当前仓库v7.0 文档线讲解如何通过实体定义、refreshMaterializedView()调用与 Schema Generator 全流程管理 PostgreSQL 物化视图读者读完可以掌握四种实体定义方式、WITH DATA/WITH NO DATA的创建控制、并发刷新、只读语义以及物化视图索引的 diff 与生成细节。物化视图与普通视图的本质区别在 MikroORM 中视图View是一种特殊的实体形态它不对应真实数据表而是对应一段SELECT查询表达式。普通视图view: true只是查询的别名每次查询都会实时执行底层 SQL物化视图view: { materialized: true }则把查询结果物理落盘后续读取直接扫描存储的数据因此读取更快但数据是快照需要手动刷新。这一区分在仓库元数据层有明确体现。在 packages/core/src/metadata/types.ts 中view选项的类型定义为view?: boolean | { materialized?: boolean; withData?: boolean };view: true表示普通视图view: { materialized: true }表示物化视图仅 PostgreSQLview: { materialized: true, withData: false }表示创建时不填充数据的物化视图。平台支持度由 packages/core/src/platforms/Platform.ts 的supportsMaterializedViews()控制基类默认返回false而 PostgreSQL 平台在 packages/sql/src/dialects/postgresql/BasePostgreSqlPlatform.ts 中将其重写为true——这正是物化视图仅限 PostgreSQL限制的底层来源。定义物化视图实体创建物化视图实体只需在实体选项中配置view: { materialized: true }并通过expression提供视图的查询定义。MikroORM 支持四种定义风格下面以一个统计每位作者书籍数量的AuthorStats实体为例逐一说明。defineEntity class推荐import { defineEntity, p } from mikro-orm/postgresql; const AuthorStatsSchema defineEntity({ name: AuthorStats, tableName: author_stats_matview, view: { materialized: true }, expression: select a.id, a.name, count(b.id)::int as book_count from author a left join book b on b.author_id a.id group by a.id , properties: { id: p.integer().primary(), name: p.string(), bookCount: p.integer(), }, }); export class AuthorStats extends AuthorStatsSchema.class {} AuthorStatsSchema.setClass(AuthorStats);defineEntity仅 Schema 常量如果不希望定义实体类只导出 Schema 常量即可import { defineEntity, p } from mikro-orm/postgresql; export const AuthorStats defineEntity({ name: AuthorStats, tableName: author_stats_matview, view: { materialized: true }, expression: select a.id, a.name, count(b.id)::int as book_count from author a left join book b on b.author_id a.id group by a.id , properties: { id: p.integer().primary(), name: p.string(), bookCount: p.integer(), }, });reflect-metadata装饰器 元数据反射使用Entity装饰器并在类属性上使用PrimaryKey/Propertyimport { Entity, Property, PrimaryKey } from mikro-orm/postgresql; Entity({ tableName: author_stats_matview, view: { materialized: true }, expression: select a.id, a.name, count(b.id)::int as book_count from author a left join book b on b.author_id a.id group by a.id , }) export class AuthorStats { PrimaryKey() id!: number; Property() name!: string; Property() bookCount!: number; }ts-morph静态代码分析使用 ts-morph 提供元数据时实体定义与 reflect-metadata 风格完全一致只是元数据发现机制不同import { Entity, Property, PrimaryKey } from mikro-orm/postgresql; Entity({ tableName: author_stats_matview, view: { materialized: true }, expression: select a.id, a.name, count(b.id)::int as book_count from author a left join book b on b.author_id a.id group by a.id , }) export class AuthorStats { PrimaryKey() id!: number; Property() name!: string; Property() bookCount!: number; }无论哪种方式实体属性都必须与expression中SELECT返回的列一一对应属性名遵循 MikroORM 的列名到属性名的映射规则如book_count映射为bookCount。从测试看元数据形态仓库测试 tests/features/view-entities/view-entities.postgres.test.ts 同样用defineEntity定义了AuthorStatsMaterializedview: { materialized: true }与AuthorStatsNoDataview: { materialized: true, withData: false }并在 第 976-981 行 断言其元数据expect(meta.view).toBe(true); expect(meta.materialized).toBe(true); expect(meta.readonly).toBe(true);即物化视图实体在元数据层面会自动带有view: true、materialized: true、readonly: true三个标记。创建无数据的物化视图WITH NO DATA默认情况下物化视图创建时会立即填充数据对应 PostgreSQL 的WITH DATA。如果希望在创建时生成一个空视图、稍后再填充数据可以设置withData: falseconst AuthorStats defineEntity({ name: AuthorStats, tableName: author_stats_matview, view: { materialized: true, withData: false }, // Creates with WITH NO DATA expression: select ..., properties: { ... }, });这一选项在以下场景特别有用创建 Schema 时底层表还是空的立即执行SELECT没有意义希望完全掌控初始数据填充的时机先建库建视图再由应用或定时任务触发首次REFRESH。在底层 SQL 生成层面该开关由 packages/sql/src/dialects/postgresql/PostgreSqlSchemaHelper.ts 的createMaterializedView方法处理override createMaterializedView( name: string, schema: string | undefined, definition: string, withData true, ): string { const viewName this.quote(this.getTableName(name, schema)); const dataClause withData ? with data : with no data; return create materialized view ${viewName} as ${definition}${dataClause}; }withData参数默认值为true只有显式传false时才会追加with no data子句。刷新物化视图物化视图缓存了查询结果数据变更后必须刷新才能看到最新数据。MikroORM 在PostgreSqlEntityManager上提供了refreshMaterializedView方法import { MikroORM, EntityManager } from mikro-orm/postgresql; const orm await MikroORM.init({ ... }); const em orm.em; // Refresh the materialized view await em.refreshMaterializedView(AuthorStats); // Now queries will return the updated data const stats await em.find(AuthorStats, {});底层实现解析该方法定义在 packages/sql/src/dialects/postgresql/BasePostgreSqlEntityManager.tspg与pglite的 EntityManager 共同基类。其核心逻辑为通过this.getMetadata(entityName)获取实体元数据校验meta.view meta.materialized若目标实体不是物化视图则抛出Entity ${meta.className} is not a materialized view错误——测试 第 1036-1038 行 专门验证了这一行为从驱动平台取得 SchemaHelper拼接 SQLrefresh materialized view table通过this.execute(sql)执行。对应的 SQL 生成在 PostgreSqlSchemaHelper.ts 第 218-221 行override refreshMaterializedView(name: string, schema?: string, concurrently false): string { const concurrent concurrently ? concurrently : ; return refresh materialized view${concurrent} ${this.quote(this.getTableName(name, schema))}; }并发刷新Concurrent RefreshPostgreSQL 支持并发刷新物化视图——刷新过程中读操作不会被阻塞。并发刷新要求物化视图上至少存在一个唯一索引否则 PostgreSQL 会直接报错// Refresh concurrently (requires unique index on the view) await em.refreshMaterializedView(AuthorStats, { concurrently: true });注意并发刷新要求物化视图上至少有一个唯一索引。缺少唯一索引时 PostgreSQL 会抛出错误。对应生成的 SQL 为refresh materialized view concurrently author_stats_matview。刷新是重量级操作生产环境中应结合唯一索引与低峰期窗口使用详见下文最佳实践。刷新后的数据可见性注意 MikroORM 的 Identity Map 机制同一 EntityManager 中刷新视图后已加载的实体快照不会自动失效建议在刷新后em.clear()或使用 fork 出来的新 EntityManager 查询。测试 第 1014-1034 行 展示了完整链路插入新作者数据 → 刷新前查询仍只有旧数据 →refreshMaterializedView()→em.clear()→ 再次查询即能看到新数据。查询物化视图物化视图实体与普通实体一样可以使用em.find/em.findOne配合条件进行查询// Find all const allStats await em.find(AuthorStats, {}); // Find with conditions const prolificAuthors await em.find(AuthorStats, { bookCount: { $gte: 5 }, }); // Find one const authorStats await em.findOne(AuthorStats, { name: Jon Snow });查询操作直接命中物理存储的数据因此性能稳定。测试 第 994-1012 行 验证了插入数据 → 刷新视图 → 查询得到聚合结果的完整流程。只读行为物化视图实体会被自动标记为只读readonly: true尝试对其持久化修改会失败const stats await em.findOne(AuthorStats, { id: 1 }); stats.bookCount 100; // This change wont be persisted await em.flush(); // No UPDATE will be generated for this entity这一行为由元数据层的readonly标记驱动见前文测试断言Unit of Work 在flush时不会为只读实体生成任何UPDATE语句。物化视图数据的唯一合法更新途径是执行REFRESH MATERIALIZED VIEW。Schema 生成Schema Generator 对物化视图提供全自动支持// Create schema (includes CREATE MATERIALIZED VIEW statements) await orm.schema.create(); // Drop schema (includes DROP MATERIALIZED VIEW statements) await orm.schema.drop(); // Update schema (detects changes to materialized views) await orm.schema.update();生成的 SQL创建物化视图默认WITH DATA会生成create materialized view author_stats_matview as select a.id, a.name, count(b.id)::int as book_count from author a left join book b on b.author_id a.id group by a.id with data;设置withData: false时create materialized view author_stats_matview as select ... with no data;源码层面的生成与 diff 逻辑视图的创建、删除、更新逻辑集中在 packages/sql/src/schema/SqlSchemaGenerator.ts创建appendViewCreation第 795-812 行根据view.materialized分发到createMaterializedView并追加WITH NO DATA视图的索引创建——但跳过withData false的视图的索引此时视图还没有数据可索引索引会在数据填充后的下一次schema:update中创建删除drop与update流程第 248-253 行、第 400-423 行按依赖顺序先删视图再删表物化视图走dropMaterializedViewIfExists生成drop materialized view if exists xxx cascade变更检测packages/sql/src/schema/SchemaComparator.ts 对物化视图的索引使用现有的表 diff 基础设施进行增删改检测通过构造临时DatabaseTable复用diffTable逻辑从而让schema:update能自动发现物化视图上索引的变化。测试 tests/features/schema-generator/materialized-view-diffing.postgres.test.ts 覆盖了新增/删除/修改物化视图、withData切换、schema:create与schema:update生成索引GH #7417等场景可当作行为规范参考。其中 第 236-268 行 验证了关键细节withData: false期间不生成索引 diff切换为withData: true后schema:update才会补建唯一索引且不会自动刷新视图——索引建立在空数据上是合法的PostgreSQL 允许对未刷新视图建索引索引保持空直到下次REFRESH。另外要注意物化视图只支持索引不支持其他表级结构其DatabaseView模型packages/sql/src/typings.ts仅包含materialized、withData与indexes三个物化视图专属字段。限制仅限 PostgreSQL物化视图只在 PostgreSQL 平台受支持。其他数据库平台一旦使用view: { materialized: true }就会报错。这一限制的根因在 packages/sql/src/schema/SchemaHelper.ts基类的createMaterializedView、dropMaterializedViewIfExists、refreshMaterializedView、getListMaterializedViewsSQL、loadMaterializedViews全部直接throw new Error(Not supported by given driver)仅 PostgreSQL 的 SchemaHelper 进行了 override。无自动刷新MikroORM 不会自动刷新物化视图。必须手动调用refreshMaterializedView()或在数据库层面建立刷新机制触发器、pg_cron 定时任务等。最佳实践选择合适的刷新策略对于频繁变化的数据优先考虑普通视图view: true物化视图适合变化不频繁、或可以接受一定时间延迟的数据聚合报表、统计看板、跨表 JOIN 的昂贵查询。为物化视图加索引物化视图支持索引。为高频查询的列建立索引尤其要满足并发刷新的前提CREATE UNIQUE INDEX author_stats_id_idx ON author_stats_matview (id); CREATE INDEX author_stats_book_count_idx ON author_stats_matview (book_count);索引可以在实体元数据中声明由schema:create/schema:update自动生成也可以直接用 SQL 手工创建。调度刷新使用数据库调度器pg_cron或应用层调度在低流量时段刷新物化视图避免高峰期的资源争抢。生产环境使用并发刷新如果应用在刷新期间仍需读取视图数据始终使用{ concurrently: true }前提是视图上有唯一索引保证刷新过程中读不阻塞。监控视图体积物化视图会持续占用磁盘空间且每次刷新都是全量重建REFRESH会锁表或通过并发刷新渐进重建。需监控其大小数据量极大时考虑分区策略或其他降级方案。总结MikroORM 将 PostgreSQL 物化视图建模为一种特殊的只读实体通过view: { materialized: true }声明用expression定义查询由 Schema Generator 负责CREATE/DROP/UPDATE含索引 diff用refreshMaterializedView()手动刷新可并发底层 SQL 由 PostgreSQL 专属 SchemaHelper 生成、平台能力由supportsMaterializedViews()统一管控。结合 tests/features/view-entities/view-entities.postgres.test.ts 与 tests/features/schema-generator/materialized-view-diffing.postgres.test.ts 两组测试可以完整验证定义 → 建库 → 填充数据 → 刷新 → 查询的闭环。合理使用物化视图能在保持 MikroORM 统一实体模型的同时为读多写少、容忍延迟的聚合查询带来稳定的性能收益。赞分享后端【免费下载链接】mikro-ormTypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, MariaDB, MS SQL Server, PostgreSQL and SQLite/libSQL databases.项目地址https://gitcode.com/gh_mirrors/mi/mikro-orm点击查看免费下载相关推荐在 MikroORM 中定义与刷新 PostgreSQL 物化视图Materialized Views实体在 MikroORM 中定义与刷新 PostgreSQL 物化视图Materialized Views实体 物化视图Materialized View将后端跨平台文本编辑器 Notepad-- 5 步跑通目录搜索与文件对比跨平台文本编辑器 Notepad 5 步跑通目录搜索与文件对比 你手头有一整目录散落的代码和配置改一处要翻遍几十上百个文件Notepad 帮你在一个窗口里搜桌面应用Materialized Viewpybind11物化视图性能优化实战Materialized Viewpybind11物化视图性能优化实战 引言当Python遇见C的性能瓶颈 在现代科学计算和机器学习应用中Python开发工具上一篇Cubiomes Viewer使用教程Minecraft种子查找与地图分析的终极指南下一篇VS Code十六进制编辑器完整指南从零开始搞定二进制文件查看与编辑创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表