Skip to content

数据与存储 ​

字数
875 字
阅读时间
4 分钟

TIP

插件需要持久化时,先想清楚数据属于哪一类:结构化记录进数据库,二进制文件走文件存储,敏感信息走凭据,向量走向量服务。不要把所有内容都写进一个 JSON 文件。

数据库 ​

用 ctx.model.extend 声明表结构,用 ctx.database 读写:

ts
ctx.model.extend('my_plugin_items', {
  id: 'unsigned',
  key: 'string',
  value: { type: 'json' },
  createdAt: 'integer',
}, {
  primary: 'id',
  autoInc: true,
  unique: [['key']],
  indexes: [['createdAt']],
})

约定:

  • 表名加插件前缀,避免冲突。
  • 主键用自增整数或字符串,不要用复合主键暴露业务键。
  • 为常用查询条件建索引。
  • 时间戳统一用毫秒整数(integer)。
  • JSON 字段只用于整体读写的对象,不要在 JSON 内部做条件查询。

新增字段会在启动时自动补齐;删除或重命名字段时,旧数据不会自动转换,需要显式写迁移逻辑,完成后清理废弃数据。

文件 ​

图片、附件、导出文件等二进制数据交给文件存储:

ts
const file = await ctx.chatluna_storage.createTempFile(buffer, 'report.csv', {
  mimeType: 'text/csv',
})

文件存储按内容哈希去重,支持临时文件并定期清理过期文件,可以按名称统一读取。不要把大文件存进数据库,也不要在数据目录下随意创建裸文件。

凭据 ​

密钥和令牌不要明文存进数据库。在 Schema 中用 role('secret') 修饰字段,系统只存储凭据引用,读取时由凭据服务解析。导出配置时这些字段不含明文,这是预期行为。

向量 ​

需要相似度检索时注册向量后端:

ts
ctx.vector.register(myStore)

一个进程只能注册一个向量后端。没有注册时调用会直接报错,不会静默降级。

数据归属 ​

  • 插件数据:由插件负责,卸载时清理或归档。
  • 用户数据:会话记录、记忆等不要复制到插件表中冗余保存。
  • 缓存:必须可以随时清除,并且能重新生成。

性能 ​

  • 分页用游标,避免大数据量下 offset 深度分页变慢。
  • 大结果集分批读取。
  • 高频写入合并为批量操作。
  • 查询变慢时先看执行计划,为高频条件建索引。

测试 ​

  • 单元测试优先用内存数据库,速度快、环境干净。
  • 重点测试「表不存在」和「缺少部分字段」时的初始化。
  • 测试插件卸载后数据是否按预期保留或清理。

常见问题 ​

表没有创建:检查插件是否启用,inject 中是否缺少 model 或 database。

写入时报类型错误:JSON 字段要求对象或数组,标量值需要包一层对象。

查询很慢:通常是缺少索引,或条件中有 OR、LIKE 这类难以优化的条件。

升级后旧数据读不出来:字段发生了破坏性变更,为旧结构写显式迁移,不要依赖自动补齐。

页面历史

在 CC-BY-SA-4.0 许可下发布