这是一个非常令人兴奋的工程挑战。要实现一个“由浅入深”且最终达到“工业级”的 Java 搜索系统,我们不能一开始就堆砌微服务。
建议采用 “演进式架构”(Evolutionary Architecture),将开发过程分为五个阶段。每个阶段都是对上一个阶段的重构和升级。
目标: 抛开网络和并发,彻底搞懂数据是怎么存进去、怎么查出来的。不使用 Elasticsearch,直接操作 Lucene SDK。
-
架构形态: 简单的 Java CLI 工具或单元测试。
-
核心任务:
-
Schema 设计: 定义一个
Item类(ID, Title, Description, Price)。 -
构建索引(Write): 使用
org.apache.lucene包。- 配置
Analyzer(分词器),初期可以用标准分词,马上引入 HanLP 做中文分词。
-
使用
IndexWriter写入 Document。 -
观察磁盘上生成的 Segment 文件结构(.doc, .pos, .tim),直观理解倒排索引。
- 配置
-
基础检索(Read):
-
使用
IndexSearcher和QueryParser。 -
实现 TermQuery(精确查)和 BooleanQuery(组合查)。
-
-
-
关键代码点:
Directory(FSDirectory vs RAMDirectory)。commit()vsflush()的区别。
目标: 让搜索变成一个 Web 服务,并处理并发读写冲突。
-
架构形态: Spring Boot 单体应用。
-
核心任务:
-
API 封装: 提供
POST /api/index/add和GET /api/search接口。 -
单机读写分离:
-
写线程: 维护全局唯一的
IndexWriter,负责写入。这里要设计一个阻塞队列(BlockingQueue)作为简单的内存缓冲。 -
读线程: 使用
SearcherManager。这是工业级关键点,它能通过NRT(Near Real-Time) 机制,让写入的数据在毫秒级内可见,而不需要昂贵的 commit 操作。
-
-
对象池化: 搜索对象很重,不要每次请求都
new。
-
-
关键技术:
- Spring Boot, Lucene
SearcherManager,ControlledRealTimeReopenThread.
- Spring Boot, Lucene
目标: 模拟真实工业场景,数据不再由 API 手动录入,而是自动从业务数据库同步。
-
架构形态: 数据源 -> 中间件 -> 搜索服务。
-
核心任务:
-
准备数据源: 在本地 MySQL 建一张
products表,模拟电商商品。 -
CDC 接入: 部署 Canal Server,监听 MySQL Binlog。
-
消费与写入:
-
在 Spring Boot 中集成 Kafka Consumer(或者直接连 Canal Client)。
-
全量同步: 写一个脚本,把 MySQL 历史数据扫入索引。
-
增量同步: 监听 Binlog 的 INSERT/UPDATE/DELETE 事件,实时操作 Lucene
IndexWriter。
-
-
-
关键技术:
- MySQL Binlog, Alibaba Canal, Kafka (可选,作为缓冲层), 最终一致性保证。
目标: 解决“搜不准”的问题,引入语义搜索。
-
架构形态: 增加“查询理解模块”和“向量索引”。
-
核心任务:
-
Query Understanding (QU) 模块:
-
在 Controller 层之前拦截 Query。
-
集成 HanLP 进行命名实体识别(NER),识别出“品牌”和“品类”。
-
实现同义词扩展(Synonym),维护一个简单的 Map 映射。
-
-
向量化(Embedding):
-
引入 DJL (Deep Java Library) 或 ONNX Runtime。
-
加载一个轻量级的 BERT 模型(如
m3e-base)。 -
在写入时,将标题转为
float[]向量。
-
-
混合检索(Hybrid Search):
-
升级 Lucene 到 9.x+,使用
KnnVectorQuery。 -
实现:
(关键词匹配 score * 0.7) + (向量相似度 score * 0.3)的简单融合逻辑。
-
-
-
关键技术:
- DJL/ONNX, Lucene HNSW Graph, HanLP.
目标: 支撑中量并发,优化延迟,实现精细化排序。
-
架构形态: 仿微服务架构(即便部署在单机,也要逻辑解耦)。
-
核心任务:
-
Scatter-Gather 模式:
-
模拟分片:在本地创建 3 个不同的 Lucene Directory(Shard 0, 1, 2)。
-
构建 Aggregator 层:收到请求后,使用
CompletableFuture并发向 3 个 Shard 查询,最后合并结果。
-
-
精排服务(Ranking Service):
-
特征快照: 将商品的静态分(销量、评分)加载到堆外内存(Off-heap)或 Redis 中。
-
算分逻辑: 在合并结果后,对 Top 100 商品应用自定义打分公式(如
Log(Sales) * Relevance)。可以尝试加载一个简单的 PMML 或 XGBoost 模型进行打分。
-
-
性能压测与调优:
-
使用 JMeter 或 Gatling 进行压测。
-
针对 GC(垃圾回收)进行调优,因为搜索产生大量临时对象。
-
-
-
关键技术:
- Java Concurrency (
CompletableFuture), Netty (可选,做高性能 RPC), Redis, XGBoost4J.
- Java Concurrency (
tiny-search-engine/
├── tiny-search-core/ # 核心索引与检索逻辑 (Lucene 封装)
├── tiny-search-api/ # Web 接口层 (Spring Boot)
├── tiny-search-data/ # 数据同步模块 (Canal/Kafka Client)
├── tiny-search-nlp/ # NLP 与 向量模型服务 (DJL, HanLP)
├── tiny-search-rank/ # 排序逻辑与特征加载
└── docker-compose.yml # 环境编排 (MySQL, Kafka, Redis)