首页/新闻资讯/正文详情

MikroORM Result Cache 结果缓存机制完全指南:从 EntityManager 到自定义 CacheAdapter

发布时间:2026/9/26 19:36:16 来源:云帆数科 栏目:资讯中心
MikroORM Result Cache 结果缓存机制完全指南:从 EntityManager 到自定义 CacheAdapter
后端【免费下载链接】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 内置了一套轻量的**查询结果缓存Result Cache**机制允许对EntityManager的查询类方法以及QueryBuilder的取数方法进行结果复用从而显著减少数据库往返。本篇指南以官方文档 docs/versioned_docs/version-5.9/caching.md 为核心骨架结合当前仓库 packages/core/src/cache 下的适配器源码与 tests/features/result-cache 中的测试用例完整讲解缓存的开启方式、三种cache参数形态、全局配置、显式缓存键与主动失效以及如何编写自定义缓存适配器。一、结果缓存机制概览哪些方法、什么默认行为MikroORM 的结果缓存只作用于读操作且覆盖面非常明确EntityManager的find()、findOne()、findAndCount()、findOneOrFail()、count()以及QueryBuilder的所有结果方法包括execute。写入类操作如persist/flush不会触碰结果缓存。默认情况下MikroORM 使用进程内内存缓存in-memory cache该缓存由整个MikroORM实例共享不区分 EntityManager fork默认过期时间为1 秒。内存缓存的底层实现见 packages/core/src/cache/MemoryCacheAdapter.ts它内部使用Mapstring, { data, expiration }存储每次get时都会检查expiration Date.now()过期条目会被即时删除并视为未命中因此过期后下一次查询必定重新执行 SQL不存在僵尸缓存问题。说明本文档以 v5.9 官方文档为主体当前仓库 packages/core 中的实现延续并增强了该机制例如全局缓存开关、会话上下文隔离涉及差异处会明确标注。二、通过 EntityManager 方法启用缓存cache 参数的三种形态在find系列方法中只需要在选项对象里传入cache字段即可启用结果缓存它支持三种形态const res await em.find(Book, { author: { name: Jon Snow } }, { populate: [author, tags], cache: 50, // 形态一传入毫秒数使用默认缓存键50ms 后过期 // cache: [cache-key, 50], // 形态二自定义缓存键 过期时间 // cache: true, // 形态三使用默认缓存键 全局默认过期时间1s });三种形态的含义形态类型效果数字number使用自动生成的缓存键过期时间为指定毫秒数布尔true使用自动生成的缓存键过期时间取配置中的默认值1000ms二元组[string, number]使用自定义缓存键便于后续主动失效过期时间为指定毫秒数cache参数在源码中的类型定义为boolean | number | [string, number] | undefined见 EntityManager.ts 中tryCache的签名。三种形态都会被tryCache解析数组形态取config[0]作为键名数字/布尔形态则把整个查询信息序列化为键。缓存命中后返回什么值得强调的是缓存保存的是实体数据POJO而非实体实例。命中缓存时MikroORM 会通过EntityFactory把缓存数据重新实例化为实体并合并进当前上下文同时触发onLoad事件——见 EntityManager.ts。也就是说从缓存取出的结果与直接查询得到的结果在 API 形态上完全一致可以继续参与populate、序列化等后续操作不会因为走缓存而拿到裸数据。三、通过 QueryBuilder 启用缓存QueryBuilder同样支持结果缓存只需在链式调用中追加.cache()const res await em.createQueryBuilder(Book) .where({ author: { name: Jon Snow } }) .cache() .getResultList();.cache()方法的定义见 packages/sql/src/query/QueryBuilder.ts它接受与find相同的三种形态参数boolean | number | [string, number]默认值为truecache(config: boolean | number | [string, number] true): this { this.ensureNotFinalized(); this.#state.cache config; return this; }也就是说可以这样写.cache(5_000)指定过期时间为 5 秒或.cache([my-qb-key, 5_000])指定自定义键。QueryBuilder 的缓存键是如何生成的与find不同QueryBuilder 的默认缓存键直接基于最终编译出的 SQL。在执行时源码会构造const cacheKey: unknown[] [qb.execute, query.sql, query.params, method];见 QueryBuilder.ts。这意味着只要 SQL 与参数相同键就相同SQL 或参数一变缓存即自然错开无需手动维护键名。QueryBuilder 缓存的两个注意事项只有通过em.createQueryBuilder(...)创建的 QueryBuilder 才能使用结果缓存因为缓存读写依赖 EntityManager 内部的tryCache/storeCache通道见 QueryBuilder.ts脱离 EntityManager 的裸 QueryBuilder 无法走缓存。命中缓存时直接返回缓存数据cached.data不会重新走驱动执行 SQL日志层面也不会出现对应的查询语句——这是测试用例中判断是否命中缓存的依据详见 result-cache.mongo.test.ts。四、全局配置默认过期时间、缓存适配器与全局开关除了逐条查询手动指定cache还可以在 ORM 初始化配置中统一管理缓存行为const orm await MikroORM.init({ resultCache: { // 以下为默认值 adapter: MemoryCacheAdapter, // 缓存适配器类 expiration: 1000, // 默认过期时间1 秒 options: {}, // 传给适配器构造函数的参数 // 也可以全局开启缓存对所有查询生效 // global: 50, // 全局缓存过期时间 50ms }, // ... });resultCache配置项在源码中的完整定义见 packages/core/src/utils/Configuration.ts包含四个字段字段类型默认值作用adapter适配器类MemoryCacheAdapter结果缓存的实际存储实现expirationnumber1000未显式指定时的默认过期毫秒数optionsDictionary{}透传给适配器构造函数的参数globalboolean \| number \| [string, number]未设置关闭全局开启所有查询的结果缓存global是当前仓库较新版本引入的能力v5.9 文档中已提及用法但当前实现更完整一旦开启所有查询即使没写cache都会自动尝试命中缓存过期时间统一取global指定的值。在 EntityManager.ts 中可以看到tryCache的逻辑config ?? this.config.get(resultCache).global——即方法级cache参数优先缺省时才回落到全局配置。注意adapter需要传**类构造函数**而非实例MikroORM 会在初始化时用options作为构造参数实例化它。五、显式缓存键与主动失效clearCache结果缓存的默认键是自动生成的对find而言由实体、方法、选项、查询条件共同决定因此无法在外部主动删除某个自动键。若要手动清理缓存必须使用自定义缓存键// 写入以 book-cache-key 为键缓存过期时间 60 秒 const res await em.find(Book, { ... }, { cache: [book-cache-key, 60_000] }); // 主动失效按键名清除 await em.clearCache(book-cache-key);clearCache的实现见 EntityManager.ts本质是调用缓存适配器的remove(name)。测试 result-cache.mongo.test.ts 验证了该流程先以[abc, 50]写入缓存多次命中后调用await orm.em.clearCache(abc)下一次同参数查询又回到了缓存未命中、重新发 SQL的状态日志调用次数从 6 增至 9。在启用了行级安全RLS会话上下文的场景下当前版本会把命名键按会话上下文做二次隔离${key}|${JSON.stringify(sessionContext)}clearCache也会一并清理当前上下文的变体键避免跨租户串数据见 EntityManager.ts。六、CacheAdapter 接口与自定义适配器所有缓存实现包括默认的内存缓存都必须实现CacheAdapter接口。接口定义见 packages/core/src/cache/CacheAdapter.tsexport interface CacheAdapter { /** * 按 name 键从缓存中读取条目。 */ getT any(name: string, origin?: string): T | PromiseT | undefined | undefined; /** * 写入缓存。origin 用于缓存失效判断应反映数据来源的变化。 */ set(name: string, data: any, origin: string, expiration?: number): void | Promisevoid; /** * 删除单个缓存条目。 */ remove(name: string): void | Promisevoid; /** * 清空全部缓存。 */ clear(): void | Promisevoid; /** * 在 MikroORM.close() 时被调用用于优雅关闭如 Redis 连接。 */ close?(): void | Promisevoid; }要点get/set/remove/clear均支持同步或异步实现返回值类型允许Promise因此可以自由对接 Redis、Memcached 等外部存储。set的expiration参数可选不传时由适配器自行决定内存适配器会回落到构造时传入的默认过期时间见 MemoryCacheAdapter.ts。close是可选钩子会在MikroORM.close()时被调用适合释放连接池等资源。该接口同时服务于结果缓存与元数据缓存为元数据缓存还扩展了同步变体SyncCacheAdapter新增combine?()方法用于生成合并缓存文件。编写自定义适配器的最小示例对接 Redis 时只需实现上述五个方法close用于关闭连接import { CacheAdapter } from mikro-orm/core; export class RedisCacheAdapter implements CacheAdapter { constructor(private client: RedisClient, private defaultExpiration 1000) {} async get(name: string) { const raw await this.client.get(mikro:${name}); return raw ? JSON.parse(raw) : undefined; } async set(name: string, data: any, origin: string, expiration?: number) { await this.client.set(mikro:${name}, JSON.stringify(data), { PX: expiration ?? this.defaultExpiration, }); } async remove(name: string) { await this.client.del(mikro:${name}); } async clear() { // 按前缀批量删除 } async close() { await this.client.quit(); } } const orm await MikroORM.init({ resultCache: { adapter: RedisCacheAdapter, options: { client } }, });七、内置缓存适配器一览当前仓库在 packages/core/src/cache 目录下提供了四种适配器了解它们有助于选择合适的方案适配器文件用途MemoryCacheAdapterMemoryCacheAdapter.ts默认适配器进程内Map存储带时间过期FileCacheAdapterFileCacheAdapter.ts磁盘 JSON 文件存储按源文件哈希做失效检测常用于元数据缓存GeneratedCacheAdapterGeneratedCacheAdapter.ts基于预生成静态数据的只读缓存由 CLIcache:generate生成NullCacheAdapterNullCacheAdapter.ts空操作适配器get永远返回null用于显式禁用缓存其中FileCacheAdapter的实现值得留意它会把缓存连同origin来源文件路径与文件内容哈希一起写入 JSON读取时校验哈希与来源一旦实体源文件变化缓存即失效——见 FileCacheAdapter.ts。这正是元数据缓存能在开发时随源码变更自动失效的原理与结果缓存的时间过期策略形成互补。八、底层调用链一次带缓存的 find 发生了什么结合 EntityManager.ts 源码一次带cache的find调用完整链路如下计算缓存键em.cacheKey(entityName, options, em.find, where)L262。键由实体表名含 schema、STI 判别值、方法名、查询选项、条件共同构成ctx、strategy、logging等与结果无关的动态选项会被剔除避免同一查询因日志上下文不同而产生不同键L3477-L3487。尝试命中em.tryCache(entityName, options.cache, cacheKey, ...)L263。未命中返回{ key, data: undefined }命中则通过EntityFactory重建实体并返回。命中分支对缓存实体执行populate后直接返回L272-L282不执行 SQL。未命中分支走driver.find真实查询L292实体化并 populate 后通过storeCache以 POJO 形式写回缓存L322-L326。因此缓存命中会同时跳过 SQL 执行与实体构建两步这是结果缓存带来性能收益的根本来源。测试用例正是用日志中查询调用次数不增长来断言命中行为见 result-cache.mongo.test.ts。九、实战注意事项与边界过期时间单位是毫秒cache: 50表示 50ms生产环境请按实际数据新鲜度需求设置如60_000表示 60 秒。缓存与实体合并命中缓存返回的实体仍会进入 Identity Map 与 Unit of Work可参与后续变更追踪但缓存的数据是查询时刻的快照写入类操作不会自动更新或失效缓存条目。主动失效需显式键只有使用cache: [key, expiration]写入的条目才能被clearCache精确定位清除自动键无法从外部删除。内存缓存的限制默认MemoryCacheAdapter是单进程内存存储多实例部署时各实例缓存互相独立如需跨实例共享请更换为 Redis 等外部适配器。global全局缓存的慎用全局开启后所有查询都走缓存需要特别注意过期时间设置避免读到明显过期的数据建议仅对读多写少、对一致性要求不高的场景启用。版本差异本文涉及的global全局配置、RLS 会话上下文隔离属于当前仓库较新版本的增强能力v5.9 基础用法三种cache形态、resultCache配置、clearCache、CacheAdapter接口在各版本中保持一致升级时可参照官方升级指南 docs/docs/guide/upgrading-v5-to-v6.md。十、延伸阅读官方结果缓存文档本文依据docs/versioned_docs/version-5.9/caching.md 与 docs/docs/caching.md缓存适配器源码packages/core/src/cacheresultCache配置项定义packages/core/src/utils/Configuration.ts缓存读写与失效实现packages/core/src/EntityManager.tsQueryBuilder 缓存支持packages/sql/src/query/QueryBuilder.ts结果缓存测试用例tests/features/result-cache/result-cache.mongo.test.ts、result-cache.postgre.test.ts赞分享后端【免费下载链接】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 结果缓存Result Cache完全指南从默认内存缓存到自定义 CacheAdapterMikroORM 结果缓存Result Cache完全指南从默认内存缓存到自定义 CacheAdapter MikroORM 内置了一套轻量级的结果缓存后端MikroORM 结果缓存Result Cache完整指南从 EntityManager 到 QueryBuilder 的缓存实战与自定义适配器MikroORM 结果缓存Result Cache完整指南从 EntityManager 到 QueryBuilder 的缓存实战与自定义适配器 导读 M后端MikroORM 结果缓存实战从 find() 的 cache 选项、全局配置到自定义 CacheAdapter 实现MikroORM 结果缓存实战从 find 的 cache 选项、全局配置到自定义 CacheAdapter 实现 本篇指南基于 MikroORM 官方文档后端创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

isomorphic-git 中的 deleteTag:删除本地 tag 引用的 API 详解
isomorphic-git 中的 deleteTag:删除本地 tag 引用的 API 详解

开发工具 【免费下载链接】isomorphic-git A pure JavaScript implementation of git for node and browsers! 项目地址: https://gitcode.com/gh_mirrors/is/isomorphic-git 点击查看 免费下载 deleteTag 是 isomorphic-git 提供的一个轻量级本地仓库操作 API&… · 2026/9/26 19:36:16

从吐槽到规则:Karpathy 如何给 AI 编程立规矩,TaoToken 统一 Key 接入 Claude Code 的 CLAUDE.md 配置骨架
从吐槽到规则:Karpathy 如何给 AI 编程立规矩,TaoToken 统一 Key 接入 Claude Code 的 CLAUDE.md 配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 19:36:10

AI Weekly | 2026年4月第二周 · GitHub热门项目与AI发展趋势深度解析:用TaoToken统一Key跑通MCP工具链
AI Weekly | 2026年4月第二周 · GitHub热门项目与AI发展趋势深度解析:用TaoToken统一Key跑通MCP工具链

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 19:36:10

本地照片管理器Lap深度评测:为什么这款离线工具值得你立刻安装
本地照片管理器Lap深度评测:为什么这款离线工具值得你立刻安装

本地照片管理器Lap深度评测:为什么这款离线工具值得你立刻安装 【免费下载链接】lap An offline-first photo manager for large local libraries 项目地址: https://gitcode.com/GitHub_Trending/lap3/lap Lap 是一款开源免费的本地照片管理器,专… · 2026/9/26 20:21:20

普通人要 OpenClaw 有什么用?从 skill 到 amazon Scraper APIs 的 Python 配置骨架
普通人要 OpenClaw 有什么用?从 skill 到 amazon Scraper APIs 的 Python 配置骨架

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 20:21:08

固定电话正则校验实战:从规则到代码,避开线上常见坑
固定电话正则校验实战:从规则到代码,避开线上常见坑

做前端表单的人,迟早会遇到一个需求:校验用户填的固定电话。你搜索“JS固定电话正则”,网页里跳出来一大串表达式,复制到项目里,测试“010-12345678”,通过了。结果上线第二天,用户反馈“0755 1… · 2026/9/26 20:21:02

Substrate 区块链开发框架详解:从状态机到应用链的模块化实践
Substrate 区块链开发框架详解:从状态机到应用链的模块化实践

过去半年里,我花了不少时间在 Substrate 上,尤其是给不同业务方搭定制化的应用链,期间被问得最多的就是一句话:“Substrate 到底是什么?它是一条链还是一个框架?”每次我都得从状态机讲到 Runtime 再讲到 p… · 2026/9/26 20:20:49

Substrate不是AI Agent框架:区块链与Agent技术栈的本质区分
Substrate不是AI Agent框架:区块链与Agent技术栈的本质区分

1. Substrate不是AI Agent框架,而是区块链底层构建平台的误读源头最近在多个技术社区和招聘JD里反复看到“Substrate”和“Agent”被混为一谈——有人问“Substrate怎么集成AI Agent”,也有人把gVisor、Kubernetes Device Plugin和Substrate全塞进同一份… · 2026/9/26 20:20:49

AI短剧工业化流水线:从剧本到成片的全链路控制
AI短剧工业化流水线:从剧本到成片的全链路控制

1. 这不是“一键生成”,而是真正能落地的AI短剧生产流水线最近三个月,我帮七家不同背景的团队落地了AI短剧项目——有刚转型的新媒体公司、有做儿童内容的教育品牌、也有想试水IP孵化的独立创作者。他们共同的问题不是“能不能做”,而是“怎么… · 2026/9/26 20:20:49

数据库课后习题答案别硬背:当测试用例集刷,效率翻倍
数据库课后习题答案别硬背:当测试用例集刷,效率翻倍

简介:万常选版《数据库原理与设计》课后习题答案资源,覆盖第2至6章及第9章,适合正在学习关系模型、数据库建模、关系数据理论与模式求精的本科生、自学者作为复习与自测材料。压缩包共7个文件,含3个doc参考答案、2个sql示例脚本、… · 2026/9/26 0:00:21

OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置
OpenClaw 替代品?Hermes Agent 踩坑实录:macOS 飞书接入 TaoToken 配置

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/26 0:00:40

向下兼容与向上兼容:接口设计中的兼容性策略与工程实践
向下兼容与向上兼容:接口设计中的兼容性策略与工程实践

一次版本升级事故,是很多团队绕不过去的坎。线上环境里,服务端明明已经上线了新版接口,老的移动端还在照着旧文档传参数。请求一到网关,校验直接拒绝,用户操作失败,客服群炸了锅,开发群里开始互… · 2026/9/26 0:00:46

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码