Elasticsearch实战指南:让你的业务搜索飞起来
当你的MySQL数据库查询突然从0.5秒飙升到15秒,当你的产品经理第20次提出"模糊搜索要支持同义词联想"时——是时候重新认识这个改变搜索游戏规则的分布式搜索引擎了。
1.为什么Elasticsearch是新时代的“数据引擎”?
传统数据库的三大死穴
模糊查询性能差(LIKE耗时随数据量指数级上升)缺乏智能排序(无法根据用户行为动态加权)扩展性弱(分库分表成本高)ES的破局武器
分布式架构:线性扩展支撑 PB 级数据倒排索引:毫秒级响应关键词搜索分词引擎:中文/拼音/同义词精准匹配2.五大场景+完整代码:从入门到实战
电商搜索——让用户“一搜即中”痛点用户搜索“苹果手机”时,无法智能匹配“iPhone”搜索结果排序僵化(无法综合销量/评分/价格动态排序)代码实现复制
// 商品实体类
@Document(indexName = "products")
public class Product {
@Id
private String id;
// 使用ik_max_word分词器
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String title;
private Double price;
private Long sales;
// getters/setters
}
// 搜索服务
@Service
public class ProductService {
@Autowired
private ElasticsearchOperations esOperations;
public List<Product> search(String keyword) {
Query query = NativeQuery.builder()
.withQuery(q -> q
.match(m -> m // 多字段匹配
.field("title")
.field("description")
.query(keyword)
)
)
.withSort(s -> s // 综合排序:销量倒序 > 价格升序
.field(f -> f.field("sales").order(SortOrder.Desc))
.field(f -> f.field("price").order(SortOrder.Asc))
)
.build();
return esOperations.search(query, Product.class)
.stream().map(SearchHit::getContent).collect(Collectors.toList());
}
}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.
复制
// 用户画像实体
@Document(indexName = "user_profiles")
public class UserProfile {
@Field(type = FieldType.Keyword)
private String userId;
// 用户兴趣标签(可动态更新)
@Field(type = FieldType.Keyword)
private List<String> tags;
@GeoPointField
private GeoPoint lastLocation;
}
// 附近相似用户推荐
public List<UserProfile> recommendUsers(String userId, int radiusKm) {
UserProfile current = getUserById(userId); // 获取当前用户
Query query = NativeQuery.builder()
.withQuery(q -> q
.bool(b -> b
.must(m -> m.geoDistance(g -> g // 地理过滤
.field("lastLocation")
.distance(radiusKm + "km")
.location(l -> l.latlon(ll ->
ll.lat(current.getLastLocation().lat())
.lon(current.getLastLocation().lon())
))
))
.must(m -> m.terms(t -> t // 标签匹配
.field("tags")
.terms(t2 -> t2.value(current.getTags()))
)
)
)
.build();
return esOperations.search(query, UserProfile.class)
.stream().map(SearchHit::getContent)
.collect(Collectors.toList());
}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.
复制
// 商家实体(含地理位置)
@Document(indexName = "shops")
public class Shop {
@Id
private String id;
@GeoPointField // 关键注解!
private GeoPoint location;
@Field(type = FieldType.Text)
private String name;
}
// 附近商家服务
@Service
public class ShopService {
public List<Shop> findNearby(double lat, double lon, double radiusKm) {
Query query = NativeQuery.builder()
.withQuery(q -> q
.geoDistance(g -> g
.field("location")
.distance(radiusKm + "km")
.location(gl -> gl.latlon(l -> l.lat(lat).lon(lon)))
)
.withSort(s -> s // 按距离排序
.geoDistance(g -> g
.field("location")
.location(l -> l.latlon(ll -> ll.lat(lat).lon(lon))
.order(SortOrder.Asc))
)
.build();
return esOperations.search(query, Shop.class)
.stream().map(SearchHit::getContent)
.collect(Collectors.toList());
}
}
// 接口调用示例
@RestController
@RequestMapping("/shops")
public class ShopController {
@GetMapping("/nearby")
public List<Shop> getNearby(
@RequestParam double lat,
@RequestParam double lon,
@RequestParam(defaultValue = "3") double radius) {
return shopService.findNearby(lat, lon, radius);
}
}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.
3.性能对比:ES如何碾压传统方案
场景
数据量
MySQL耗时
ES耗时
优势倍数
商品搜索
1000万
4.2s
28ms
150x
附近商家查询
50万
920ms
15ms
61x
4.避坑指南:ES不是银弹
事务场景:订单支付等需强一致性时,仍需结合MySQL
冷数据存储:历史归档数据建议转存至OSS
精确统计:UV去重请用HyperLogLog
5.小结
从电商搜索到地理围栏,Elasticsearch 正在重新定义数据处理的边界。当你的业务面临以下挑战时,就是时候考虑 ES 了
数据量超过千万级需要复杂搜索/聚合对实时性要求高阅读剩余
THE END