跳到正文
Joeplover
后端开发·2026-03-20·约 5 分钟阅读

information-collection-s:Spring Boot 单表 80 万数据优化实战

招标公告信息采集系统从旧表 infor_notice 迁移到 tender_notice,引入 ES 全文搜索 + ngram/ik 分词器,精简接口响应字段,记录 4 个踩坑点和完整改造过程。

服务器与数据库优化

项目背景

information-collection-s 是一个招标公告信息采集系统,基于 Spring Boot 2.3.4 + Spring Cloud Hoxton.SR3 构建,服务于政府采购和招标信息的采集、检索和展示。系统承接的是原 infor_notice 单表(历史遗留),需要迁移到新表 tender_notice,并针对单表 80 万条数据量做全链路优化。

核心挑战是:在不中断服务的前提下,完成数据源切换、检索能力升级(引入 ES 全文搜索),并将接口响应字段精简到只返回业务需要的核心数据。

技术栈

组件版本用途
Spring Boot2.3.4.RELEASE后端框架
Spring CloudHoxton.SR3微服务治理
Java1.8JDK
MyBatis—数据访问层
MySQL—业务数据库
ElasticsearchRestHighLevelClient全文搜索
RedisJedis 客户端缓存 / 幂等

优化动机

原系统的问题很典型:单表 infor_notice 存量 80 万条数据,接口返回了将近 30 个字段,但前端真正用到的不到一半。大量无用的字段传输(content、styledContent、fileList、nodes、attrs 等)增加了网络开销和序列化成本。

同时,原系统的 ES 索引 information-collection-dev-s-file 只支持基础搜索,没有针对中文招标场景做分词优化,搜索准确性差。

核心改造

1. 数据源切换

将接口 /inforNotice/getNoticeList 的数据源从旧表 infor_notice 切换到新表 tender_notice:

场景改造前改造后
无关键字查询(查数据库)infor_noticetender_notice
有关键字搜索(查 ES)ES information-collection-dev-s-fileES information-collection-dev-s

2. ES 索引重设计

新建索引 information-collection-dev-s,针对招标公告场景做分词优化:

{
  "title": "ngram_analyzer (支持模糊搜索)",
  "noticeTypeName": "ik_max_word / ik_smart (中文分词)",
  "realNoticeTypeName": "ik_max_word / ik_smart (中文分词)"
}
  • title 字段 用 ngram 分词器实现模糊匹配,用户输入部分关键词也能命中
  • noticeTypeName / realNoticeTypeName 用 ik 分词器做精确中文分词

3. 响应字段精简

接口返回从 28 个字段精简到 14 个。删除的字段包括:

  • content / styledContent → 大文本内容,前端不展示
  • fileList / url / localFilePath / localFileName → 附件信息冗余
  • author / agency / contactInformation / sourceName → 招标场景不需要
  • attrs / nodes / remark / searchLabel / label → 历史遗留字段

同时新增 5 个业务字段:

字段类型说明
winnerAmountInteger中标金额
winnerCompanyString中标公司
loserCompaniesString未中标公司(JSON 数组)
contactPersonString联系人
contactPhoneString联系电话

4. 数据同步机制

新增 /sync/tenderNoticeToEs 接口,支持两种同步模式:

  • 全量同步(fullSync=true):一次性将 MySQL 全表数据同步到 ES
  • 增量同步(默认):按 last_sync_time 只同步新增/变更数据

单批 500 条,循环写入 ES。新增 tender_notice_sync_record 表记录每次同步的元信息(同步时间、同步条数、同步类型)。

当前存量数据:14,214 条(后续持续增长至 80 万+)。

踩坑记录

1. NGram 分词器差值错误

The difference between max_gram and min_gram in NGram Tokenizer 
must be less than or equal to: [1] but was [9]

ES 7.x 默认 max_ngram_diff 上限为 1。当 min_gram=2, max_gram=11 时差值 9 超出限制。在索引设置中加上 "max_ngram_diff": "10" 解决。

2. ES 索引未自动创建

项目启动时 afterPropertiesSet() 只初始化了 InforNoticeES 的索引,没有初始化 TenderNoticeES。导致首次搜索时报 index_not_found_exception。在初始化方法中补充一行解决:

initDetail(TenderNoticeES.class);

3. 泛型类型推断失败

ES 查询返回的 BaseResponse 泛型擦除导致编译报错:

// 编译报错:不兼容的类型
return BaseResponse.success(data);

// 修复:明确指定泛型
return BaseResponse.<String>buildResponse()
    .setCode(200)
    .setMessage("请求成功");

改造结果

指标改造前改造后
接口返回字段28 个14 个
检索方式单数据库 LIKEMySQL + ES 双引擎
中文搜索不支持ngram 模糊 + ik 精确分词
数据源旧表 infor_notice新表 tender_notice
ES 存量数据—14,214 条(持续增长)
同步方式—全量 + 增量手动触发

总结

这次改造的核心思路是精简 + 分层:

  • 接口只返回前端要的数据,不做无谓传输
  • MySQL 兜底精确查询,ES 负责全文搜索,各司其职
  • 数据同步机制设计为手动触发 + 增量模式,不给系统增加自动同步的运维负担

对于单表 80 万数据量来说,单纯靠 MySQL LIKE 迟早会扛不住。引入 ES 后,检索性能的瓶颈从数据库 IO 转移到了 ES 的倒排索引,配合 ngram 分词器还能实现模糊匹配,用户体验提升明显。

后续如果数据量继续增长到百万级,可以考虑将同步机制升级为监听 MySQL binlog 的准实时同步(Canal),而不是手动触发。