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
访问:
- ES:http://localhost:9200
- Kibana:http://localhost:5601
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入门步骤:
- 搭建环境(Docker推荐)
- 安装IK分词器
- 创建索引和Mapping
- CRUD操作
- 各种查询语法
- Spring Boot集成
常用查询:
term:精确匹配match:全文搜索bool:组合查询range:范围查询
ES功能很强大,这篇只是入门。更多高级用法(聚合分析、集群运维等)后续再分享。
有问题评论区交流~

浙公网安备 33010602011771号