尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Mastra MySQL 存储适配器全解析:@mastra/mysql 的架构、事务保障与关键能力演进

Mastra MySQL 存储适配器全解析:@mastra/mysql 的架构、事务保障与关键能力演进 Mastra MySQL 存储适配器全解析mastra/mysql 的架构、事务保障与关键能力演进【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra本文以 Mastra 仓库中 stores/mysql/CHANGELOG.md 为骨架结合 stores/mysql 包的源码与测试系统讲解mastra/mysql存储适配器的能力全貌从连接配置、存储域覆盖到线程所有权转移、调用方驱动实验、多租户隔离、初始化性能优化等核心机制并给出源码级的原理支撑与可复现的代码示例。读完本文你将能独立评估并落地把 MySQL 作为 Mastra 应用的持久化后端理解其事务语义与版本演进边界。一、mastra/mysql 是什么mastra/mysql是 Mastra 官方提供的 MySQL 存储实现为 Agent 应用提供线程threads、消息messages、工作流workflows、追踪traces、数据集datasets等对象的持久化能力内置连接池与事务支持。它于 0.1.0 版本作为一等公民加入适配器家族目标是与其余官方适配器拥有相同的存储域覆盖范围见 stores/mysql/CHANGELOG.md 0.1.0 条目覆盖记忆、线程、工作流、可观测性、Agent 等场景。从源码看MySQLStore继承自MastraCompositeStorestores/mysql/src/storage/index.ts内部组合了 22 个存储域类stores字段包括MemoryMySQL、WorkflowsMySQL、ObservabilityMySQL、DatasetsMySQL、ExperimentsMySQL、AgentsMySQL、SchedulesMySQL、ScoresMySQL、WorkflowDefinitionsMySQL等。所有域共享同一个 mysql2 连接池这既是能力覆盖广的根基也是后续各项事务与并发保障机制的设计前提。二、安装与连接配置2.1 安装npm install mastra/mysql包依赖与运行约束见 stores/mysql/package.json运行时依赖仅mysql2^3.11.3驱动能力全部来自 mysql2/promisepeerDependencies要求mastra/core 1.63.1-0 2.0.0-00.8.4 版本曾修复过旧 core 导致的加载失败并校正了最低版本见 stores/mysql/CHANGELOG.md 0.8.4 条目engines.node 22.13.00.8.5 起从发布包中移除了CHANGELOG.md减小 npm 包体积。2.2 基础用法stores/mysql/README.md 给出的最小接入方式import { Mastra } from mastra/core; import { MySQLStore } from mastra/mysql; export const mastra new Mastra({ storage: new MySQLStore({ connectionString: mysql://user:passwordlocalhost:3306/mastra, }), });2.3 配置项详解源自源码类型定义MySQLStoreConfig在 stores/mysql/src/storage/index.ts 中定义支持两种互斥的配置形态配置形态字段说明默认值连接串式connectionString必填非空字符串如mysql://user:passhost:3306/db无database覆盖连接串路径中的库名取自 URL pathnamemax连接池上限映射为connectionLimit10ssl布尔或 mysql2 ssl 配置对象无字段式host/user/database三项必填且不可为空字符串无port端口3306password可选提供时必须是字符串无max连接池上限10waitForConnections池满时是否排队等待truequeueLimit排队上限0 表示不限0ssl同上无通用skipDefaultIndexes跳过默认索引创建构建期不再创建默认性能索引falseindexes自定义CreateIndexOptions[]无连接串解析parseConnectionString同文件 L130-L193还支持额外的 URL 查询参数waitForConnections、queueLimit、connectionLimit、dateStrings、ssl可传true/false/on/off或 JSON 对象字符串。注意解析器强制设置了dateStrings: true即日期列以字符串形式返回避免时区漂移问题。validateConfigL79-L100会在构造阶段拒绝空connectionString、空host/user/database等非法输入。2.4 本地开发环境仓库为适配器提供了开箱即用的 Docker 测试环境 stores/mysql/docker-compose.yaml使用mysql:9.7镜像默认账号mastra/mastra、默认库mastra均可通过环境变量MYSQL_USER、MYSQL_PASSWORD、MYSQL_DB覆盖并内置 mysqladmin 健康检查。包脚本pretest会自动拉起容器并等待健康后再跑 vitest。三、线程所有权转移updateThreadResourceId0.9.0-alpha.1引入的核心能力见 stores/mysql/CHANGELOG.md 0.9.0-alpha.1 条目线程所有权转移resourceId 重分配。它解决的真实场景是将某个私有线程迁移到共享工作区而无需此前先 upsert 到新 resourceId的变通做法。3.1 能力矩阵mastra/core/mastra/memory新增Memory.updateThreadResourceId({ threadId, resourceId })默认实现为MemoryStorage.updateThreadResourceId启用语义召回时消息向量会同步迁移到新resourceId保证资源级检索仍能命中被转移的线程。mastra/server新增POST /memory/threads/:threadId/transfer路由仅允许特权、非资源级non-resource-scoped调用方访问带资源作用域的请求会被拒绝。mastra/client-js新增MemoryThread.transfer({ resourceId })方法。存储层mastra/mysql等 7 个适配器pg、libsql、mssql、dsql、oracledb、mysql、spanner提供原子化、串行化的updateThreadResourceId覆写实现。3.2 事务语义与并发保障关键设计是线程及其全部消息在单个事务内完成搬迁重叠的转移请求不会交错执行导致所有权分裂。不同数据库采用各自的锁策略适配器并发控制策略Postgres / MySQL / SQL Server / Oracle行锁SELECT ... FOR UPDATE/UPDLOCK, HOLDLOCKlibSQL / Spanner写事务串行化Aurora DSQL乐观并发控制 自动重试无事务原语的适配器回退到基类尽力而为实现出错即回滚fail closed3.3 代码示例// 服务端从特权非资源级上下文调用 const thread await memory.updateThreadResourceId({ threadId: thread-123, resourceId: new-resource-456, }); // 客户端 const client new MastraClient({ baseUrl: http://localhost:4111 }); const thread client.getMemoryThread(thread-123, agent-id); await thread.transfer({ resourceId: new-resource-456 });3.4 源码验证仓库测试 stores/mysql/src/storage/domains/memory/update-thread-resource-id.test.ts 用 mock 连接池精确记录了调用序列验证了三条关键不变量单事务提交调用序列为[begin, commit, release]先加行锁再更新存在对mastra_threads的FOR UPDATE查询随后更新mastra_threads.resourceId再按thread_id批量更新mastra_messages.resourceId异常回滚线程不存在时序列为[begin, rollback, release]且抛错包含not foundresourceId未变化时零 UPDATE 语句保持原createdAt不变。四、初始化性能优化schema snapshot 与 ALTER 风暴修复0.8.0系列对 MySQL 适配器的初始化init路径做了两次关键手术这是本包演进中最具工程借鉴价值的部分。4.1 问题一alterTable 探测的字段大小写 BugMySQL 通过 mysql2 返回的information_schema结果字段键是大写的如COLUMN_NAME而旧探测逻辑按小写读取导致已存在列集合永远为空。于是每次热启动都会重复执行 107 条ALTER TABLE ADD COLUMN这些语句以ER_DUP_FIELDNAME失败后被静默吞掉——白占了生产表的元数据锁metadata lock。修复后探测逻辑读取任意大小写键在 dockermysql:9.7上测得热启动从 326 次客户端-服务端往返降到 109 次且 ALTER 语句降为零。4.2 问题二init 级 schema snapshot在列探测修复之上0.8.0又引入init 作用域的快照机制见 stores/mysql/src/storage/db/schema-snapshot.tsinit()一开始用3 条 information_schema 查询一次性把表、列、索引的存在性读入内存快照SchemaSnapshot此后createTable、alterTable、createIndex、hasColumn以及记忆域对idx_om_lookup_key的裸CREATE INDEX都先查快照、本地作答并在创建对象时同步维护快照。效果热启动往返从 109~111 次降到 7 次冷启动从 253 次降到 153~154 次且冷启动前后的表与索引清单完全一致。该机制的设计约束值得注意源码注释明确写明了仅限 init 窗口快照在MySQLStore.init()的finally中必然被清除见 stores/mysql/src/storage/index.ts运行时调用仍然查询实时 catalog绝不充当进程级全局缓存只回答是否存在不做全 schema 相等性判断收敛是增量的旧客户端面对新 schema 也必须判定为已收敛无默认库无 schemaName时快照加载返回 null回退到逐探测行为——正确性优先于优化。配套的单元测试 stores/mysql/src/storage/db/schema-snapshot.test.ts 覆盖快照构建与indexKeytable.index小写键的语义。五、数据集与实验域调用方驱动的实验闭环实验experiments与数据集datasets是mastra/mysql演进最密集的领域。0.8.1引入了调用方驱动实验caller-driven experiments外部编排器如 Temporal workers拥有实验循环而 Mastra 保持为记录系统system of record。5.1 方法组mastra/core侧新增四个方法stores/mysql/CHANGELOG.md 0.8.1 条目createExperiment()幂等地创建实验传入自己的 id 时幂等runExperimentItem()指定 target 时Mastra 代跑每个 item——执行注册的 agent 或 workflow按实验scorers→ itemscorerIds→ datasetscorerIds的优先级解析评分器并 upsert 结果submitExperimentResult()无 target 时由调用方自行执行并上报结果按(experimentId, itemId, attempt)键 upsert重试的 worker 收敛到同一行finalizeExperiment()关闭实验Mastra 按持久化行计算每 item 的 succeeded/failed/skipped 计数。// 调用方驱动循环Mastra 执行每个 item const { experimentId } await dataset.createExperiment({ id: workflowRunId, targetType: agent, targetId: support-agent, scorers: [accuracy], }); await dataset.runExperimentItem({ experimentId, itemId }); // 或者调用方全权执行Mastra 只负责入库 const ingest await dataset.createExperiment({ id: workflowRunId }); await dataset.submitExperimentResult({ experimentId: ingest.experimentId, itemId, output, scores: [{ scorerId: accuracy, score: 0.92 }], }); const experiment await dataset.finalizeExperiment({ experimentId });同一版本还补齐了配套 HTTP 路由POST /datasets/:datasetId/experiments接受start: false创建不自动跑的实验可带可选 target 与运行级scorerIds新增POST /datasets/:datasetId/experiments/:experimentId/items/:itemId/run、POST /datasets/:datasetId/experiments/:experimentId/results、POST /datasets/:datasetId/experiments/:experimentId/finalize。存储层新增upsertExperimentResult()experiment results 表增加attempt列实验表增加可空 target 与scorerIds列saveScore()接受可选的调用方 id 并按其 upsert重试提交最新覆盖而非堆积重复。5.2 按标签过滤实验0.8.7为listExperimentResults增加 tags 过滤所有请求的标签都必须存在才匹配const { results, pagination } await storage.listExperimentResults({ experimentId: exp-id, pagination: { page: 0, perPage: 50 }, tags: [regression, p0], });5.3 数据净化purgeItem0.8.6引入dataset.purgeItem()从既有数据集历史与关联实验结果中脱敏item 内容同时保留版本历史与评审状态。被 purge 的 item 拒绝后续更新后续实验结果的写入保持脱敏状态MongoDB 的 purge 要求事务支持。注意约束数据集 item 写入不得与 purge 并发执行并发更新的数据无法恢复已清除的负载见0.8.8-alpha.0的修复。await dataset.purgeItem({ itemId: item-123 });5.4 实验来源与分组0.7.0为 LibSQL、MongoDB、MySQL、PostgreSQL、Spanner 五个适配器加入实验provenance来源与 grouping分组字段为后续按组过滤预留await dataset.startExperiment({ task, scorers, provenance: { source: github, sourceVersion: abc123 }, grouping: { experimentSetId: benchmark-1, variantId: candidate, trialIndex: 0 }, });5.5 幂等的调用方定义 ID0.4.0引入原子化的调用方定义数据集 IDmastra.datasets.create({ id })幂等创建不兼容的不可变身份字段抛DATASET_ID_CONFLICT以及数据集 item 的externalId身份机制重试同身份同负载返回既有 item同身份不同内容抛类型化冲突并发写入亦然更新与删除保持身份MySQL 批量写入完整保留所有受支持的 item 字段。await dataset.addItem({ externalId: source-item-123, input: { prompt: Hello }, });六、多租户隔离organizationId / projectId 贯穿从0.3.0到0.3.3MySQL 适配器完成了一整套多租户隔离改造核心思想是把租户谓词折叠进 SQL 本身scoped predicate而不是先查后断言从而消除 TOCTOU时间差攻击窗口。6.1 数据集与实验的租户化createDataset现在持久化organizationId、projectId、candidateKey、candidateIditem 的租户跟随父数据集调用方不可逐条设置。实验域mastra_experiments、mastra_experiment_results新增organizationId/projectId列实验记录与每条 item 结果继承父数据集的租户桶denormalize 到每个ExperimentResult上以支持高效租户查询。const experiment await storage.createExperiment({ name: qa-regression, datasetId: ds_123, datasetVersion: 1, targetType: agent, targetId: agent_qa, totalItems: 10, organizationId: org_123, projectId: proj_123, }); const experiments await storage.listExperiments({ pagination: { page: 0, perPage: 20 }, filters: { organizationId: org_123, projectId: proj_123 }, });6.2 行为约定防存在性泄露租户不匹配时get*在存储层返回nullHTTP 层 404租户不匹配时delete*为静默 no-op与删除不存在的 id 是 no-op保持一致——绝不通过错误时间或状态泄露跨租户存在性ExperimentsStorage的getExperimentById、getExperimentResultById、deleteExperiment、deleteExperimentResults及DatasetsStorage.deleteDataset全部接受可选filters: { organizationId?, projectId? }数据集的 HTTP 路由GET/PATCH/DELETE /datasets/:datasetId支持organizationId、projectId查询参数GET /datasets/abc123?organizationIdorg_aprojectIdproj_1 DELETE /datasets/abc123?organizationIdorg_a6.3 评分的租户与批处理溯源0.3.3同时为saveScore/listScoresBy*增加可选的organizationId/projectIdprojectId表示项目作用域与表示 Agent 记忆资源的resourceId语义分离并为已持久化评分增加batchId、datasetId、datasetItemId三个溯源字段使基线评分可归组为一次评分批次并回溯到来源 itemawait scoreTrace({ storage, scorer, target: { traceId }, batchId: baseline-batch-1, datasetId, datasetItemId, });6.4 MySQL 特有的过滤修复0.3.3修复了 MySQL store 的listExperiments忽略targetType、targetId、agentVersion、status过滤的问题此前查询不按这些字段收窄并补写了实验行的agentVersion列列存在但从未写入/读出导致按 agentVersion 过滤永远匹配不到。数据集 CRUD 也补齐了targetType、targetIds、scorerIds、tags、requestContextSchema的持久化与反序列化——这些列此前在共享 schema 中已声明但从未读写。七、记忆Memory域的可靠性演进记忆域是 Agent 对话与长期记忆的载体mastra/mysql在其上经历了多次正确性修复错误不再静默吞掉0.7.0listThreads、listMessages、listMessagesByResourceId、listMessagesById此前捕获后端失败、记日志并返回空负载如{ threads: [], total: 0, hasMore: false }导致 Agent 在短暂故障表锁、断连时把数据库故障误判为没有历史而覆盖真实状态。现在这些方法将失败重新抛出为MastraError校验类USER错误与真空结果行为不变。直接调用这些读方法时应包 try/catchAgent 内部已透传错误try { const { threads } await storage.listThreads({ resourceId }); // ...use threads } catch (error) { // 真正的后端故障决定重试、上报还是降级 }仓库另有专项测试 stores/mysql/src/storage/memory-error-propagation.test.ts 验证该行为。部分线程更新0.7.0updateThread的title与metadata改为独立可选只改 metadata 的调用方不再回写 title避免标题生成完成于读取与写入之间导致的生成标题被旧值覆盖#21041 的标题被覆盖修复以及针对旧存储包做向后兼容的混合版本修复 #21257适配器通过声明支持部分线程更新来让新 memory 保留既有标题。元数据精确过滤0.5.0memory.recall()支持按 metadata 精确过滤多字段为 AND 语义支持的值类型为字符串、有限数字、布尔值与nullconst messages await memory.recall({ threadId: thread-1, filter: { metadata: { status: done, priority: high, }, }, });资源边界0.7.0-alpha.1修复跨存储适配器的资源级消息 include防止被包含的上下文跨越资源边界。八、工作流与 Agent 域的持久化工作流定义持久化0.6.0为 libsql、pg、mysql、mssql、mongodb、spanner 实现workflowDefinitions存储域。此前POST /stored/workflows、Mastra.addStoredWorkflow只对mastra/core的内存存储生效持久化适配器返回undefined并抛错。现在每个适配器在init()期间按WORKFLOW_DEFINITIONS_SCHEMA创建共享表mastra_workflow_definitions及status默认索引实现upsert/get/list/delete并发首写采用检测重复键 → 重读 → 走部分更新路径的竞态安全策略JSON 列inputSchema、outputSchema、stateSchema、requestContextSchema、metadata、graph经各适配器的 JSON 处理往返损坏的持久化 JSON 会报出指明行与列的可操作错误。MySQLStore自动装配该域storage.getStore(workflowDefinitions)直接返回可用句柄const workflowDefinitions await storage.getStore(workflowDefinitions); await workflowDefinitions.upsert({ id: greeting-workflow, inputSchema: { type: object, properties: { name: { type: string } }, required: [name] }, outputSchema: { type: object, properties: { text: { type: string } }, required: [text] }, graph: [{ type: agent, id: greet, agentId: greeter-agent }], });工作流运行快照去重0.3.1修复工作流快照与 AI span 每次插入新行的问题导致表无限膨胀、读性能劣化改为原地更新并保证工作流运行在 MySQL 存储中重复持久化时保留原始创建时间含并发保存场景。可持久化 Agent0.8.0通过 Agents API 创建的存储型 Agent 可开启durable选项获得持久执行能力无需部署代码await mastraClient.createStoredAgent({ id: helper, name: Helper, instructions: You are a helpful assistant., model: { provider: openai, name: gpt-5 }, durable: true, });传true使用默认值或传{ maxSteps, cleanupTimeoutMs }调优持久循环缓存与 pubsub 继承自服务端 Mastra 实例跨副本持久需在服务端配置分布式后端自动恢复仍需通过recovery.durableAgents在代码中配置。调度兼容0.3.4旧版以target.type: heartbeat持久化的调度行读取时归一化为target.type: agent保证 heartbeats-to-schedules 重命名后既有 Agent 调度继续触发。九、已知限制与运维注意Tool mock 不支持0.3.0MySQL 适配器尚未支持 item 级工具 mock 的持久化。保存带toolMocks的数据集 item或带toolMockReport的实验结果会快速失败并给出明确错误而不是静默丢弃数据。dataset 级写入与 purge 不得并发0.8.6/0.8.8-alpha.0。后台任务存储0.8.2为保障原子条件更新cancellation 不可被 dispatch 覆盖后台任务状态更新要求 compare-and-set 语义Cloudflare KV 与 ClickHouse 因无法提供该语义而不再暴露后台任务存储——MySQL 适配器不受影响。混合版本部署0.7.0、0.8.4较新的mastra/memory搭配较旧的存储包时适配器通过支持部分线程更新声明来兜底同时注意 core peer 依赖下限1.63.1-0。供应链安全0.1.32026-06-17 easy-day-js 供应链事件后该包进行了补丁发布以推进latestdist-tag 并发布干净版本替代声明了恶意easy-day-js依赖的受影响版本——升级时请使用干净版本。打包产物0.8.5mastra build产物在开启 observational memory 并首次触达存储请求时曾出现死锁mastra start独有mastra dev不受影响0.3.0已修复应用侧无需bundler.externals变通。十、总结mastra/mysql从 0.1.0 的适配器初版演进为一个覆盖 22 个存储域、具备事务级并发保障与多租户隔离的完整持久化后端。其技术主线可以概括为四条领域覆盖记忆、线程、工作流、可观测性、Agent、数据集、实验、评分、调度、技能、Prompt、MCP、后台任务、Blob、知识等域统一由组合式 store 装配stores/mysql/src/storage/index.ts并支持exportSchemas()离线导出 DDLstores/mysql/src/storage/index.ts并发与事务以FOR UPDATE行锁 单事务保证线程转移等关键操作原子化配合折叠进 DML 的租户谓词消除 TOCTOU初始化可靠性schema snapshot 把热启动往返降到个位数杜绝 ALTER 风暴与元数据锁空耗幂等与重试友好调用方定义 ID、externalId、(experimentId, itemId, attempt)键、评分 upsert让外部编排器如 Temporal能安全地驱动实验闭环。对于正在为 Mastra 应用选型 MySQL 后端的团队本文涉及的核心代码、测试与配置均可在仓库中直接查阅README 与使用示例、配置类型与 store 实现、schema snapshot 机制、线程转移事务测试、本地 MySQL 环境 以及 完整版本演进记录。【免费下载链接】mastraMastra is the modern TypeScript framework for AI-powered applications and agents.项目地址: https://gitcode.com/GitHub_Trending/ma/mastra创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表