【技术底稿 25】日志说入库成功了,但 Milvus 里没有?——一次“假成功”背后的配置与注入排查实录
程序打印了“✅ Milvus 向量入库成功!ID: xxx”,但 Attu 里就是看不到数据。
换集合、换模型、改维度,折腾了半天,最后发现:数据根本没进 Milvus,而是写进了内存。
这不是“玄学”,是配置加载 + Bean 注入的连环坑。
一、问题现象
程序日志:
✅ Milvus 向量入库成功!ID: 9b7c0083-408a-4b69-ab4e-4573be722eee 向量维度: 1024Attu 里查询shangzhuhui_1024集合,总行数始终为 0。
日志说“成功了”,但数据就是不在库里。
二、第一步排查:确认写入的是哪个 Store
在store方法里加一行日志:
java
log.info("当前使用的 embeddingStore 类型: {}", embeddingStore.getClass().getName());重启后输出:
embeddingStore 类型: dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore真相大白:数据根本没有写入 Milvus,而是写进了内存。InMemoryEmbeddingStore是 LangChain4j 的内存版实现,进程一关数据就丢。
三、为什么注入了内存版?
检查MilvusConfig的写法:
java
@Component public class MilvusConfig { private EmbeddingStore<TextSegment> embeddingStore; @PostConstruct public void init() { this.embeddingStore = MilvusEmbeddingStore.builder() .host(host) .port(port) .collectionName(collectionName) .dimension(dimension) .build(); } }问题:@Component只是把MilvusConfig这个类本身注册成了 Bean,但手动new出来的embeddingStore并没有被 Spring 管理。AiKnowledgeService通过构造方法注入EmbeddingStore<TextSegment>时,Spring 找不到匹配的 Bean,于是 fallback 到了内存版。
原则:所有需要被注入的对象,都必须由 Spring 容器创建和管理。手动new出来的对象,Spring 不知道它的存在。
四、为什么当初要手动 new?
因为之前配置没加载对。
java
@Value("${milvus.dimension:384}") private int dimension;spring.profiles.active没生效,application-dev.yml没有被加载,@Value一直用默认值384。
而qwen3-embedding:0.6b模型输出的是1024 维。维度不匹配,Milvus 静默丢弃了写入数据。
为了“让配置生效”,改成了@Component+@PostConstruct手动创建——这解决了配置读取问题,却引入了新的问题。
五、根本原因链条
text
多环境配置没加载 ↓ @Value 用了默认值 384 ↓ 1024 维数据写不进 384 维集合 ↓ 为了“让配置生效”改成手动 new ↓ 手动 new 的对象不是 Spring Bean ↓ 注入时 fallback 到 InMemoryEmbeddingStore ↓ 日志显示“成功”,数据没进 Milvus六、解决方案
1. 修复多环境配置
确认application.yml中指定了spring.profiles.active,并且对应的配置文件能正常加载。
2. 恢复标准写法
java
@Configuration public class MilvusConfig { @Value("${milvus.host:192.168.92.139}") private String host; @Value("${milvus.port:19530}") private int port; @Value("${milvus.collection-name:shangzhuhui_1024}") private String collectionName; @Value("${milvus.dimension:1024}") private int dimension; @Bean public EmbeddingStore<TextSegment> embeddingStore() { return MilvusEmbeddingStore.builder() .host(host) .port(port) .collectionName(collectionName) .dimension(dimension) .build(); } }3. 配置打印
用@ConfigurationProperties+@PostConstruct在启动时打印所有关键配置:
java
@Slf4j @Component @ConfigurationProperties(prefix = "milvus") public class MilvusProperties { private String host; private int port; private String collectionName; private int dimension; @PostConstruct public void init() { log.info("========== Milvus 配置 =========="); log.info("host: {}", host); log.info("port: {}", port); log.info("collectionName: {}", collectionName); log.info("dimension: {}", dimension); log.info("================================="); } // getters/setters }七、经验总结
| 教训 | 正确做法 |
|---|---|
| 配置值不确定 | 启动时打印所有重要配置,不要瞎猜 |
| Bean 类型不确定 | 打印xxx.getClass().getName() |
| 怀疑数据没落盘 | 第一时间确认embeddingStore是不是Milvus版 |
| 手动 new 对象 | 需要被注入的对象必须由 Spring 管理 |
| 日志显示成功但没数据 | 检查维度和集合是否匹配,Milvus 会静默丢弃不匹配的数据 |
| 多环境配置 | 打印spring.profiles.active的值,确认生效 |
八、最后
今天最大的收获不是“修好了”,而是建立了一套排查惯性:
配置值必须打印
Bean 类型必须打印
怀疑链路时,从最源头打印日志
有了这套习惯,以后再遇到类似的“幽灵问题”,你不会再花一上午去猜。你会直接看日志,一眼定位。👊
📚 系列导航:
【人生底稿 01】|农村少年(1995–2005)
【技术底稿】01:37岁老码农,用4台机器搭了套个人DevOps平台
【产品底稿01】37 岁 Java 老码农,用 Java 搭了个 AI 写作助手,把自己 14 年技术文章全喂给了 AI!
