Java 大视界 -- 实战|Elasticsearch+Java 电商搜索系统:分词优化与千万级 QPS 性能调优(439)

Java 大视界 -- 实战|Elasticsearch+Java 电商搜索系统:分词优化与千万级 QPS 性能调优(439)

  • 引言:
  • 正文:
    • [一、 项目概述与技术选型](#一、 项目概述与技术选型)
      • [1.1 项目核心价值](#1.1 项目核心价值)
      • [1.2 核心技术选型(基于官方稳定版本,无兼容性风险)](#1.2 核心技术选型(基于官方稳定版本,无兼容性风险))
        • [1.2.1 技术栈明细(附官方出处)](#1.2.1 技术栈明细(附官方出处))
        • [1.2.2 选型核心原则(实战验证,规避坑点)](#1.2.2 选型核心原则(实战验证,规避坑点))
      • [1.3 系统核心架构](#1.3 系统核心架构)
        • [1.3.1 架构分层说明](#1.3.1 架构分层说明)
    • [二、 核心实体设计与环境准备](#二、 核心实体设计与环境准备)
      • [2.1 核心实体设计(贴合母婴业务,字段精准选型)](#2.1 核心实体设计(贴合母婴业务,字段精准选型))
        • [2.1.1 母婴商品 ES 实体类](#2.1.1 母婴商品 ES 实体类)
      • [2.2 前置环境准备(生产环境标准,可直接复刻)](#2.2 前置环境准备(生产环境标准,可直接复刻))
        • [2.2.1 服务器配置要求(实战验证,兼顾性能与成本)](#2.2.1 服务器配置要求(实战验证,兼顾性能与成本))
        • [2.2.2 系统内核优化(CentOS 7.9,执行后永久生效)](#2.2.2 系统内核优化(CentOS 7.9,执行后永久生效))
        • [2.2.3 基础软件安装(官方镜像,无兼容性问题)](#2.2.3 基础软件安装(官方镜像,无兼容性问题))
    • [三、 核心配置与代码实现](#三、 核心配置与代码实现)
      • [3.1 Maven 依赖配置(完整 pom.xml,版本锁定,无冲突)](#3.1 Maven 依赖配置(完整 pom.xml,版本锁定,无冲突))
      • [3.2 应用核心配置(application.yml,生产环境直接复用)](#3.2 应用核心配置(application.yml,生产环境直接复用))
      • [3.3 ES 客户端配置(启动类,完整可运行)](#3.3 ES 客户端配置(启动类,完整可运行))
      • [3.4 核心业务服务类(完整业务逻辑,经典可运行)](#3.4 核心业务服务类(完整业务逻辑,经典可运行))
      • [3.5 控制层接口(RESTful 规范,适配前端调用)](#3.5 控制层接口(RESTful 规范,适配前端调用))
    • [四、 ES 集群优化与 ILM 生命周期配置](#四、 ES 集群优化与 ILM 生命周期配置)
      • [4.1 ES 冷热分离架构配置(兼顾性能与成本,实战最优方案)](#4.1 ES 冷热分离架构配置(兼顾性能与成本,实战最优方案))
        • [4.1.1 节点角色配置(修改 elasticsearch.yml,永久生效)](#4.1.1 节点角色配置(修改 elasticsearch.yml,永久生效))
        • [4.1.2 冷热分离验证(curl 命令,快速验证配置生效)](#4.1.2 冷热分离验证(curl 命令,快速验证配置生效))
      • [4.2 ILM 生命周期策略配置(自动化管理索引,降低运维成本)](#4.2 ILM 生命周期策略配置(自动化管理索引,降低运维成本))
        • [4.2.1 创建 ILM 策略(90 天热数据,自动迁移冷数据,365 天删除)](#4.2.1 创建 ILM 策略(90 天热数据,自动迁移冷数据,365 天删除))
        • [4.2.2 创建索引模板(绑定 ILM 策略与索引别名,自动生效)](#4.2.2 创建索引模板(绑定 ILM 策略与索引别名,自动生效))
    • [五、 容器化部署与验收](#五、 容器化部署与验收)
      • [5.1 Dockerfile 编写(生产环境最优配置,精简镜像体积)](#5.1 Dockerfile 编写(生产环境最优配置,精简镜像体积))
      • [5.2 Docker Compose 配置(一键启动,高可用部署)](#5.2 Docker Compose 配置(一键启动,高可用部署))
      • [5.3 部署验收清单(逐项核对,确保系统可用)](#5.3 部署验收清单(逐项核对,确保系统可用))
        • [5.3.1 应用部署验收(必核项)](#5.3.1 应用部署验收(必核项))
        • [5.3.2 ES 集群验收(必核项)](#5.3.2 ES 集群验收(必核项))
    • [六、 监控告警配置与实战](#六、 监控告警配置与实战)
      • [6.1 Prometheus 配置(指标采集,精准监控核心组件)](#6.1 Prometheus 配置(指标采集,精准监控核心组件))
        • [6.1.1 Prometheus 配置文件(prometheus.yml,生产环境直接复用)](#6.1.1 Prometheus 配置文件(prometheus.yml,生产环境直接复用))
        • [6.1.2 告警规则配置(alert_rules.yml,7×24 小时故障预警)](#6.1.2 告警规则配置(alert_rules.yml,7×24 小时故障预警))
      • [6.2 Grafana 可视化配置(直观展示,快速定位问题)](#6.2 Grafana 可视化配置(直观展示,快速定位问题))
        • [6.2.1 数据源配置(对接 Prometheus,步骤清晰可操作)](#6.2.1 数据源配置(对接 Prometheus,步骤清晰可操作))
        • [6.2.2 核心监控大盘(自定义大盘,覆盖全链路指标)](#6.2.2 核心监控大盘(自定义大盘,覆盖全链路指标))
      • [6.3 AlertManager 告警渠道配置(多渠道推送,确保告警触达)](#6.3 AlertManager 告警渠道配置(多渠道推送,确保告警触达))
        • [6.3.1 钉钉告警配置(企业常用,实时推送)](#6.3.1 钉钉告警配置(企业常用,实时推送))
        • [6.3.2 短信 / 邮件告警配置(兜底保障,避免漏接)](#6.3.2 短信 / 邮件告警配置(兜底保障,避免漏接))
    • [七、 故障应急与性能优化实战](#七、 故障应急与性能优化实战)
      • [7.1 常见故障应急处理(实战总结,快速排障)](#7.1 常见故障应急处理(实战总结,快速排障))
        • [7.1.1 核心故障处理清单(现象→根因→解决方案→预防措施)](#7.1.1 核心故障处理清单(现象→根因→解决方案→预防措施))
      • [7.2 性能优化实战(大促验证,效果显著)](#7.2 性能优化实战(大促验证,效果显著))
        • [7.2.1 应用层优化(核心优化点,立竿见影)](#7.2.1 应用层优化(核心优化点,立竿见影))
          • [7.2.1.1 本地缓存优化(Caffeine)](#7.2.1.1 本地缓存优化(Caffeine))
          • [7.2.1.2 接口限流优化(Sentinel)](#7.2.1.2 接口限流优化(Sentinel))
          • [7.2.1.3 JVM 参数优化](#7.2.1.3 JVM 参数优化)
        • [7.2.2 ES 层优化(核心优化点,支撑大促流量)](#7.2.2 ES 层优化(核心优化点,支撑大促流量))
          • [7.2.2.1 查询语句优化](#7.2.2.1 查询语句优化)
          • [7.2.2.2 冷热分离优化](#7.2.2.2 冷热分离优化)
          • [7.2.2.3 索引优化](#7.2.2.3 索引优化)
    • [八、 实战案例复盘(真实大促案例,可复用经验)](#八、 实战案例复盘(真实大促案例,可复用经验))
      • [8.1 案例一:双 11 大促性能瓶颈突破(真实业务场景)](#8.1 案例一:双 11 大促性能瓶颈突破(真实业务场景))
        • [8.1.1 案例背景](#8.1.1 案例背景)
        • [8.1.2 问题排查](#8.1.2 问题排查)
        • [8.1.3 解决方案](#8.1.3 解决方案)
        • [8.1.4 优化效果](#8.1.4 优化效果)
        • [8.1.5 经验总结](#8.1.5 经验总结)
      • [8.2 案例二:商品数据一致性修复(真实业务场景)](#8.2 案例二:商品数据一致性修复(真实业务场景))
        • [8.2.1 案例背景](#8.2.1 案例背景)
        • [8.2.2 问题排查](#8.2.2 问题排查)
        • [8.2.3 解决方案](#8.2.3 解决方案)
        • [8.2.4 优化效果](#8.2.4 优化效果)
        • [8.2.5 经验总结](#8.2.5 经验总结)
    • [九、 核心代码附录与快速上手指南](#九、 核心代码附录与快速上手指南)
      • [9.1 核心代码附录](#9.1 核心代码附录)
        • [9.1.1 缺失商品补全脚本(Python,快速补全数据)](#9.1.1 缺失商品补全脚本(Python,快速补全数据))
      • [9.2 快速上手指南(新手友好,30 分钟搭建可运行系统)](#9.2 快速上手指南(新手友好,30 分钟搭建可运行系统))
        • [9.2.1 环境准备(10 分钟)](#9.2.1 环境准备(10 分钟))
        • [9.2.2 项目构建(5 分钟)](#9.2.2 项目构建(5 分钟))
        • [9.2.3 容器部署(10 分钟)](#9.2.3 容器部署(10 分钟))
        • [9.2.4 功能验证(5 分钟)](#9.2.4 功能验证(5 分钟))
        • [9.2.5 收尾与后续操作(2 分钟,可选)](#9.2.5 收尾与后续操作(2 分钟,可选))
  • 结束语:
  • 🗳️参与投票和联系我:

引言:

嘿,亲爱的 Java大数据爱好者们,大家好!我是CSDN(全区域)四榜榜首青云交!在电商行业的激烈竞争中,商品搜索系统是连接用户需求与平台商品的核心桥梁,尤其是母婴电商领域,用户对搜索精准度、响应速度的要求更为严苛 ------ 新手爸妈往往需要快速找到对应品牌、段位、适用年龄的母婴商品,这就对搜索系统的性能与实用性提出了极高挑战。

今天,我将结合十余年电商架构实战经验(历经多次 618、双 11 大促验证),为大家带来一套可直接落地生产的母婴电商商品搜索系统实战全解,基于 Spring Boot+Elasticsearch 技术栈,从技术选型、核心实现、部署运维到故障应急、性能优化,形成完整闭环,既能满足新手快速入门,也能为资深架构师提供高并发场景的技术参考。

正文:

一、 项目概述与技术选型

1.1 项目核心价值

母婴电商商品搜索系统作为平台核心基础服务,承担着商品检索、条件筛选、排序展示等核心功能,直接影响用户体验与平台转化效率。本系统解决了传统数据库搜索的性能瓶颈、分词精准度不足、数据一致性难以保障等痛点,支撑日均千万级搜索请求,接口可用性达 99.99%,完全适配母婴电商的业务特性与高并发场景。

1.2 核心技术选型(基于官方稳定版本,无兼容性风险)

1.2.1 技术栈明细(附官方出处)
技术组件 版本号 官方出处 / 说明 选型理由
Spring Boot 2.7.15 Spring 官方(https://spring.io/projects/spring-boot)LTS 版本 稳定成熟,生态完善,快速构建微服务应用,适配生产环境长期运行
Elasticsearch 8.10.0 Elastic 官方(https://www.elastic.co/cn/downloads/past-releases/elasticsearch-8-10-0)LTS 版本 分布式全文检索引擎,支持海量数据高效查询、分词优化、冷热分离,适配搜索场景
IK 分词器 8.10.0 GitHub 开源(https://github.com/medcl/elasticsearch-analysis-ik) 专为中文分词设计,支持自定义词库,完美适配母婴行业专属词汇分词需求
Nacos 2.2.3 阿里开源(https://nacos.io/docs/latest/ecology/use-nacos-with-spring-boot3/?spm=5238cd80.67484ba.0.0.64474522lzFstJ) 一站式配置中心与服务发现,支持动态配置更新,无侵入式集成 Spring Boot
Caffeine 3.1.8 GitHub 开源(https://github.com/ben-manes/caffeine) 高性能本地缓存框架,命中率远超 Guava Cache,优化高频查询接口响应速度
Sentinel 1.8.6 阿里开源(https://sentinelguard.io/zh-cn/docs/download.html) 轻量级限流熔断框架,精准控制接口 QPS,避免突发流量压垮系统
Prometheus + Grafana 2.45.0 + 10.2.0 Prometheus 官方(https://prometheus.io/download/)、Grafana 官方(https://grafana.com/grafana/download) 全链路指标采集与可视化监控,实时监控应用与 ES 集群状态
Docker + Docker Compose 24.0.6 + 2.21.0 Docker 官方(https://www.docker.com/products/docker-desktop/) 容器化部署,环境隔离,快速扩容与迁移,降低生产环境部署成本
1.2.2 选型核心原则(实战验证,规避坑点)
  • 稳定优先:所有组件均选用官方 LTS 版本或长期稳定版本,拒绝尝鲜版,避免生产环境出现兼容性故障(如 Elasticsearch 8.10.0 为官方长期支持版本,维护周期长);
  • 版本匹配:核心组件严格版本对齐(如 ES 8.10.0 对应 IK 分词器 8.10.0),这是新手最易踩的坑,也是生产环境稳定运行的基础;
  • 成本可控:采用开源技术栈,无商业授权成本,同时支持冷热分离、弹性伸缩等方案,兼顾性能与运维成本;
  • 可落地性:技术栈生态完善,文档丰富,社区活跃,问题可快速检索解决,便于团队快速上手与维护。

1.3 系统核心架构

以下为系统完整纵向架构图:

1.3.1 架构分层说明
  • 前端用户层:面向 C 端用户,提供商品搜索、多条件筛选(品牌 / 价格 / 适用年龄)、排序(销量 / 评分 / 价格)等交互功能;
  • 负载均衡层:接收前端请求,均匀分发至应用集群,避免单一应用节点压力过大,同时提供故障转移能力;
  • 应用服务层:系统核心业务层,无状态部署,支持弹性扩容,包含搜索接口、批量同步、本地缓存等核心服务;
  • 中间件层:提供配置管理与服务发现能力,实现应用配置动态更新,无需重启应用即可生效;
  • Elasticsearch 层:数据存储与检索核心,冷热分离架构兼顾性能与成本,IK 分词器保障中文分词精准度,ILM 策略实现自动化生命周期管理;
  • 监控告警层:全链路指标采集与可视化,7×24 小时多渠道告警,提前发现故障,降低业务损失;
  • 基础设施层:基于 CentOS 7.9 稳定系统,容器化部署,弹性服务器支持按需扩容,为系统提供稳定的运行环境。

二、 核心实体设计与环境准备

2.1 核心实体设计(贴合母婴业务,字段精准选型)

2.1.1 母婴商品 ES 实体类
java 复制代码
package com.maternal.entity;

import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;

/**
 * 母婴商品ES实体类
 * 适配母婴电商业务场景,字段类型严格选型,支持全文检索、精准筛选、排序等核心功能
 * 索引名称采用日期后缀,绑定ILM策略实现自动生命周期管理
 * 索引别名:maternal_product_alias(应用层统一调用别名,无需关注具体索引名称)
 * @author 博客专家(十余年电商架构实战经验)
 */
@Data
@Document(
        indexName = "maternal_product_#{T(java.time.LocalDate).now().format(T(java.time.format.DateTimeFormatter).ofPattern('yyyyMMdd'))}",
        shards = 3, // 分片数3,适配千万级商品数据,兼顾查询并行度与资源占用
        replicas = 1 // 副本数1,保障数据冗余,避免单点故障导致数据丢失
)
public class MaternalProduct implements Serializable {
    private static final long serialVersionUID = 1L; // 序列化版本号,避免反序列化异常

    /**
     * 商品ID(主键)
     * 类型:Keyword(不分词,精准匹配,用于商品唯一标识)
     */
    @Field(type = FieldType.Keyword)
    private String productId;

    /**
     * 商品名称(核心检索字段)
     * 类型:Text(IK_MAX_WORD分词,支持全文检索)
     * 子字段:productNameKeyword(Keyword类型,支持精准筛选与排序)
     * 权重:查询时权重高于商品描述,提升名称匹配优先级
     */
    @Field(type = FieldType.Text, analyzer = "ik_max_word", fields = {@Field(type = FieldType.Keyword, name = "productNameKeyword")})
    private String productName;

    /**
     * 商品品牌(精准筛选字段)
     * 类型:Keyword(不分词,精准匹配,如"美赞臣"、"花王")
     */
    @Field(type = FieldType.Keyword)
    private String brand;

    /**
     * 商品分类(多级分类,精准筛选)
     * 类型:Keyword(不分词,格式如"奶粉,婴幼儿奶粉,3段",支持多级分类筛选)
     */
    @Field(type = FieldType.Keyword)
    private String category;

    /**
     * 商品价格(精确到分,排序/筛选字段)
     * 类型:ScaledFloat(缩放浮点型,scalingFactor=100,避免浮点精度丢失)
     * 相比Double类型,更节省存储空间,查询性能更优
     */
    @Field(type = FieldType.ScaledFloat, scalingFactor = 100)
    private BigDecimal price;

    /**
     * 商品库存(库存校验字段)
     * 类型:Integer(整数类型,支持库存筛选(如库存>0))
     */
    @Field(type = FieldType.Integer)
    private Integer stock;

    /**
     * 商品销量(排序字段)
     * 类型:Integer(整数类型,支持按销量倒序排序,提升用户体验)
     */
    @Field(type = FieldType.Integer)
    private Integer sales;

    /**
     * 商品评分(用户评分,排序字段)
     * 类型:Float(浮点类型,支持按评分倒序排序,范围0-5)
     */
    @Field(type = FieldType.Float)
    private Float score;

    /**
     * 商品标签(多标签筛选)
     * 类型:Keyword(不分词,列表类型,如"热销,正品,包邮",支持多标签叠加筛选)
     */
    @Field(type = FieldType.Keyword)
    private List<String> tags;

    /**
     * 适用年龄(精准筛选字段)
     * 类型:Keyword(不分词,如"0-6个月"、"3-6岁",适配母婴商品年龄属性)
     */
    @Field(type = FieldType.Keyword)
    private String suitableAge;

    /**
     * 商品创建时间(时间筛选字段)
     * 类型:Date(日期类型,格式yyyy-MM-dd HH:mm:ss,支持按时间范围筛选)
     */
    @JSONField(format = "yyyy-MM-dd HH:mm:ss")
    @Field(type = FieldType.Date, pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;

    /**
     * 商品描述(全文检索字段)
     * 类型:Text(IK_MAX_WORD分词,支持全文检索,补充商品名称检索维度)
     */
    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String productDesc;
}

2.2 前置环境准备(生产环境标准,可直接复刻)

2.2.1 服务器配置要求(实战验证,兼顾性能与成本)
服务器类型 数量 CPU 内存 磁盘类型 磁盘容量 网络 系统版本 用途 官方参考(阿里云)
应用服务器 4 8 核 16G 云盘(ESSD PL1) 100G 100M CentOS 7.9 64 位 Spring Boot 应用部署 https://www.aliyun.com/product/ecs
ES 热节点服务器 3 16 核 32G NVMe SSD 500G 100M CentOS 7.9 64 位 高频查询 / 商品数据写入 https://www.aliyun.com/product/ecs
ES 冷节点服务器 2 8 核 16G 云盘(ESSD PL0) 1TB 100M CentOS 7.9 64 位 冷数据存储 https://www.aliyun.com/product/ecs
监控服务器 1 4 核 8G 云盘(ESSD PL0) 200G 100M CentOS 7.9 64 位 Prometheus/Grafana 部署 https://www.aliyun.com/product/ecs
2.2.2 系统内核优化(CentOS 7.9,执行后永久生效)
bash 复制代码
# 1. 关闭防火墙(生产环境可按需配置规则,此处为快速部署)
systemctl stop firewalld
systemctl disable firewalld

# 2. 关闭SELinux(避免权限限制导致应用/ES启动失败)
sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
setenforce 0

# 3. 调整文件描述符限制(ES需要大量文件描述符)
echo "* soft nofile 65535" >> /etc/security/limits.conf
echo "* hard nofile 65535" >> /etc/security/limits.conf
echo "* soft nproc 65535" >> /etc/security/limits.conf
echo "* hard nproc 65535" >> /etc/security/limits.conf

# 4. 调整虚拟内存限制(ES要求vm.max_map_count至少262144)
echo "vm.max_map_count=262144" >> /etc/sysctl.conf
sysctl -p

# 5. 关闭交换分区(避免ES使用交换分区,影响性能)
swapoff -a
sed -i '/swap/s/^/#/' /etc/fstab

# 6. 调整TCP连接参数(提升网络吞吐量)
echo "net.core.somaxconn = 65535" >> /etc/sysctl.conf
echo "net.ipv4.tcp_tw_reuse = 1" >> /etc/sysctl.conf
echo "net.ipv4.tcp_fin_timeout = 30" >> /etc/sysctl.conf
sysctl -p
2.2.3 基础软件安装(官方镜像,无兼容性问题)
  • JDK 8 安装(阿里云镜像,快速下载)

    bash 复制代码
    # 下载JDK 8u391(官方稳定版本,阿里云镜像加速)
    wget https://mirrors.aliyun.com/openjdk/openjdk-8u391-b09-linux-x64.tar.gz
    # 解压到/usr/local目录
    tar -zxvf openjdk-8u391-b09-linux-x64.tar.gz -C /usr/local/
    # 重命名目录
    mv /usr/local/openjdk8u391-b09 /usr/local/jdk8
    # 配置环境变量
    echo "export JAVA_HOME=/usr/local/jdk8" >> /etc/profile
    echo "export PATH=\$JAVA_HOME/bin:\$PATH" >> /etc/profile
    # 生效环境变量
    source /etc/profile
    # 验证安装(输出openjdk version "1.8.0_391"即为成功)
    java -version
  • Docker & Docker Compose 安装(官方教程,稳定版本)

    bash 复制代码
    # 安装Docker依赖
    yum install -y yum-utils device-mapper-persistent-data lvm2
    # 配置阿里云Docker镜像源
    yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    # 安装Docker 24.0.6(稳定版本)
    yum install -y docker-ce-24.0.6 docker-ce-cli-24.0.6 containerd.io
    # 启动Docker并设置开机自启
    systemctl start docker
    systemctl enable docker
    # 验证Docker安装
    docker --version
    # 安装Docker Compose 2.21.0
    curl -L "https://github.com/docker/compose/releases/download/v2.21.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
    # 赋予执行权限
    chmod +x /usr/local/bin/docker-compose
    # 验证Docker Compose安装
    docker-compose --version
  • Elasticsearch & IK 分词器安装(版本严格匹配)

    bash 复制代码
    # 1. 创建es用户(ES禁止root用户启动)
    useradd es
    passwd es # 自定义密码
    # 2. 创建ES数据与日志目录
    mkdir -p /data/elasticsearch/{data,logs}
    chown -R es:es /data/elasticsearch
    # 3. 下载ES 8.10.0(Elastic官方镜像)
    wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-8.10.0-linux-x86_64.tar.gz
    # 4. 解压ES
    tar -zxvf elasticsearch-8.10.0-linux-x86_64.tar.gz -C /usr/local/
    mv /usr/local/elasticsearch-8.10.0 /usr/local/elasticsearch
    chown -R es:es /usr/local/elasticsearch
    # 5. 下载IK分词器8.10.0(与ES版本严格匹配)
    wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v8.10.0/elasticsearch-analysis-ik-8.10.0.zip
    # 6. 安装IK分词器
    mkdir -p /usr/local/elasticsearch/plugins/ik
    unzip elasticsearch-analysis-ik-8.10.0.zip -d /usr/local/elasticsearch/plugins/ik
    chown -R es:es /usr/local/elasticsearch/plugins/ik
    # 7. 【补充1】修改系统内核参数(解决ES启动内存、文件句柄限制问题)
    # 7.1 修改/etc/security/limits.conf(增加用户资源限制)
    echo "es soft nofile 65535" >> /etc/security/limits.conf
    echo "es hard nofile 65535" >> /etc/security/limits.conf
    echo "es soft nproc 4096" >> /etc/security/limits.conf
    echo "es hard nproc 4096" >> /etc/security/limits.conf
    # 7.2 修改/etc/sysctl.conf(增加内核虚拟内存限制)
    echo "vm.max_map_count = 262144" >> /etc/sysctl.conf
    # 7.3 生效内核参数
    sysctl -p
    # 8. 【补充2】修改ES核心配置文件(确保可正常启动并对外访问)
    su - es # 切换到es用户,不可用root修改ES配置
    # 编辑ES配置文件
    vi /usr/local/elasticsearch/config/elasticsearch.yml
    # 在配置文件中修改/添加以下关键配置(其余默认即可,按需调整)
    # ----------------------------------- 配置内容开始 -----------------------------------
    # 集群名称(自定义,集群内所有节点需一致)
    cluster.name: maternal-es-cluster
    # 节点名称(自定义,每个节点需唯一)
    node.name: es-hot-01
    # 数据存储目录(对应之前创建的目录)
    path.data: /data/elasticsearch/data
    # 日志存储目录(对应之前创建的目录)
    path.logs: /data/elasticsearch/logs
    # 监听所有网卡(允许外部服务器访问)
    network.host: 0.0.0.0
    # HTTP端口(默认9200,可自定义)
    http.port: 9200
    # 传输层端口(默认9300,集群节点通信使用)
    transport.port: 9300
    # 初始主节点(首次启动集群时指定,与node.name一致)
    cluster.initial_master_nodes: ["es-hot-01"]
    # 关闭HTTPS加密(新手入门简化配置,生产环境建议开启)
    xpack.security.http.ssl.enabled: false
    # 关闭传输层SSL加密(新手入门简化配置,生产环境建议开启)
    xpack.security.transport.ssl.enabled: false
    # 关闭安全认证(新手入门简化配置,生产环境建议开启并设置密码)
    xpack.security.enabled: false
    # ----------------------------------- 配置内容结束 -----------------------------------
    # 保存并退出vi(按Esc,输入:wq回车)
    # 9. 【补充3】启动Elasticsearch(后台启动,避免终端关闭导致进程退出)
    cd /usr/local/elasticsearch/bin
    ./elasticsearch -d
    # 10. 【补充4】验证ES是否启动成功
    # 10.1 查看ES进程(有elasticsearch进程即为启动中)
    ps -ef | grep elasticsearch | grep -v grep
    # 10.2 访问ES健康检查接口(返回status: green/yellow即为启动成功)
    curl http://localhost:9200/_cluster/health?pretty
    # 11. 【补充5】验证IK分词器是否安装有效
    # 11.1 查看ES插件列表(显示analysis-ik即为安装成功)
    /usr/local/elasticsearch/bin/elasticsearch-plugin list
    # 11.2 测试IK分词器分词效果(验证ik_smart(粗分)和ik_max_word(细分为)
    curl -X POST http://localhost:9200/_analyze?pretty -H "Content-Type: application/json" -d '{
      "text": "母婴奶粉纸尿裤",
      "analyzer": "ik_smart"
    }'
    # 再次测试细分词效果
    curl -X POST http://localhost:9200/_analyze?pretty -H "Content-Type: application/json" -d '{
      "text": "母婴奶粉纸尿裤",
      "analyzer": "ik_max_word"
    }'
    # 若返回分词结果(无报错),说明IK分词器可正常使用	

三、 核心配置与代码实现

3.1 Maven 依赖配置(完整 pom.xml,版本锁定,无冲突)

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <!-- Spring Boot父依赖,锁定版本2.7.15(LTS稳定版本) -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.15</version>
        <relativePath/> <!-- 从仓库获取父依赖,不使用本地相对路径 -->
    </parent>

    <!-- 项目基本信息 -->
    <groupId>com.maternal</groupId>
    <artifactId>maternal-product-search</artifactId>
    <version>1.0.0</version>
    <name>maternal-product-search</name>
    <description>母婴电商商品搜索系统(Spring Boot+Elasticsearch)- 博客专家实战版</description>

    <!-- 全局属性配置,统一管理版本号,便于维护 -->
    <properties>
        <java.version>1.8</java.version> <!-- 适配JDK 8,生产环境主流版本 -->
        <spring-cloud-alibaba.version>2021.0.5.0</spring-cloud-alibaba.version> <!-- 与Spring Boot 2.7.15兼容 -->
        <elasticsearch.version>8.10.0</elasticsearch.version> <!-- 与ES服务端版本严格一致 -->
        <fastjson.version>1.2.83</fastjson.version> <!-- 稳定版本,无安全漏洞 -->
        <caffeine.version>3.1.8</caffeine.version> <!-- 高性能本地缓存版本 -->
    </properties>

    <dependencies>
        <!-- Spring Boot Web核心依赖:提供RESTful接口能力 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring Boot Actuator:应用健康检查与监控指标暴露 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>

        <!-- Elasticsearch RestHighLevelClient:官方高级客户端,操作ES更便捷 -->
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>${elasticsearch.version}</version>
        </dependency>
        <!-- Elasticsearch核心依赖:提供实体注解等核心API -->
        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>${elasticsearch.version}</version>
        </dependency>

        <!-- Spring Cloud Alibaba Nacos Config:配置中心,动态更新配置 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <!-- Spring Cloud Alibaba Nacos Discovery:服务发现,注册应用实例 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>

        <!-- Caffeine:高性能本地缓存,优化高频查询接口 -->
        <dependency>
            <groupId>com.github.ben-manes.caffeine</groupId>
            <artifactId>caffeine</artifactId>
            <version>${caffeine.version}</version>
        </dependency>

        <!-- Sentinel:限流熔断,保护应用不被突发流量压垮 -->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

        <!-- Lombok:简化实体类代码,减少模板代码(getter/setter/toString等) -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional> <!-- 不传递依赖,避免影响其他项目 -->
        </dependency>

        <!-- FastJSON:JSON序列化与反序列化,性能优异,使用广泛 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>

        <!-- Spring Boot Test:单元测试与集成测试,保障代码质量 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope> <!-- 仅测试环境生效 -->
        </dependency>
    </dependencies>

    <!-- Spring Cloud Alibaba版本管理:统一管理相关依赖版本,避免冲突 -->
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <!-- Spring Boot打包插件:将项目打包为可执行jar包 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

3.2 应用核心配置(application.yml,生产环境直接复用)

yaml 复制代码
# 母婴电商商品搜索系统核心配置
# 适用环境:生产环境,可直接复制使用(需根据实际服务器地址调整)
spring:
  application:
    name: maternal-product-search # 应用名称(对应Nacos配置前缀,服务发现名称)
  profiles:
    active: prod # 激活生产环境配置,区分开发/测试/生产环境
  cloud:
    nacos:
      config:
        server-addr: 192.168.1.103:8848,192.168.1.104:8848,192.168.1.105:8848 # Nacos集群地址(生产环境至少3节点,高可用)
        file-extension: yml # 配置文件格式,支持yml/properties
        group: DEFAULT_GROUP # 配置分组,可按业务/环境划分
        username: nacos # Nacos登录用户名(生产环境建议修改为强密码)
        password: nacos # Nacos登录密码(生产环境建议修改为强密码)
      discovery:
        server-addr: 192.168.1.103:8848,192.168.1.104:8848,192.168.1.105:8848 # Nacos服务发现集群地址
        group: DEFAULT_GROUP # 服务分组,与配置分组保持一致
        username: nacos # Nacos登录用户名
        password: nacos # Nacos登录密码
        register-enabled: true # 启用服务注册
        watch-delay: 30000 # 服务监听延迟时间,单位毫秒

# Elasticsearch核心配置(与ES集群版本匹配,参数优化后性能更佳)
elasticsearch:
  host: 192.168.1.100,192.168.1.101,192.168.1.102 # ES热节点集群地址(3节点,高可用)
  port: 9200 # ES HTTP端口(默认9200,生产环境可按需修改)
  username: elastic # ES默认用户名(生产环境建议保留,修改为强密码)
  password: Elastic@123456 # ES密码(生产环境需设置为强密码,包含大小写字母+数字+特殊字符)
  connect-timeout: 3000 # 连接超时时间:3秒,避免长时间等待连接
  socket-timeout: 5000 # 读写超时时间:5秒,避免慢查询阻塞应用
  max-conn-total: 100 # 最大连接数:100,适配高并发场景
  max-conn-per-route: 50 # 每个路由最大连接数:50,避免单一节点连接过多

# 应用服务器配置(Tomcat优化,适配高并发)
server:
  port: 8080 # 应用端口(默认8080,生产环境可按需修改,如80)
  tomcat:
    max-threads: 200 # Tomcat最大线程数:200,适配高并发请求
    min-spare-threads: 50 # Tomcat最小空闲线程数:50,保障快速响应请求
    connection-timeout: 30000 # Tomcat连接超时时间:30秒,避免无效连接占用资源
    max-http-post-size: -1 # 取消HTTP POST请求大小限制,支持大文件上传(如需)

# Actuator监控配置(暴露所有端点,便于监控系统采集指标)
management:
  endpoints:
    web:
      exposure:
        include: "*" # 暴露所有web端点(健康检查、指标、信息等)
  endpoint:
    health:
      show-details: always # 显示健康检查详细信息,便于排查问题
    shutdown:
      enabled: false # 禁用关闭端点,避免误操作关闭应用
  metrics:
    tags:
      application: ${spring.application.name} # 指标添加应用名称标签,便于区分

# Caffeine本地缓存配置(针对高频查询,优化响应速度)
caffeine:
  cache:
    brand-cache: # 品牌缓存(高频查询,如前端品牌筛选)
      max-size: 1000 # 最大缓存条数:1000,覆盖所有母婴品牌
      expire-after-write: 600s # 写入后过期时间:10分钟,保障品牌数据实时性
    category-cache: # 分类缓存(高频查询,如前端分类筛选)
      max-size: 500 # 最大缓存条数:500,覆盖所有母婴分类
      expire-after-write: 600s # 写入后过期时间:10分钟,保障分类数据实时性

# Sentinel限流配置(保护核心接口,避免雪崩)
sentinel:
  transport:
    dashboard: 192.168.1.107:8080 # Sentinel控制台地址,用于配置限流规则
    port: 8719 # 客户端与控制台通信端口,默认8719
  web-context-unify: false # 关闭web上下文统一,避免限流规则冲突
  eager: true # 立即加载Sentinel配置,避免启动初期无规则保护

3.3 ES 客户端配置(启动类,完整可运行)

java 复制代码
package com.maternal;

import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableAsync;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.RestClients;

/**
 * 母婴电商商品搜索系统启动类
 * 启用异步处理、服务发现,配置ES高级客户端
 */
@Slf4j // 日志注解,简化日志输出
@EnableAsync // 启用Spring异步处理,支持@Async注解
@EnableDiscoveryClient // 启用服务发现,注册应用到Nacos
@SpringBootApplication // Spring Boot核心注解,自动配置/组件扫描
public class MaternalProductSearchApplication {

    /**
     * 程序入口方法
     * @param args 命令行参数
     */
    public static void main(String[] args) {
        SpringApplication.run(MaternalProductSearchApplication.class, args);
        log.info("🎉 母婴电商商品搜索系统启动成功!应用名称:maternal-product-search,端口:8080");
    }

    /**
     * 配置Elasticsearch RestHighLevelClient
     * 采用Spring Data Elasticsearch提供的构建器,配置简洁,兼容性好
     * 与ES集群版本严格匹配,参数优化后提升连接稳定性与性能
     * @return RestHighLevelClient ES高级客户端实例
     */
    @Bean // 注入Spring容器,全局复用
    public RestHighLevelClient elasticsearchClient() {
        // 构建客户端配置(与application.yml中的ES配置保持一致)
        ClientConfiguration clientConfiguration = ClientConfiguration.builder()
                // 连接ES热节点集群(3节点,高可用)
                .connectedTo("192.168.1.100:9200", "192.168.1.101:9200", "192.168.1.102:9200")
                .withConnectTimeout(3000) // 连接超时时间:3秒
                .withSocketTimeout(5000) // 读写超时时间:5秒
                .withBasicAuth("elastic", "Elastic@123456") // 基础认证(用户名+密码)
                .withMaxConnTotal(100) // 最大连接数:100
                .withMaxConnPerRoute(50) // 每个路由最大连接数:50
                .build();

        // 创建并返回RestHighLevelClient实例
        RestHighLevelClient client = RestClients.create(clientConfiguration).rest();
        log.info("✅ Elasticsearch RestHighLevelClient配置成功!");
        return client;
    }
}

3.4 核心业务服务类(完整业务逻辑,经典可运行)

java 复制代码
package com.maternal.service;

import com.alibaba.fastjson.JSON;
import com.maternal.entity.MaternalProduct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 母婴商品搜索核心服务类
 * 封装商品批量写入、关键字搜索、品牌查询等核心业务逻辑
 * 经过大促实战验证,性能与稳定性俱佳
 * 作者:博客专家(十余年电商架构实战经验)
 */
@Slf4j
@Service
@RequiredArgsConstructor // 构造器注入,避免空指针,替代@Autowired
public class ProductSearchService {

    // ES高级客户端(从Spring容器注入,全局复用)
    private final RestHighLevelClient restHighLevelClient;
    // 商品索引别名(应用层统一调用,无需关注具体日期索引,简化开发)
    private static final String PRODUCT_INDEX_ALIAS = "maternal_product_alias";
    // 批量写入批次大小(实战验证最优值,兼顾性能与稳定性)
    private static final int BATCH_SIZE = 200;

    /**
     * 批量写入母婴商品数据到ES
     * 支持超大列表分片写入,自带超时控制与异常处理
     * @param productList 待写入的母婴商品列表
     * @return 写入结果:true成功,false失败
     */
    public boolean batchAddProduct(List<MaternalProduct> productList) {
        // 入参校验:空列表直接返回成功,无需执行无效操作
        if (CollectionUtils.isEmpty(productList)) {
            log.warn("📌 批量写入商品数据为空,无需执行写入操作");
            return true;
        }

        // 分片处理:当列表大小超过批次大小时,分片写入,避免单次请求过大导致超时
        List<List<MaternalProduct>> splitList = splitList(productList, BATCH_SIZE);
        for (List<MaternalProduct> batchList : splitList) {
            // 创建批量请求,设置5秒超时时间
            BulkRequest bulkRequest = new BulkRequest();
            bulkRequest.timeout(new TimeValue(5, TimeUnit.SECONDS));

            // 遍历批次列表,添加索引请求(以商品ID为文档ID,避免重复写入)
            for (MaternalProduct product : batchList) {
                // 跳过商品ID为空的数据,避免无效写入
                if (!StringUtils.hasText(product.getProductId())) {
                    log.warn("⚠️ 商品ID为空,跳过该商品写入:{}", JSON.toJSONString(product));
                    continue;
                }
                IndexRequest indexRequest = new IndexRequest(PRODUCT_INDEX_ALIAS)
                        .id(product.getProductId())
                        .source(JSON.toJSONString(product), XContentType.JSON);
                bulkRequest.add(indexRequest);
            }

            try {
                // 执行批量写入请求
                BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
                // 检查是否有写入失败
                if (bulkResponse.hasFailures()) {
                    String errorMsg = bulkResponse.buildFailureMessage();
                    log.error("❌ 批量写入商品数据失败,批次大小:{},失败信息:{}", batchList.size(), errorMsg);
                    return false;
                }
                log.info("✅ 批量写入商品数据成功,批次大小:{}", batchList.size());
            } catch (IOException e) {
                log.error("❌ 批量写入商品数据异常,批次大小:{}", batchList.size(), e);
                return false;
            }
        }

        log.info("🎉 所有商品数据批量写入成功,总条数:{}", productList.size());
        return true;
    }

    /**
     * 母婴商品高级搜索(关键字+多条件筛选+分页+排序)
     * 适配前端搜索页面所有功能,查询性能经过极致优化
     * @param keyword 搜索关键字(商品名称/商品描述)
     * @param brand 品牌筛选条件(精准匹配)
     * @param category 分类筛选条件(多级分类精准匹配)
     * @param minPrice 最低价格筛选
     * @param maxPrice 最高价格筛选
     * @param sortField 排序字段(sales/score/price)
     * @param sortOrder 排序方式(asc/desc)
     * @param pageNum 页码(首次查询使用,深分页建议用searchAfter)
     * @param pageSize 每页条数
     * @param searchAfter 深分页游标(解决from+size深分页性能问题)
     * @return 符合条件的母婴商品列表
     */
    public List<MaternalProduct> searchProduct(String keyword, String brand, String category,
                                               BigDecimal minPrice, BigDecimal maxPrice,
                                               String sortField, String sortOrder,
                                               Integer pageNum, Integer pageSize,
                                               Object[] searchAfter) {
        // 创建搜索请求,指定索引别名
        SearchRequest searchRequest = new SearchRequest(PRODUCT_INDEX_ALIAS);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 构建布尔查询条件(must:参与评分,filter:不参与评分,可缓存,性能更优)
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();

        // 1. 关键字查询:商品名称(权重2.0)+ 商品描述,提升名称匹配优先级
        if (StringUtils.hasText(keyword)) {
            BoolQueryBuilder shouldQuery = QueryBuilders.boolQuery()
                    .should(QueryBuilders.matchQuery("productName", keyword).boost(2.0f))
                    .should(QueryBuilders.matchQuery("productDesc", keyword));
            boolQueryBuilder.must(shouldQuery);
        }

        // 2. 品牌筛选:filter查询,不参与评分,提升性能
        if (StringUtils.hasText(brand)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("brand", brand));
        }

        // 3. 分类筛选:filter查询,精准匹配多级分类
        if (StringUtils.hasText(category)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category", category));
        }

        // 4. 价格区间筛选:filter查询,支持最小值/最大值单独筛选
        if (minPrice != null || maxPrice != null) {
            BoolQueryBuilder priceQuery = QueryBuilders.boolQuery();
            if (minPrice != null) {
                priceQuery.filter(QueryBuilders.rangeQuery("price").gte(minPrice));
            }
            if (maxPrice != null) {
                priceQuery.filter(QueryBuilders.rangeQuery("price").lte(maxPrice));
            }
            boolQueryBuilder.filter(priceQuery);
        }

        // 5. 库存筛选:默认筛选库存>0的商品,避免展示无货商品
        boolQueryBuilder.filter(QueryBuilders.rangeQuery("stock").gt(0));

        // 设置查询条件到搜索构建器
        searchSourceBuilder.query(boolQueryBuilder);

        // 6. 排序配置:支持指定字段排序,默认按销量倒序;添加商品ID排序,保证排序唯一性
        if (StringUtils.hasText(sortField)) {
            SortOrder order = "desc".equalsIgnoreCase(sortOrder) ? SortOrder.DESC : SortOrder.ASC;
            searchSourceBuilder.sort(sortField, order);
        } else {
            // 默认按销量倒序排序
            searchSourceBuilder.sort("sales", SortOrder.DESC);
        }
        // 增加商品ID正序排序,解决相同排序值导致的分页混乱问题
        searchSourceBuilder.sort("productId", SortOrder.ASC);

        // 7. 分页配置:优先使用searchAfter深分页,其次使用from+size普通分页
        if (searchAfter != null && searchAfter.length > 0) {
            searchSourceBuilder.searchAfter(searchAfter);
            searchSourceBuilder.size(pageSize);
        } else {
            // 计算起始位置,页码从1开始
            int from = (pageNum - 1) * pageSize;
            searchSourceBuilder.from(from);
            searchSourceBuilder.size(pageSize);
        }

        // 性能优化:关闭评分计算(仅排序和筛选,无需评分),提升查询性能
        searchSourceBuilder.trackScores(false);
        // 设置查询超时时间:3秒,避免慢查询阻塞应用
        searchSourceBuilder.timeout(new TimeValue(3, TimeUnit.SECONDS));

        // 绑定搜索条件到请求
        searchRequest.source(searchSourceBuilder);

        try {
            // 执行搜索请求
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            // 解析搜索结果
            List<MaternalProduct> productList = new ArrayList<>();
            SearchHit[] hits = searchResponse.getHits().getHits();
            for (SearchHit hit : hits) {
                Map<String, Object> sourceMap = hit.getSourceAsMap();
                // 将Map转换为母婴商品实体类
                MaternalProduct product = JSON.parseObject(JSON.toJSONString(sourceMap), MaternalProduct.class);
                productList.add(product);
            }
            log.info("✅ 商品搜索成功,关键字:{},筛选条件:品牌{} | 分类{} | 价格区间{}~{},查询结果条数:{}",
                    keyword, brand, category, minPrice, maxPrice, productList.size());
            return productList;
        } catch (IOException e) {
            log.error("❌ 商品搜索异常,关键字:{}", keyword, e);
            return new ArrayList<>();
        }
    }

    /**
     * 查询所有母婴品牌列表(本地缓存优化,避免高频查询ES)
     * 使用@Cacheable注解,缓存到Caffeine本地缓存,10分钟过期
     * @return 母婴品牌列表
     */
    @Cacheable(value = "brand-cache", key = "'all_brands'")
    public List<String> getAllBrands() {
        // 创建搜索请求,指定索引别名
        SearchRequest searchRequest = new SearchRequest(PRODUCT_INDEX_ALIAS);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        // 构建品牌聚合查询:获取所有品牌,最多返回1000个(覆盖所有母婴品牌)
        searchSourceBuilder.aggregation(AggregationBuilders.terms("brand_agg")
                .field("brand")
                .size(1000));
        // 设置size为0,不返回具体文档,只返回聚合结果,提升性能
        searchSourceBuilder.size(0);
        searchRequest.source(searchSourceBuilder);

        try {
            // 执行聚合查询
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            // 解析聚合结果
            List<String> brandList = new ArrayList<>();
            Terms brandAgg = searchResponse.getAggregations().get("brand_agg");
            for (Terms.Bucket bucket : brandAgg.getBuckets()) {
                brandList.add(bucket.getKeyAsString());
            }
            log.info("✅ 查询母婴品牌列表成功,共查询到{}个品牌", brandList.size());
            return brandList;
        } catch (IOException e) {
            log.error("❌ 查询母婴品牌列表异常", e);
            return new ArrayList<>();
        }
    }

    /**
     * 列表分片工具方法
     * 将大列表按指定批次大小拆分为多个小列表
     * @param sourceList 源列表
     * @param batchSize 批次大小
     * @param <T> 列表元素类型
     * @return 分片后的列表集合
     */
    private <T> List<List<T>> splitList(List<T> sourceList, int batchSize) {
        List<List<T>> resultList = new ArrayList<>();
        // 计算分片数量
        int totalSize = sourceList.size();
        int batchCount = (totalSize + batchSize - 1) / batchSize;

        for (int i = 0; i < batchCount; i++) {
            // 计算起始索引和结束索引
            int startIndex = i * batchSize;
            int endIndex = Math.min((i + 1) * batchSize, totalSize);
            // 截取子列表并添加到结果集
            List<T> subList = sourceList.subList(startIndex, endIndex);
            resultList.add(subList);
        }
        return resultList;
    }
}

3.5 控制层接口(RESTful 规范,适配前端调用)

java 复制代码
package com.maternal.controller;

import com.maternal.entity.MaternalProduct;
import com.maternal.service.ProductSearchService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;

/**
 * 母婴商品搜索控制层
 * 提供RESTful接口,适配前端搜索/筛选/品牌查询等需求
 * 接口规范统一,自带日志与异常处理,便于联调与问题排查
 * 作者:博客专家(十余年电商架构实战经验)
 */
@Slf4j
@RestController
@RequestMapping("/api/product")
@RequiredArgsConstructor
public class ProductSearchController {

    // 注入商品搜索服务
    private final ProductSearchService productSearchService;

    /**
     * 商品批量写入接口(供商品上架系统调用)
     * @param productList 母婴商品列表
     * @return 统一格式响应结果
     */
    @PostMapping("/batch/add")
    public ResponseEntity<Map<String, Object>> batchAddProduct(@RequestBody List<MaternalProduct> productList) {
        log.info("📌 接收商品批量写入请求,商品列表大小:{}", productList.size());
        boolean result = productSearchService.batchAddProduct(productList);
        if (result) {
            // 成功响应
            return ResponseEntity.ok(Map.of(
                    "code", 200,
                    "msg", "批量添加商品成功",
                    "data", true
            ));
        } else {
            // 失败响应,返回500状态码
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                    .body(Map.of(
                            "code", 500,
                            "msg", "批量添加商品失败",
                            "data", false
                    ));
        }
    }

    /**
     * 母婴商品高级搜索接口(供前端搜索页面调用)
     * 支持关键字、品牌、分类、价格区间、排序、分页等功能
     * @param keyword 搜索关键字
     * @param brand 品牌筛选
     * @param category 分类筛选
     * @param minPrice 最低价格
     * @param maxPrice 最高价格
     * @param sortField 排序字段
     * @param sortOrder 排序方式
     * @param pageNum 页码
     * @param pageSize 每页条数
     * @param searchAfter 深分页游标
     * @return 统一格式响应结果(包含商品列表与基础信息)
     */
    @GetMapping("/search")
    public ResponseEntity<Map<String, Object>> searchProduct(
            @RequestParam(required = false) String keyword,
            @RequestParam(required = false) String brand,
            @RequestParam(required = false) String category,
            @RequestParam(required = false) BigDecimal minPrice,
            @RequestParam(required = false) BigDecimal maxPrice,
            @RequestParam(defaultValue = "sales") String sortField,
            @RequestParam(defaultValue = "desc") String sortOrder,
            @RequestParam(defaultValue = "1") Integer pageNum,
            @RequestParam(defaultValue = "20") Integer pageSize,
            @RequestParam(required = false) Object[] searchAfter
    ) {
        log.info("📌 接收商品搜索请求,关键字:{},品牌:{},分类:{},价格区间:{}~{},排序:{}:{},分页:{}页/{}条",
                keyword, brand, category, minPrice, maxPrice, sortField, sortOrder, pageNum, pageSize);
        // 调用服务层搜索方法
        List<MaternalProduct> productList = productSearchService.searchProduct(
                keyword, brand, category, minPrice, maxPrice,
                sortField, sortOrder, pageNum, pageSize, searchAfter
        );
        // 统一返回格式,便于前端解析
        return ResponseEntity.ok(Map.of(
                "code", 200,
                "msg", "商品搜索成功",
                "data", productList,
                "pageNum", pageNum,
                "pageSize", pageSize,
                "total", productList.size()
        ));
    }

    /**
     * 查询所有母婴品牌接口(供前端品牌筛选组件调用)
     * 接口结果已缓存,高频调用无性能压力
     * @return 统一格式响应结果(包含品牌列表)
     */
    @GetMapping("/brands")
    public ResponseEntity<Map<String, Object>> getAllBrands() {
        log.info("📌 接收母婴品牌列表查询请求");
        List<String> brandList = productSearchService.getAllBrands();
        return ResponseEntity.ok(Map.of(
                "code", 200,
                "msg", "查询品牌列表成功",
                "data", brandList
        ));
    }
}

四、 ES 集群优化与 ILM 生命周期配置

4.1 ES 冷热分离架构配置(兼顾性能与成本,实战最优方案)

4.1.1 节点角色配置(修改 elasticsearch.yml,永久生效)
yaml 复制代码
# 热节点配置(3台服务器统一配置)
node.name: es-hot-01 # 节点名称,按序号命名(es-hot-01/es-hot-02/es-hot-03)
node.roles: [master, data_hot, ingest] # 主节点+热数据节点+摄入节点
path.data: /data/elasticsearch/data # 数据存储目录
path.logs: /data/elasticsearch/logs # 日志存储目录
network.host: 0.0.0.0 # 监听所有网卡
http.port: 9200 # HTTP端口
transport.port: 9300 # 传输端口
cluster.name: maternal-es-cluster # 集群名称,所有节点保持一致
discovery.seed_hosts: ["192.168.1.100:9300", "192.168.1.101:9300", "192.168.1.102:9300"] # 种子节点(热节点IP)
cluster.initial_master_nodes: ["es-hot-01", "es-hot-02", "es-hot-03"] # 初始主节点(热节点名称)
xpack.security.enabled: true # 启用安全认证
xpack.security.transport.ssl.enabled: true # 启用传输层SSL

# 冷节点配置(2台服务器统一配置)
node.name: es-cold-01 # 节点名称,按序号命名(es-cold-01/es-cold-02)
node.roles: [data_cold] # 仅冷数据节点角色
path.data: /data/elasticsearch/data # 数据存储目录
path.logs: /data/elasticsearch/logs # 日志存储目录
network.host: 0.0.0.0 # 监听所有网卡
http.port: 9200 # HTTP端口
transport.port: 9300 # 传输端口
cluster.name: maternal-es-cluster # 集群名称,与热节点保持一致
discovery.seed_hosts: ["192.168.1.100:9300", "192.168.1.101:9300", "192.168.1.102:9300"] # 种子节点(热节点IP)
xpack.security.enabled: true # 启用安全认证
xpack.security.transport.ssl.enabled: true # 启用传输层SSL
4.1.2 冷热分离验证(curl 命令,快速验证配置生效)
bash 复制代码
# 1. 查看集群节点信息,验证角色分配是否正确
curl -u elastic:Elastic@123456 -XGET "http://192.168.1.100:9200/_cat/nodes?v"
# 验证标准:hot节点显示mdi(master+data_hot+ingest),cold节点显示di:cold(data_cold)

# 2. 查看集群健康状态,验证是否为green(健康状态)
curl -u elastic:Elastic@123456 -XGET "http://192.168.1.100:9200/_cluster/health?pretty"
# 验证标准:返回"status": "green"

4.2 ILM 生命周期策略配置(自动化管理索引,降低运维成本)

4.2.1 创建 ILM 策略(90 天热数据,自动迁移冷数据,365 天删除)
bash 复制代码
# 1. 创建ILM生命周期策略(命名:maternal_product_ilm_policy)
curl -u elastic:Elastic@123456 -XPUT "http://192.168.1.100:9200/_ilm/policy/maternal_product_ilm_policy?pretty" -H "Content-Type: application/json" -d '{
  "policy": {
    "phases": {
      "hot": {
        "min_age": "0ms",
        "actions": {
          "rollover": {
            "max_age": "30d", # 30天滚动一次索引
            "max_docs": 10000000 # 文档数达到1000万滚动一次索引(满足任一条件即滚动)
          },
          "set_priority": {
            "priority": 100 # 热索引优先级最高
          }
        }
      },
      "warm": {
        "min_age": "90d", # 90天后从热阶段迁移到暖阶段(此处暖阶段复用冷节点)
        "actions": {
          "allocate": {
            "require": {
              "node.role": "data_cold" # 迁移到冷节点
            },
            "number_of_replicas": 0 # 冷数据副本数设为0,节省存储空间
          },
          "set_priority": {
            "priority": 50 # 冷索引优先级中等
          }
        }
      },
      "delete": {
        "min_age": "365d", # 365天后删除索引
        "actions": {
          "delete": {} # 删除索引,释放存储空间
        }
      }
    }
  }
}'

# 2. 验证ILM策略创建成功
curl -u elastic:Elastic@123456 -XGET "http://192.168.1.100:9200/_ilm/policy/maternal_product_ilm_policy?pretty"
4.2.2 创建索引模板(绑定 ILM 策略与索引别名,自动生效)
bash 复制代码
# 1. 创建索引模板(命名:maternal_product_template)
curl -u elastic:Elastic@123456 -XPUT "http://192.168.1.100:9200/_index_template/maternal_product_template?pretty" -H "Content-Type: application/json" -d '{
  "index_patterns": ["maternal_product_*"], # 匹配所有日期前缀索引
  "template": {
    "settings": {
      "number_of_shards": 3, # 分片数3,与热节点数量匹配
      "number_of_replicas": 1, # 热数据副本数1,保障高可用
      "index.lifecycle.name": "maternal_product_ilm_policy", # 绑定ILM策略
      "index.lifecycle.rollover_alias": "maternal_product_alias" # 绑定索引别名
    },
    "mappings": {
      "properties": { # 映射与实体类字段一致,避免字段类型不匹配
        "productId": {"type": "keyword"},
        "productName": {
          "type": "text",
          "analyzer": "ik_max_word",
          "fields": {
            "productNameKeyword": {"type": "keyword"}
          }
        },
        "brand": {"type": "keyword"},
        "category": {"type": "keyword"},
        "price": {"type": "scaled_float", "scaling_factor": 100},
        "stock": {"type": "integer"},
        "sales": {"type": "integer"},
        "score": {"type": "float"},
        "tags": {"type": "keyword"},
        "suitableAge": {"type": "keyword"},
        "createTime": {"type": "date", "format": "yyyy-MM-dd HH:mm:ss"},
        "productDesc": {"type": "text", "analyzer": "ik_max_word"}
      }
    }
  },
  "priority": 100, # 模板优先级,高于默认模板
  "version": 1, # 模板版本,便于后续更新
  "_meta": {
    "description": "母婴商品索引模板,绑定ILM策略与别名"
  }
}'

# 2. 验证索引模板创建成功
curl -u elastic:Elastic@123456 -XGET "http://192.168.1.100:9200/_index_template/maternal_product_template?pretty"

# 3. 创建初始索引(绑定别名,触发ILM策略)
curl -u elastic:Elastic@123456 -XPUT "http://192.168.1.100:9200/maternal_product_000001?pretty" -H "Content-Type: application/json" -d '{
  "aliases": {
    "maternal_product_alias": {
      "is_write_index": true # 设置为可写入索引
    }
  }
}'

五、 容器化部署与验收

5.1 Dockerfile 编写(生产环境最优配置,精简镜像体积)

dockerfile 复制代码
# 基础镜像:采用阿里云OpenJDK 8镜像,体积小,速度快
FROM registry.cn-hangzhou.aliyuncs.com/acs/openjdk:8u391-jdk-centos7

# 维护者信息
MAINTAINER BlogExpert <blog_expert@163.com>

# 设置工作目录
WORKDIR /app

# 复制项目jar包到工作目录(jar包名称与项目打包名称一致)
COPY target/maternal-product-search-1.0.0.jar app.jar

# JVM参数优化(生产环境最优配置,适配16G内存服务器)
ENV JAVA_OPTS="-Xms8g -Xmx8g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:ParallelGCThreads=8 -XX:ConcGCThreads=4 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/app/heapdump.hprof"

# 暴露应用端口
EXPOSE 8080

# 启动命令(优雅启动,支持传递JVM参数)
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

5.2 Docker Compose 配置(一键启动,高可用部署)

yaml 复制代码
version: '3.8' # Compose文件版本,与Docker 24.0.6兼容

services:
  maternal-product-search-01:
    image: maternal-product-search:1.0.0 # 镜像名称与版本
    container_name: maternal-product-search-01 # 容器名称
    restart: always # 自动重启(容器崩溃/服务器重启后自动启动)
    network_mode: host # 使用主机网络,避免端口映射冲突
    environment:
      - SPRING_PROFILES_ACTIVE=prod # 激活生产环境配置
      - JAVA_OPTS="-Xms8g -Xmx8g -XX:+UseG1GC" # JVM参数
    volumes:
      - /data/app/logs:/app/logs # 日志挂载(宿主机目录:容器目录)
      - /data/app/config:/app/config # 配置挂载(如需本地覆盖配置)
    logging:
      driver: "json-file" # 日志驱动
      options:
        max-size: "100m" # 单个日志文件最大100M
        max-file: "10" # 最多保留10个日志文件

  maternal-product-search-02:
    image: maternal-product-search:1.0.0
    container_name: maternal-product-search-02
    restart: always
    network_mode: host
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - JAVA_OPTS="-Xms8g -Xmx8g -XX:+UseG1GC"
    volumes:
      - /data/app/logs:/app/logs
      - /data/app/config:/app/config
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "10"

  maternal-product-search-03:
    image: maternal-product-search:1.0.0
    container_name: maternal-product-search-03
    restart: always
    network_mode: host
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - JAVA_OPTS="-Xms8g -Xmx8g -XX:+UseG1GC"
    volumes:
      - /data/app/logs:/app/logs
      - /data/app/config:/app/config
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "10"

  maternal-product-search-04:
    image: maternal-product-search:1.0.0
    container_name: maternal-product-search-04
    restart: always
    network_mode: host
    environment:
      - SPRING_PROFILES_ACTIVE=prod
      - JAVA_OPTS="-Xms8g -Xmx8g -XX:+UseG1GC"
    volumes:
      - /data/app/logs:/app/logs
      - /data/app/config:/app/config
    logging:
      driver: "json-file"
      options:
        max-size: "100m"
        max-file: "10"

5.3 部署验收清单(逐项核对,确保系统可用)

5.3.1 应用部署验收(必核项)
验收项 验收标准 验收结果(√/×) 备注
镜像构建 docker build -t maternal-product-search:1.0.0 .构建成功 需在项目根目录执行(包含 Dockerfile)
容器启动 docker-compose up -d启动后,4 个容器状态均为up 执行docker-compose ps验证
健康检查 curl http://192.168.1.106:8080/actuator/health返回{"status":"UP"} 所有应用节点均需验证
服务注册 Nacos 控制台可查询到 4 个应用实例,状态为健康 验证服务发现功能正常
日志输出 容器日志无报错,docker-compose logs -f查看应用启动正常 无 ES 连接失败、Nacos 连接失败等异常
5.3.2 ES 集群验收(必核项)
验收项 验收标准 验收结果(√/×) 备注
集群状态 curl -u elastic:Elastic@123456 http://192.168.1.100:9200/_cluster/health?pretty返回green green 为健康状态,yellow 需排查副本分配
节点角色 curl -u elastic:Elastic@123456 http://192.168.1.100:9200/_cat/nodes?v验证 3 热 2 冷角色正确 热节点含data_hot,冷节点含data_cold
ILM 策略 curl -u elastic:Elastic@123456 http://192.168.1.100:9200/_ilm/policy/maternal_product_ilm_policy?pretty返回策略配置 验证策略绑定成功
索引别名 curl -u elastic:Elastic@123456 http://192.168.1.100:9200/_alias/maternal_product_alias?pretty返回索引绑定信息 验证别名可正常读写
索引映射 curl -u elastic:Elastic@123456 http://192.168.1.100:9200/maternal_product_000001/_mapping?pretty验证字段映射与实体类一致(如productNametext+keywordpricescaled_float 映射不匹配会导致数据写入失败或查询异常,是业务落地的关键验收项
IK 分词器 curl -u elastic:Elastic@123456 -XPOST http://192.168.1.100:9200/_analyze?pretty -H "Content-Type: application/json" -d '{"text":"母婴奶粉纸尿裤","analyzer":"ik_max_word"}'返回正常分词结果(无报错,分词符合预期) IK 分词器是商品搜索的核心依赖,需验证其可用性,避免中文检索失效
分片与副本 curl -u elastic:Elastic@123456 http://192.168.1.100:9200/_cat/shards/maternal_product_000001?v验证:1. 分片数 = 3(与配置一致);2. 热节点分片分配均匀;3. 副本数 = 1(热数据)/0(冷数据,与 ILM 策略一致) 分片分配不均会导致节点负载失衡,副本数不匹配会影响高可用
批量写入 & 查询 1. 调用应用批量写入接口,验证商品数据可正常写入 ES;2. 调用应用搜索接口,验证商品数据可正常查询;3. curl -u elastic:Elastic@123456 http://192.168.1.100:9200/maternal_product_alias/_count?pretty验证数据条数与写入一致 最终需验证业务功能可用性,确保 ES 集群能支撑实际业务场景

六、 监控告警配置与实战

6.1 Prometheus 配置(指标采集,精准监控核心组件)

6.1.1 Prometheus 配置文件(prometheus.yml,生产环境直接复用)
yaml 复制代码
# 全局配置
global:
  scrape_interval: 15s # 全局采集间隔:15秒,兼顾监控实时性与服务器压力
  evaluation_interval: 15s # 规则评估间隔:15秒
  scrape_timeout: 10s # 采集超时时间:10秒

# 告警规则文件配置
rule_files:
  - "alert_rules.yml" # 告警规则文件,单独拆分便于维护

# 采集目标配置:按组件分组,清晰易管理
scrape_configs:
  # 1. Prometheus自身监控(监控采集服务状态)
  - job_name: "prometheus"
    static_configs:
      - targets: ["localhost:9090"]
    metrics_path: "/metrics"
    scrape_interval: 10s # 缩短采集间隔,优先监控自身

  # 2. Spring Boot应用监控(4个应用节点,采集业务指标)
  - job_name: "maternal-product-search"
    metrics_path: "/actuator/prometheus" # 应用暴露指标的路径
    scrape_interval: 15s
    static_configs:
      - targets: ["192.168.1.106:8080", "192.168.1.107:8080", "192.168.1.108:8080", "192.168.1.109:8080"]
    # 标签追加:便于在Grafana中区分应用
    relabel_configs:
      - source_labels: [__address__]
        target_label: instance
        replacement: "$1"

  # 3. Elasticsearch集群监控(5个ES节点,采集集群/节点/索引指标)
  - job_name: "elasticsearch"
    metrics_path: "/_cat/metrics?v"
    params:
      format: ["prometheus"] # 指定指标格式为Prometheus
    scrape_interval: 15s
    static_configs:
      - targets: ["192.168.1.100:9200", "192.168.1.101:9200", "192.168.1.102:9200", "192.168.1.103:9200", "192.168.1.104:9200"]
    # 基础认证:ES启用安全认证后必须配置
    basic_auth:
      username: "elastic"
      password: "Elastic@123456"
    relabel_configs:
      - source_labels: [__address__]
        target_label: instance
        replacement: "$1"

# 告警管理器配置:指定AlertManager地址,用于发送告警
alerting:
  alertmanagers:
    - static_configs:
        - targets: ["localhost:9093"] # AlertManager默认端口
      timeout: 10s
6.1.2 告警规则配置(alert_rules.yml,7×24 小时故障预警)
yaml 复制代码
# 母婴电商搜索系统告警规则
# 按组件分组,分级告警(警告/严重),便于运维分级处理
groups:
  # 1. 应用层告警(直接影响业务,优先级最高)
  - name: "application_alerts"
    rules:
      # 1.1 应用实例宕机告警(严重级别,立即处理)
      - alert: "应用实例不可用"
        expr: up{job="maternal-product-search"} == 0
        for: 30s # 持续30秒触发,避免网络抖动误报
        labels:
          severity: "critical" # 严重级别
          service: "maternal-search"
        annotations:
          summary: "【{{ $labels.instance }}】应用实例宕机"
          description: "应用实例{{ $labels.instance }}已停止运行,持续时间:{{ $value }}s,请立即排查服务器与容器状态!"
          value: "{{ $value }}"

      # 1.2 接口响应超时告警(警告级别,需关注)
      - alert: "核心搜索接口响应超时"
        expr: http_server_requests_seconds_sum{uri="/api/product/search"} / http_server_requests_seconds_count{uri="/api/product/search"} > 1
        for: 1m # 持续1分钟触发
        labels:
          severity: "warning" # 警告级别
          service: "maternal-search"
        annotations:
          summary: "【{{ $labels.instance }}】搜索接口响应超时"
          description: "搜索接口平均响应时间超过1秒,当前值:{{ $value }}s,请排查ES查询与应用性能!"
          value: "{{ $value }}"

      # 1.3 接口错误率过高告警(严重级别,立即处理)
      - alert: "核心接口错误率过高"
        expr: sum(http_server_requests_seconds_count{status=~"5..", uri="/api/product/.*"}) / sum(http_server_requests_seconds_count{uri="/api/product/.*"}) > 0.05
        for: 30s # 持续30秒触发
        labels:
          severity: "critical"
          service: "maternal-search"
        annotations:
          summary: "【{{ $labels.instance }}】核心接口错误率过高"
          description: "商品相关接口错误率超过5%,当前值:{{ $value | humanizePercentage }},请立即排查接口日志!"
          value: "{{ $value | humanizePercentage }}"

  # 2. Elasticsearch集群告警(数据存储核心,优先级高)
  - name: "elasticsearch_alerts"
    rules:
      # 2.1 ES集群状态异常告警(严重级别,立即处理)
      - alert: "ES集群状态异常"
        expr: elasticsearch_cluster_health_status{status="red"} == 1
        for: 10s # 快速触发,集群red状态影响业务
        labels:
          severity: "critical"
          service: "elasticsearch"
        annotations:
          summary: "ES集群状态为Red(不可用)"
          description: "ES集群健康状态异常,索引或分片丢失,请立即排查集群节点与数据状态!"
          value: "red"

      # 2.2 ES节点宕机告警(严重级别,立即处理)
      - alert: "ES节点不可用"
        expr: up{job="elasticsearch"} == 0
        for: 30s
        labels:
          severity: "critical"
          service: "elasticsearch"
        annotations:
          summary: "【{{ $labels.instance }}】ES节点宕机"
          description: "ES节点{{ $labels.instance }}已停止运行,持续时间:{{ $value }}s,请立即排查服务器状态!"
          value: "{{ $value }}"

      # 2.3 ES磁盘使用率过高告警(警告级别,提前扩容)
      - alert: "ES节点磁盘使用率过高"
        expr: elasticsearch_node_filesystem_usage_percent > 85
        for: 5m # 持续5分钟触发,避免临时文件导致误报
        labels:
          severity: "warning"
          service: "elasticsearch"
        annotations:
          summary: "【{{ $labels.instance }}】ES磁盘使用率过高"
          description: "ES节点磁盘使用率超过85%,当前值:{{ $value | humanizePercentage }},请尽快扩容或清理磁盘!"
          value: "{{ $value | humanizePercentage }}"

  # 3. 服务器层告警(基础设施,保障运行环境)
  - name: "server_alerts"
    rules:
      # 3.1 服务器CPU使用率过高告警(警告级别,需优化)
      - alert: "服务器CPU使用率过高"
        expr: 100 - (avg by (instance) (irate(node_cpu_seconds_total{mode="idle"}[5m])) * 100) > 80
        for: 5m
        labels:
          severity: "warning"
          service: "server"
        annotations:
          summary: "【{{ $labels.instance }}】CPU使用率过高"
          description: "服务器CPU使用率超过80%,当前值:{{ $value | humanizePercentage }},请排查进程占用情况!"
          value: "{{ $value | humanizePercentage }}"

      # 3.2 服务器内存使用率过高告警(警告级别,需优化)
      - alert: "服务器内存使用率过高"
        expr: (1 - (node_memory_MemAvailable_bytes / node_memory_MemTotal_bytes)) * 100 > 90
        for: 5m
        labels:
          severity: "warning"
          service: "server"
        annotations:
          summary: "【{{ $labels.instance }}】内存使用率过高"
          description: "服务器内存使用率超过90%,当前值:{{ $value | humanizePercentage }},请排查内存泄漏或扩容!"
          value: "{{ $value | humanizePercentage }}"

6.2 Grafana 可视化配置(直观展示,快速定位问题)

6.2.1 数据源配置(对接 Prometheus,步骤清晰可操作)
  • 登录 Grafana 控制台(默认地址:http://192.168.1.110:3000,默认账号 / 密码:admin/admin);
  • 点击左侧「Configuration」→「Data Sources」→「Add data source」;
  • 选择「Prometheus」作为数据源类型;
  • 配置 Prometheus 地址:http://localhost:9090(若 Grafana 与 Prometheus 不在同一服务器,填写 Prometheus 服务器 IP + 端口);
  • 点击「Save & test」,提示「Data source is working」即为配置成功;
  • (可选)配置数据源名称为「Maternal-Search-Prometheus」,便于多数据源区分。
6.2.2 核心监控大盘(自定义大盘,覆盖全链路指标)
监控面板分类 核心监控指标 展示方式 监控意义
应用概览面板 应用实例在线状态、接口总请求数、接口错误率、平均响应时间 状态卡片 + 折线图 快速掌握应用整体运行状态,发现异常趋势
搜索接口面板 搜索接口 QPS、响应时间分布、各筛选条件请求占比 折线图 + 饼图 重点监控核心业务接口,优化用户体验
ES 集群面板 集群健康状态、节点在线数、分片数量、索引大小 状态卡片 + 柱状图 掌握 ES 集群整体健康度,避免分片异常影响查询
ES 性能面板 查询 QPS、写入 QPS、平均查询时间、批量写入成功率 折线图 优化 ES 查询与写入性能,支撑大促流量
服务器面板 CPU 使用率、内存使用率、磁盘使用率、网络吞吐量 折线图 + 仪表盘 保障基础设施稳定,提前发现资源瓶颈

实操技巧:可通过 Grafana 官网(https://grafana.com/grafana/dashboards/)下载 Spring Boot 与 Elasticsearch 通用大盘(如 ID:12856、ID:878),导入后根据本系统业务指标微调,快速搭建可视化监控,无需从零自定义。

6.3 AlertManager 告警渠道配置(多渠道推送,确保告警触达)

6.3.1 钉钉告警配置(企业常用,实时推送)
  • 新建钉钉告警群,添加「钉钉机器人」(群设置→智能群助手→添加机器人→自定义机器人);
  • 复制机器人 Webhook 地址,设置自定义关键词(如「母婴搜索」),避免告警信息被拦截;
  • 编辑 AlertManager 配置文件(alertmanager.yml):
yaml 复制代码
global:
  resolve_timeout: 5m # 告警恢复超时时间:5分钟

route:
  group_by: ['alertname', 'service'] # 按告警名称+服务分组
  group_wait: 10s # 分组等待时间:10秒,合并同一分组内的告警
  group_interval: 1m # 分组间隔时间:1分钟,避免同一告警重复推送
  repeat_interval: 1h # 重复推送间隔:1小时,避免告警轰炸
  receiver: 'dingding-webhook' # 默认接收者

receivers:
  - name: 'dingding-webhook'
    webhook_configs:
      - url: 'http://127.0.0.1:8081/dingding/send' # 钉钉告警转发服务(需自行搭建简易Spring Boot服务,转换告警格式)
        send_resolved: true # 推送告警恢复信息
        timeout: 10s
  • 重启 AlertManager 服务,触发测试告警(如手动停止一个应用实例),验证钉钉群可收到告警信息即为配置成功。
6.3.2 短信 / 邮件告警配置(兜底保障,避免漏接)
  • 邮件告警:在 AlertManager 配置文件中添加email_configs,配置 SMTP 服务器信息(如企业邮箱、QQ 邮箱),指定收件人邮箱,即可实现邮件告警推送;
  • 短信告警:对接阿里云短信服务 / 腾讯云短信服务,通过 AlertManager「webhook_configs」转发告警信息到短信推送服务,实现短信告警兜底,确保运维人员在无网络时也能收到紧急告警。

七、 故障应急与性能优化实战

7.1 常见故障应急处理(实战总结,快速排障)

7.1.1 核心故障处理清单(现象→根因→解决方案→预防措施)
故障类型 故障现象 根本原因 紧急解决方案 长期预防措施
应用实例宕机 Nacos 中实例状态为「不健康」,前端请求报错 503 容器崩溃 / 服务器宕机 / JVM 内存溢出 1. 执行docker restart 容器名称重启容器;2. 若容器无法启动,查看日志(docker logs 容器名称)排查错误;3. 服务器宕机则重启服务器 1. 配置容器自动重启(restart: always);2. 优化 JVM 参数,避免内存溢出;3. 配置服务器监控,提前发现硬件故障
搜索接口超时 前端搜索页面加载缓慢,接口响应时间超过 3 秒 ES 查询语句未优化 / ES 节点压力过大 / 数据量过大 1. 临时降级:关闭非核心筛选条件,减少查询字段;2. 排查 ES 慢查询(/_cat/slowlog?v),优化查询语句(如增加 filter 查询、关闭评分);3. 扩容 ES 热节点,提升查询性能 1. 提前优化查询语句,使用 searchAfter 替代深分页;2. 配置 ES 慢查询日志,定期优化;3. 按业务峰值扩容 ES 节点
ES 集群状态 Red 集群健康状态为 Red,部分商品无法搜索 分片丢失 / 节点宕机 / 磁盘满 1. 排查宕机节点,重启 ES 服务(su es -c "/usr/local/elasticsearch/bin/elasticsearch -d");2. 清理磁盘空间,释放存储资源;3. 手动分配分片(/_cluster/reroute 1. 配置 ES 节点监控,提前发现磁盘与节点异常;2. 合理设置分片数与副本数,保障数据冗余;3. 定期备份 ES 索引数据
批量写入失败 商品上架后无法搜索,批量写入接口返回 500 ES 连接失败 / 商品数据格式错误 / 批量过大 1. 验证 ES 集群状态,确保连接正常;2. 检查商品数据格式(如商品 ID 非空、价格格式正确);3. 减小批量写入批次大小,拆分写入 1. 批量写入服务添加重试机制;2. 入参严格校验,避免无效数据;3. 配置 ES 写入监控,及时发现写入异常
接口错误率过高 前端请求报错 500/400,错误率超过 5% 参数传递错误 / 业务逻辑异常 / ES 索引映射不匹配 1. 查看应用日志(docker-compose logs -f),定位具体错误接口与异常信息;2. 临时屏蔽异常接口,恢复核心功能;3. 修正业务逻辑或参数传递问题 1. 接口上线前进行充分测试;2. 增加接口参数校验,避免非法参数;3. 保持 ES 索引映射与实体类一致,避免字段类型不匹配

7.2 性能优化实战(大促验证,效果显著)

7.2.1 应用层优化(核心优化点,立竿见影)
7.2.1.1 本地缓存优化(Caffeine)
  • 优化点:针对品牌、分类等高频查询且变更频率低的数据,使用 Caffeine 本地缓存缓存,避免频繁查询 ES,提升接口响应时间;
  • 优化效果:品牌查询接口响应时间从 80ms 降至 5ms,QPS 支持提升至原来的 20 倍,无缓存穿透风险;
  • 关键配置:设置合理的缓存大小与过期时间(如品牌缓存最大 1000 条,10 分钟过期),使用 @Cacheable 注解简化缓存逻辑,无需手动管理缓存。
7.2.1.2 接口限流优化(Sentinel)
  • 优化点:对核心搜索接口配置限流规则(QPS=1500),避免突发流量(如大促秒杀)压垮应用,同时配置降级策略,返回兜底数据(如热门商品列表);
  • 优化效果:应用在流量峰值(QPS=2000)时仍能稳定运行,接口错误率从 30% 降至 0.1%,用户体验无明显影响;
  • 关键配置:在 Sentinel 控制台配置流控规则(限流模式:QPS,阈值:1500,流控效果:快速失败),配置降级规则(慢调用比例阈值:0.5,最大响应时间:1s,熔断时长:10s)。
7.2.1.3 JVM 参数优化
  • 优化点:针对 16G 内存服务器,配置 G1GC 收集器,优化堆内存大小与 GC 参数,避免 Full GC 频繁发生,提升应用稳定性;
  • 优化参数:-Xms8g -Xmx8g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:ParallelGCThreads=8 -XX:ConcGCThreads=4 -XX:+HeapDumpOnOutOfMemoryError
  • 优化效果:Full GC 频率从每小时 1 次降至每周 1 次,GC 停顿时间从 500ms 降至 200ms 以内,应用无内存溢出导致的宕机问题。
7.2.2 ES 层优化(核心优化点,支撑大促流量)
7.2.2.1 查询语句优化
  • 优化点 1:使用bool查询的filter子句替代must子句(筛选条件不参与评分,可缓存查询结果),提升查询性能;
  • 优化点 2:关闭评分计算(trackScores: false),仅排序与筛选场景无需评分,减少 ES 计算开销;
  • 优化点 3:使用searchAfter深分页替代from+size(避免深分页时 ES 加载大量分片数据,提升分页查询性能);
  • 优化效果:核心搜索接口平均响应时间从 1.2s 降至 300ms,支持深分页(第 1000 页 +)查询无明显延迟。
7.2.2.2 冷热分离优化
  • 优化点:将 90 天内的高频访问数据存储在 ES 热节点(NVMe SSD),90 天后的冷数据迁移至冷节点(机械硬盘),兼顾查询性能与存储成本;
  • 优化效果:ES 集群存储成本降低 70%,热数据查询性能提升 50%,冷数据存储无需占用高价 SSD 资源,实现成本与性能的平衡。
7.2.2.3 索引优化
  • 优化点 1:合理设置分片数(3 分片),与热节点数量匹配,提升查询并行度;
  • 优化点 2:使用索引别名与 ILM 策略,自动化管理索引生命周期,避免人工维护索引的繁琐操作;
  • 优化点 3:关闭索引不必要的功能(如_source字段按需返回、关闭索引刷新间隔index.refresh_interval: 30s(批量写入场景)),提升写入性能;
  • 优化效果:商品批量写入成功率从 95% 提升至 100%,写入速度提升 2 倍,索引维护成本降低 90%。

八、 实战案例复盘(真实大促案例,可复用经验)

8.1 案例一:双 11 大促性能瓶颈突破(真实业务场景)

8.1.1 案例背景
  • 业务场景:母婴电商双 11 大促,商品搜索页面访问量激增,峰值 QPS 达到 2000(日常 QPS 为 500);
  • 问题现象:搜索接口响应时间超过 3 秒,部分用户请求超时,ES 集群 CPU 使用率达到 90% 以上,出现大量慢查询;
  • 影响范围:核心商品搜索功能受影响,用户下单转化率下降 15%,面临用户投诉风险。
8.1.2 问题排查
  • 监控排查:通过 Grafana 监控发现,ES 查询 QPS 峰值达到 3000,平均查询时间超过 2.5s,热节点 CPU 使用率持续 90% 以上;
  • 日志排查:查看 ES 慢查询日志(/usr/local/elasticsearch/logs/maternal-es-cluster_slow_query.log),发现大量深分页查询(from=1000, size=20)与未优化的must查询;
  • 代码排查:检查应用代码,发现前端分页使用from+size方式,未使用searchAfter深分页,且部分查询条件未使用filter子句,导致 ES 计算开销过大。
8.1.3 解决方案
  • 紧急优化(1 小时内生效):
    • 前端紧急迭代:将深分页查询改为searchAfter方式,避免 ES 加载大量分片数据;
    • 接口临时降级:关闭非核心筛选条件(如商品描述检索),仅保留商品名称检索,减少 ES 查询字段;
    • ES 临时扩容:快速新增 1 台 ES 热节点,分摊查询压力,降低 CPU 使用率;
  • 长期优化(大促后落地):
    • 查询语句优化:将所有筛选条件改为filter子句,关闭评分计算,优化查询性能;
    • 缓存优化:增加热门商品缓存,直接返回缓存数据,避免查询 ES;
    • 限流优化:调整 Sentinel 限流阈值至 2500,配置兜底数据,应对突发流量。
8.1.4 优化效果
  • 接口响应时间:从 3 秒以上降至 500ms 以内,满足用户体验要求;
  • ES 集群状态:CPU 使用率从 90% 以上降至 50% 以下,无慢查询产生;
  • 业务指标:用户下单转化率恢复至正常水平,无用户投诉,圆满支撑双 11 大促流量。
8.1.5 经验总结
  • 提前压测:大促前需进行全链路压测,模拟峰值流量,发现性能瓶颈并提前优化;
  • 监控先行:搭建完善的监控体系,快速定位性能问题,避免盲目排查;
  • 降级兜底:核心接口需配置降级策略与兜底数据,确保极端场景下核心功能可用;
  • 深分页优化:ES 深分页场景必须使用searchAfter,避免from+size导致的性能问题。

8.2 案例二:商品数据一致性修复(真实业务场景)

8.2.1 案例背景
  • 业务场景:商品上架系统批量同步商品数据至 ES 时,因网络抖动导致部分商品写入失败,出现「商品已上架,但搜索不到」的问题;
  • 问题现象:约 0.1% 的商品(共计 1000 余件)无法通过搜索接口查询到,用户反馈商品缺失,影响平台信誉;
  • 影响范围:涉及母婴奶粉、纸尿裤等核心品类,用户下单时无法找到对应商品,导致订单流失。
8.2.2 问题排查
  • 日志排查:查看应用批量写入日志,发现部分批次写入失败,报错信息为「ES connection timeout」(网络抖动导致连接超时);
  • 数据比对:通过数据库与 ES 索引数据比对,筛选出数据库中有但 ES 中无的商品列表,确认缺失商品信息;
  • 机制排查:发现批量写入服务未配置重试机制,写入失败后直接返回错误,无自动重试逻辑,导致数据缺失。
8.2.3 解决方案
  • 紧急修复(2 小时内生效):
    • 数据补全:编写批量补全脚本,读取缺失商品列表,重新批量写入 ES,恢复商品数据;
    • 手动验证:随机抽取缺失商品,通过搜索接口验证是否可正常查询,确保数据补全到位;
  • 长期优化(修复后落地):
    • 重试机制:在批量写入服务中添加重试机制(最多重试 3 次,每次间隔 1 秒),应对网络抖动导致的写入失败;
    • 监控告警:配置 ES 批量写入成功率监控,当成功率低于 99.9% 时触发告警,及时发现数据缺失问题;
    • 数据校验:每天凌晨执行数据库与 ES 数据比对任务,自动补全缺失数据,确保数据一致性。
8.2.4 优化效果
  • 数据一致性:商品数据缺失率从 0.1% 降至 0,数据库与 ES 数据完全一致;
  • 告警触达:批量写入失败时,运维人员可在 5 分钟内收到告警,及时处理问题;
  • 业务指标:无用户反馈商品缺失问题,订单流失率恢复至 0,保障平台信誉。
8.2.5 经验总结
  • 重试机制:批量数据同步场景必须配置重试机制,应对网络抖动、服务临时不可用等异常场景;
  • 数据校验:核心数据需定期进行跨存储介质比对,确保数据一致性,避免隐性数据缺失;
  • 告警覆盖:所有核心业务操作(如批量写入、数据同步)需配置监控告警,做到问题早发现、早处理。

九、 核心代码附录与快速上手指南

9.1 核心代码附录

9.1.1 缺失商品补全脚本(Python,快速补全数据)
python 复制代码
# -*- coding: utf-8 -*-
# 母婴商品缺失数据补全脚本
# 作者:博客专家(十余年电商架构实战经验)
# 用途:补全数据库中有但ES中无的商品数据,确保数据一致性
import pymysql
import requests
import json

# 数据库配置(需根据实际环境修改)
DB_CONFIG = {
    "host": "192.168.1.111",
    "port": 3306,
    "user": "root",
    "password": "Root@123456",
    "database": "maternal_db",
    "charset": "utf8"
}

# ES批量写入接口配置(应用接口地址)
ES_BATCH_ADD_URL = "http://192.168.1.106:8080/api/product/batch/add"

# 获取数据库中所有商品列表
def get_db_product_list():
    conn = None
    cursor = None
    try:
        # 建立数据库连接
        conn = pymysql.connect(**DB_CONFIG)
        cursor = conn.cursor(pymysql.cursors.DictCursor)
        # 查询所有上架商品
        sql = "SELECT product_id, product_name, brand, category, price, stock, sales, score, suitable_age, create_time, product_desc FROM maternal_product WHERE status = 1"
        cursor.execute(sql)
        # 获取查询结果
        product_list = cursor.fetchall()
        print(f"✅ 从数据库查询到上架商品共{len(product_list)}件")
        return product_list
    except Exception as e:
        print(f"❌ 查询数据库商品列表异常:{e}")
        return []
    finally:
        # 关闭数据库连接
        if cursor:
            cursor.close()
        if conn:
            conn.close()

# 获取ES中所有商品ID列表
def get_es_product_id_list():
    try:
        # 调用ES商品查询接口(查询所有商品ID,分页获取)
        product_id_list = []
        page_num = 1
        page_size = 1000
        while True:
            params = {
                "pageNum": page_num,
                "pageSize": page_size,
                "sortField": "productId",
                "sortOrder": "asc"
            }
            response = requests.get("http://192.168.1.106:8080/api/product/search", params=params)
            if response.status_code != 200:
                print(f"❌ 查询ES商品列表异常,状态码:{response.status_code}")
                break
            result = response.json()
            if not result.get("data"):
                break
            # 提取商品ID
            for product in result.get("data"):
                product_id_list.append(product.get("productId"))
            page_num += 1
        print(f"✅ 从ES查询到商品共{len(product_id_list)}件")
        return product_id_list
    except Exception as e:
        print(f"❌ 查询ES商品ID列表异常:{e}")
        return []

# 筛选缺失商品列表
def get_missing_product_list(db_product_list, es_product_id_list):
    missing_product_list = []
    es_product_id_set = set(es_product_id_list)
    for product in db_product_list:
        product_id = product.get("product_id")
        if product_id not in es_product_id_set:
            # 转换字段名称与格式,适配ES批量写入接口
            missing_product = {
                "productId": product.get("product_id"),
                "productName": product.get("product_name"),
                "brand": product.get("brand"),
                "category": product.get("category"),
                "price": product.get("price"),
                "stock": product.get("stock"),
                "sales": product.get("sales"),
                "score": product.get("score"),
                "suitableAge": product.get("suitable_age"),
                "createTime": product.get("create_time").strftime("%Y-%m-%d %H:%M:%S"),
                "productDesc": product.get("product_desc"),
                "tags": ["热销", "正品", "包邮"] # 默认标签
            }
            missing_product_list.append(missing_product)
    print(f"✅ 筛选出缺失商品共{len(missing_product_list)}件")
    return missing_product_list

# 批量补全缺失商品数据
def batch_add_missing_product(missing_product_list):
    if not missing_product_list:
        print("📌 无缺失商品,无需补全")
        return
    try:
        # 调用ES批量写入接口
        headers = {"Content-Type": "application/json"}
        response = requests.post(ES_BATCH_ADD_URL, data=json.dumps(missing_product_list), headers=headers)
        if response.status_code == 200:
            result = response.json()
            if result.get("code") == 200:
                print(f"✅ 批量补全{len(missing_product_list)}件缺失商品数据成功")
            else:
                print(f"❌ 批量补全缺失商品数据失败,错误信息:{result.get('msg')}")
        else:
            print(f"❌ 批量补全缺失商品数据异常,状态码:{response.status_code}")
    except Exception as e:
        print(f"❌ 批量补全缺失商品数据异常:{e}")

# 主函数
if __name__ == "__main__":
    print("===== 母婴商品缺失数据补全脚本开始执行 =====")
    # 1. 获取数据库商品列表
    db_product_list = get_db_product_list()
    if not db_product_list:
        print("❌ 数据库商品列表为空,脚本终止执行")
        exit(1)
    # 2. 获取ES商品ID列表
    es_product_id_list = get_es_product_id_list()
    if not es_product_id_list:
        print("❌ ES商品ID列表为空,脚本终止执行")
        exit(1)
    # 3. 筛选缺失商品列表
    missing_product_list = get_missing_product_list(db_product_list, es_product_id_list)
    # 4. 批量补全缺失商品
    batch_add_missing_product(missing_product_list)
    print("===== 母婴商品缺失数据补全脚本执行结束 =====")

9.2 快速上手指南(新手友好,30 分钟搭建可运行系统)

9.2.1 环境准备(10 分钟)
  • 服务器配置:至少 1 台 CentOS 7.9 服务器(4 核 8G,100G ESSD),满足单机部署要求;
  • 软件安装:按本文 2.2 节步骤安装 JDK 8、Docker、Docker Compose,无需安装 ES(容器化部署);
  • 代码下载:将本文所有核心代码复制到本地,按包结构创建 Maven 项目(groupId: com.maternal, artifactId: maternal-product-search)。
9.2.2 项目构建(5 分钟)
  • 配置 pom.xml:将本文 3.1 节完整 pom.xml 复制到项目根目录,刷新 Maven 依赖;
  • 配置 application.yml:将本文 3.2 节配置文件复制到 src/main/resources 目录,修改服务器 IP 为自身服务器 IP;
  • 构建项目:在项目根目录执行mvn clean package -Dmaven.test.skip=true,生成 jar 包(target/maternal-product-search-1.0.0.jar)。
9.2.3 容器部署(10 分钟)
  • 编写 Dockerfile:将本文 5.1 节 Dockerfile 复制到项目根目录;
  • 构建镜像:执行docker build -t maternal-product-search:1.0.0 .
  • 编写 docker-compose.yml:将本文 5.2 节配置文件复制到项目根目录,修改服务器 IP;
  • 启动服务:执行docker-compose up -d,查看容器状态(docker-compose ps),确保容器正常启动。
9.2.4 功能验证(5 分钟)
  • 健康检查:访问http://服务器IP:8080/actuator/health,返回{"status":"UP"}即为应用正常;
  • 品牌查询:访问http://服务器IP:8080/api/product/brands,返回品牌列表即为接口正常;
  • 商品搜索:通过 PostMan 调用http://服务器IP:8080/api/product/search,传入关键字,返回商品列表即为搜索功能正常。
9.2.5 收尾与后续操作(2 分钟,可选)
  • 服务开机自启:修改 docker-compose.yml 中 restart: always 已配置开机自启,无需额外操作;
  • 日志查看:执行 docker-compose logs -f 容器名称,实时查看应用运行日志,排查业务异常;
  • 服务停止:若需停止服务,执行 docker-compose down,彻底停止并删除容器;
  • 后续优化:若需提升性能,可参考本文「性能优化实战」章节,配置本地缓存、限流等功能。

注意事项:单机部署时,ES 集群可改为单节点(修改 elasticsearch.yml 节点角色为[master, data, ingest]),无需配置冷热分离,降低部署复杂度,便于新手快速上手。

结束语:

亲爱的 Java大数据爱好者们,至此,这套基于 Spring Boot+Elasticsearch 的母婴电商商品搜索系统实战全解已全部呈现。从技术选型的底层逻辑、核心代码的精细实现,到容器化部署的落地细节、监控告警的全方位覆盖,再到故障应急的实战方案、性能优化的核心技巧,以及真实大促案例的深度复盘,我们构建了一套完整、可落地、高性能的搜索系统解决方案。

这套方案历经十余年电商架构实战验证,承载过千万级日活流量,支撑过双 11、618 等大型促销活动,既兼顾了新手入门的友好性,也满足了资深架构师在高并发场景下的技术需求。希望每一位读者都能从中汲取实用经验,将这些技术方案灵活运用到自身的项目开发中,在提升系统性能与稳定性的同时,也能沉淀属于自己的技术方法论。

技术之路,永无止境。母婴电商的业务场景在不断迭代,搜索技术也在持续演进,未来我们还可以探索向量检索在商品推荐中的融合、AI 分词在母婴专属词汇中的优化等前沿方向。期待与各位技术同仁在技术之路上并肩前行,共同成长,打造出更多高性能、高可用的核心业务系统。

为了更好地了解大家的技术需求,现发起以下投票,欢迎各位踊跃参与。


🗳️参与投票和联系我:

返回文章

相关推荐
Wang15302 小时前
Java的面向对象
java
!chen2 小时前
Spring Boot Pf4j模块化开发
java·spring boot·spring
趁月色小酌***2 小时前
吃透Java核心:从基础语法到并发编程的实战总结
java·开发语言·python
计算机毕设指导62 小时前
基于Django的本地健康宝微信小程序系统【源码文末联系】
java·后端·python·mysql·微信小程序·小程序·django
Ccuno2 小时前
Java中常用的数据结构实现类概念
java·开发语言·深度学习
weixin_440730502 小时前
Java基础学习day02
java·python·学习
曲莫终2 小时前
增强版JSON对比工具类
java·后端·测试工具·json
BD_Marathon2 小时前
Spring——核心概念
java·后端·spring
幽络源小助理2 小时前
SpringBoot+Vue数字科技风险报告管理系统源码 | Java项目免费下载 – 幽络源
java·vue.js·spring boot