使用Amnesia创建的数据库在Phoenix应用及observer中无法查看的原因
我刚踩过Amnesia和Phoenix整合的坑,咱们一步步来排查你遇到的问题:
1. 数据库存储路径不统一
Amnesia 默认会把磁盘数据库放在当前运行进程的工作目录下的 Mnesia 文件夹里。哪怕你觉得都是在项目根目录运行,也可能因为启动方式的细微差异(比如 IDE 启动 vs 终端启动)导致路径偏差;或者你的 database.ex 里没明确指定统一路径,最终 mix 任务和 Phoenix 读写的是完全不同的数据库文件。
解决方法:
- 在
database.ex定义数据库时,明确指定存储路径(推荐放在项目的priv目录下,避免版本控制系统误提交):defdatabase Database do # 保留你原有的 deftable 定义 dir "./priv/mnesia" end - 在全局配置文件
config/config.exs里统一设置 Mnesia 目录,确保所有进程都用同一个路径:config :mnesia, dir: "./priv/mnesia"
2. Phoenix 应用未正确初始化数据库表
你的 mix 任务里肯定手动执行了 Amnesia.start() 和 Database.create!() 来加载表,但 Phoenix 启动时并不会自动帮你做这件事。如果应用启动时没加载数据库表,自然看不到之前插入的数据。
解决方法:
- 修改
lib/my_app/application.ex的start/2函数,把数据库初始化逻辑放在最前面:def start(_type, _args) do # 先启动 Amnesia 并加载数据库 Amnesia.start() # 确保磁盘数据库和表存在,wait() 会阻塞直到表加载完成 Database.create!(disk: true) Database.wait() # 保留你原有的子树启动代码 children = [ MyApp.Repo, MyAppWeb.Endpoint # 其他服务... ] Supervisor.start_link(children, strategy: :one_for_one) end
3. 数据操作未在事务中执行
Amnesia 的磁盘存储依赖事务来持久化数据。如果你的 mix 任务里插入数据时没包裹在 Amnesia.transaction 块中,IO.inspect 能读到的只是内存中的临时数据,根本没写入磁盘。
解决方法:
- 检查你的
Mix.Tasks.PopulateDB代码,确保所有数据写入操作都在事务块里:def run(_args) do Amnesia.start() Database.create!(disk: true) Database.wait() Amnesia.transaction do # 你的数据填充逻辑,比如: Enum.each(1..10, fn id -> MyApp.Database.User.write(%MyApp.Database.User{id: id, name: "User #{id}"}) end) end IO.puts("测试数据填充完成") end
4. 运行环境不一致
如果你的 mix 任务默认运行在 :dev 环境,但 Phoenix 启动时不小心切换到了其他环境(比如 :prod),而不同环境的 Mnesia 配置不同,也会导致两边读写不同的数据库。
解决方法:
- 运行 mix 任务时明确指定环境,和 Phoenix 保持一致:
MIX_ENV=dev mix PopulateDB - 检查
config/dev.exs、config/prod.exs等配置文件,确保各个环境下的 Mnesia 目录配置统一(如果需要区分环境,也要保证任务和应用用的是同一个环境)。
按照上面的步骤调整后,你可以先重新运行 mix PopulateDB,然后去 ./priv/mnesia 目录下确认生成了数据库文件,再启动 Phoenix 应用,此时不管是查询数据还是用 observer:start() 查看,都应该能看到你的表和数据了。
内容的提问来源于stack exchange,提问作者Jas6033

