MySQL模糊查询3秒太慢,换ES后50毫秒搞定

商品搜索功能,用MySQL的LIKE查询,数据量到百万级直接3秒起步。

用户等不了啊。换了Elasticsearch,50毫秒搞定。分享一下怎么搭建和优化的。


一、ES是什么?

Elasticsearch是分布式搜索引擎,特点:

  • 全文搜索:中文分词、模糊匹配
  • 近实时:数据写入后秒级可搜
  • 分布式:天然支持集群
  • RESTful API:HTTP接口,使用简单

常见用途:

  • 站内搜索
  • 日志分析(ELK)
  • 数据分析

二、核心概念

ES概念 类比MySQL
Index Database
Type(7.x已废弃) Table
Document Row
Field Column
Mapping Schema

三、环境搭建

3.1 Docker方式(推荐)

# docker-compose.yml
version: '3'
services:
  elasticsearch:
    image: elasticsearch:8.11.0
    container_name: elasticsearch
    environment:
      - discovery.type=single-node
      - ES_JAVA_OPTS=-Xms512m -Xmx512m
      - xpack.security.enabled=false
    ports:
      - "9200:9200"
      - "9300:9300"
    volumes:
      - es_data:/usr/share/elasticsearch/data

  kibana:
    image: kibana:8.11.0
    container_name: kibana
    ports:
      - "5601:5601"
    environment:
      - ELASTICSEARCH_HOSTS=http://elasticsearch:9200
    depends_on:
      - elasticsearch

volumes:
  es_data:
docker-compose up -d

访问:

3.2 验证安装

curl http://localhost:9200
{
  "name" : "xxx",
  "cluster_name" : "docker-cluster",
  "version" : {
    "number" : "8.11.0"
  }
}

四、基本操作

4.1 创建索引

PUT /products
{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 0
  },
  "mappings": {
    "properties": {
      "name": { "type": "text", "analyzer": "ik_max_word" },
      "description": { "type": "text", "analyzer": "ik_max_word" },
      "price": { "type": "double" },
      "category": { "type": "keyword" },
      "createTime": { "type": "date" }
    }
  }
}

4.2 插入文档

POST /products/_doc/1
{
  "name": "iPhone 15 Pro",
  "description": "苹果最新旗舰手机,A17 Pro芯片",
  "price": 8999,
  "category": "手机",
  "createTime": "2024-01-01"
}

4.3 查询文档

# 查询单个
GET /products/_doc/1

# 查询全部
GET /products/_search
{
  "query": {
    "match_all": {}
  }
}

# 关键词搜索
GET /products/_search
{
  "query": {
    "match": {
      "name": "苹果手机"
    }
  }
}

4.4 更新文档

POST /products/_update/1
{
  "doc": {
    "price": 7999
  }
}

4.5 删除文档

DELETE /products/_doc/1

五、中文分词

ES默认分词器不支持中文,需要安装IK分词器。

5.1 安装IK分词器

# 进入容器
docker exec -it elasticsearch bash

# 安装插件
./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v8.11.0/elasticsearch-analysis-ik-8.11.0.zip

# 重启
docker restart elasticsearch

5.2 测试分词

POST /_analyze
{
  "analyzer": "ik_max_word",
  "text": "苹果最新旗舰手机"
}

结果:

{
  "tokens": [
    { "token": "苹果" },
    { "token": "最新" },
    { "token": "旗舰" },
    { "token": "手机" }
  ]
}

5.3 分词器选择

  • ik_max_word:最细粒度分词,搜索时用
  • ik_smart:粗粒度分词,索引时用

六、常用查询

6.1 精确查询(term)

GET /products/_search
{
  "query": {
    "term": {
      "category": "手机"
    }
  }
}

6.2 全文搜索(match)

GET /products/_search
{
  "query": {
    "match": {
      "description": "苹果 手机"
    }
  }
}

6.3 多字段搜索(multi_match)

GET /products/_search
{
  "query": {
    "multi_match": {
      "query": "苹果",
      "fields": ["name", "description"]
    }
  }
}

6.4 范围查询(range)

GET /products/_search
{
  "query": {
    "range": {
      "price": {
        "gte": 5000,
        "lte": 10000
      }
    }
  }
}

6.5 组合查询(bool)

GET /products/_search
{
  "query": {
    "bool": {
      "must": [
        { "match": { "description": "手机" } }
      ],
      "filter": [
        { "term": { "category": "手机" } },
        { "range": { "price": { "lte": 10000 } } }
      ]
    }
  }
}

6.6 高亮显示

GET /products/_search
{
  "query": {
    "match": { "description": "苹果" }
  },
  "highlight": {
    "fields": {
      "description": {
        "pre_tags": ["<em>"],
        "post_tags": ["</em>"]
      }
    }
  }
}

七、Spring Boot集成

7.1 依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>

7.2 配置

spring:
  elasticsearch:
    uris: http://localhost:9200

7.3 实体类

@Document(indexName = "products")
public class Product {
    
    @Id
    private String id;
    
    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String name;
    
    @Field(type = FieldType.Text, analyzer = "ik_max_word")
    private String description;
    
    @Field(type = FieldType.Double)
    private Double price;
    
    @Field(type = FieldType.Keyword)
    private String category;
    
    @Field(type = FieldType.Date)
    private LocalDateTime createTime;
}

7.4 Repository

public interface ProductRepository extends ElasticsearchRepository<Product, String> {
    
    List<Product> findByName(String name);
    
    List<Product> findByPriceBetween(Double min, Double max);
    
    @Query("{\"match\": {\"description\": \"?0\"}}")
    List<Product> searchByDescription(String keyword);
}

7.5 使用

@Service
public class ProductService {
    
    @Autowired
    private ProductRepository productRepository;
    
    @Autowired
    private ElasticsearchOperations elasticsearchOperations;
    
    // 简单搜索
    public List<Product> search(String keyword) {
        return productRepository.searchByDescription(keyword);
    }
    
    // 复杂搜索
    public SearchHits<Product> complexSearch(String keyword, Double minPrice, Double maxPrice) {
        NativeQuery query = NativeQuery.builder()
            .withQuery(q -> q
                .bool(b -> b
                    .must(m -> m.match(t -> t.field("description").query(keyword)))
                    .filter(f -> f.range(r -> r.field("price").gte(JsonData.of(minPrice)).lte(JsonData.of(maxPrice))))
                )
            )
            .withHighlightFields(
                new HighlightFieldParameters.HighlightFieldParametersBuilder()
                    .withField("description")
                    .build()
            )
            .build();
            
        return elasticsearchOperations.search(query, Product.class);
    }
}

八、性能优化

8.1 索引设置

{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1,
    "refresh_interval": "30s"
  }
}

8.2 批量操作

POST /_bulk
{"index":{"_index":"products","_id":"1"}}
{"name":"iPhone","price":8999}
{"index":{"_index":"products","_id":"2"}}
{"name":"Samsung","price":7999}

8.3 只返回需要的字段

GET /products/_search
{
  "_source": ["name", "price"],
  "query": { "match_all": {} }
}

九、远程ES管理

ES集群一般部署在服务器上,怎么本地管理?

我用星空组网工具把本地和服务器连起来,Kibana直接用内网地址访问:

http://192.168.188.10:5601

本地开发时,Spring Boot也能直接连内网ES:

spring:
  elasticsearch:
    uris: http://192.168.188.10:9200

不用每次都配置端口转发或VPN,调试方便很多。


十、总结

ES入门步骤:

  1. 搭建环境(Docker推荐)
  2. 安装IK分词器
  3. 创建索引和Mapping
  4. CRUD操作
  5. 各种查询语法
  6. Spring Boot集成

常用查询:

  • term:精确匹配
  • match:全文搜索
  • bool:组合查询
  • range:范围查询

ES功能很强大,这篇只是入门。更多高级用法(聚合分析、集群运维等)后续再分享。

有问题评论区交流~


posted @ 2025-12-08 11:41  花宝宝  阅读(7)  评论(0)    收藏  举报