当前位置: 首页 > news >正文

从卡顿到秒查:Java 项目引入 Elasticsearch 实现亿级地址数据的复杂查询实战

在当今的 Java 应用中,地址查询功能几乎无处不在 —— 电商平台的收货地址检索、外卖 App 的定位服务、物流系统的路径规划,都离不开高效的地址处理能力。然而,当面对 "上海市浦东新区张江高科技园区博云路 2 号附近 3 公里内的咖啡馆" 这类复杂查询时,传统关系型数据库往往力不从心,查询延迟甚至能达到秒级,严重影响用户体验。

本文将带你从零开始,一步步在 Java 项目中集成 Elasticsearch (以下简称 ES),构建一个能处理亿级地址数据、支持复杂空间查询的高性能系统。我们不仅会讲解技术实现,更会深入底层原理,让你明白 "为什么这么做",真正做到知其然更知其所以然。

一、为什么传统数据库搞不定复杂地址查询?

在引入 ES 之前,我们先来搞清楚一个核心问题:为什么 MySQL 这类传统数据库在复杂地址查询场景下表现拉胯?

1.1 地址数据的特殊性

地址数据不同于普通业务数据,它具有以下特点:

  • 层级性:国家→省→市→区→街道→门牌号,形成天然的层级结构
  • 模糊性:"张江科技园" 和 "张江高科技园区" 指的可能是同一区域
  • 空间性:地址本质上是地球表面的一个点,具有经纬度坐标
  • 多样性:包含拼音、简称、别名等多种表达方式

这些特性使得地址查询需求异常复杂,远超简单的 CRUD 操作。

1.2 传统数据库的局限性

流程图展示传统数据库处理地址查询的困境:

具体来说,传统数据库存在以下瓶颈:

  1. 模糊查询效率低下:使用LIKE '%关键词%'会导致全表扫描,无法利用索引
  2. 多条件组合查询复杂:地址的多维度查询需要大量 JOIN 操作,性能随数据量增长急剧下降
  3. 缺乏空间索引支持:难以高效实现 "附近 X 公里" 这类空间查询
  4. 无法处理语义相似性:无法识别 "张江科技园" 和 "张江高科技园区" 的关联

1.3 Elasticsearch 的优势

相比之下,ES 在地址查询场景中展现出显著优势:

  • 倒排索引:专为全文检索设计,模糊查询性能远超传统数据库
  • 丰富的字段类型:支持 geo_point 等空间类型,原生支持地理位置计算
  • 复杂查询 DSL:灵活组合多种查询条件,轻松实现多维度过滤
  • 分布式架构:天然支持水平扩展,轻松应对亿级数据量
  • 聚合分析:强大的聚合功能支持地址数据的统计分析需求

通过引入 ES,我们可以将复杂地址查询的响应时间从秒级降至毫秒级,同时支持更丰富的查询场景。

二、项目环境搭建与依赖配置

工欲善其事,必先利其器。我们先搭建基础项目环境,选择当前最新的稳定版本组件。

2.1 技术栈选型

组件 版本 说明
JDK 17 长期支持版本,性能优异
Spring Boot 3.2.0 简化 Java 开发的框架
Elasticsearch 8.11.0 搜索引擎核心
Spring Data Elasticsearch 5.2.0 Spring 生态的 ES 集成方案
MyBatis-Plus 3.5.5 增强版 MyBatis,简化 CRUD
MySQL 8.0.35 存储基础业务数据
Lombok 1.18.30 简化 Java 代码
Fastjson2 2.0.32 JSON 处理工具
Swagger3 2.2.0 API 文档生成工具

2.2 Maven 依赖配置

创建一个 Spring Boot 项目,在pom.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><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.0</version><relativePath/></parent><groupId>com.address</groupId><artifactId>address-search</artifactId><version>0.0.1-SNAPSHOT</version><name>address-search</name><description>Address Search with Elasticsearch</description><properties><java.version>17</java.version><elasticsearch.version>8.11.0</elasticsearch.version></properties><dependencies><!-- Spring Boot核心 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Elasticsearch --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency><!-- MyBatis-Plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.5</version></dependency><!-- MySQL驱动 --><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version><scope>provided</scope></dependency><!-- Fastjson2 --><dependency><groupId>com.alibaba.fastjson2</groupId><artifactId>fastjson2</artifactId><version>2.0.32</version></dependency><!-- Swagger3 --><dependency><groupId>org.springdoc</groupId><artifactId>springdoc-openapi-starter-webmvc-ui</artifactId><version>2.2.0</version></dependency><!-- 谷歌集合工具 --><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>32.1.3-jre</version></dependency><!-- 测试 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><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>

2.3 配置文件

创建application.yml配置文件,配置各组件连接信息:

spring:application:name: address-search# 数据库配置datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/address_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghaiusername: rootpassword: root# Elasticsearch配置elasticsearch:uris: http://localhost:9200username: elasticpassword: elastic# MyBatis-Plus配置
mybatis-plus:mapper-locations: classpath*:mapper/**/*.xmltype-aliases-package: com.address.search.entityconfiguration:map-underscore-to-camel-case: truelog-impl: org.apache.ibatis.logging.stdout.StdOutImpl# 日志配置
logging:level:com.address.search: debugorg.elasticsearch.client: warn# 服务器配置
server:port: 8080# Swagger配置
springdoc:api-docs:path: /api-docsswagger-ui:path: /swagger-ui.htmloperationsSorter: method

三、地址数据模型设计

设计合理的数据模型是实现高效地址查询的基础。我们需要同时考虑 MySQL 中的存储模型和 ES 中的索引模型。

3.1 MySQL 表设计

首先设计 MySQL 中的地址表,存储基础地址数据:

-- 地址表
CREATE TABLE `address` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',`country` varchar(50) NOT NULL COMMENT '国家',`province` varchar(50) NOT NULL COMMENT '省份',`city` varchar(50) NOT NULL COMMENT '城市',`district` varchar(50) DEFAULT NULL COMMENT '区/县',`town` varchar(50) DEFAULT NULL COMMENT '乡镇/街道',`detail` varchar(200) NOT NULL COMMENT '详细地址',`zip_code` varchar(20) DEFAULT NULL COMMENT '邮政编码',`longitude` decimal(10,6) NOT NULL COMMENT '经度',`latitude` decimal(10,6) NOT NULL COMMENT '纬度',`name` varchar(100) DEFAULT NULL COMMENT '地址名称(如大厦名、小区名)',`pinyin` varchar(200) GENERATED ALWAYS AS (concat(pinyin(country),'',pinyin(province),'',pinyin(city),'',if(district is null,'',pinyin(district)),'',if(town is null,'',pinyin(town)),'',pinyin(detail))) STORED COMMENT '地址拼音',`created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',`updated_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',`is_deleted` tinyint NOT NULL DEFAULT '0' COMMENT '是否删除(0-未删除,1-已删除)',PRIMARY KEY (`id`),KEY `idx_region` (`country`,`province`,`city`,`district`) COMMENT '地区索引'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='地址信息表';-- 地址别名表
CREATE TABLE `address_alias` (`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键ID',`address_id` bigint NOT NULL COMMENT '地址ID',`alias` varchar(100) NOT NULL COMMENT '别名',`alias_pinyin` varchar(200) GENERATED ALWAYS AS (pinyin(alias)) STORED COMMENT '别名拼音',`created_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',PRIMARY KEY (`id`),KEY `idx_address_id` (`address_id`) COMMENT '地址ID索引',KEY `idx_alias` (`alias`) COMMENT '别名索引'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='地址别名表';

注意:pinyin()函数需要 MySQL 安装 pinyin 插件,如mysql-pinyin,用于生成地址的拼音,方便后续拼音检索。

3.2 Elasticsearch 索引设计

ES 的索引设计直接影响查询性能和功能支持,需要精心设计。我们将创建一个address索引,包含以下字段:

package com.address.search.entity.es;import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.address.search.entity.Address;
import com.address.search.entity.AddressAlias;
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 org.springframework.data.elasticsearch.annotations.GeoPointField;
import org.springframework.data.elasticsearch.core.geo.GeoPoint;import java.util.List;
import java.util.stream.Collectors;/*** 地址ES文档实体** @author ken*/
@Data
@Document(indexName = "address", createIndex = false)
public 
http://www.dtcms.com/a/573225.html

相关文章:

  • 国外可以做推广的网站有哪些广州品牌形象设计
  • 【MySQL】SQL语法详细总结
  • 宿迁华夏建设集团网站下列什么软件不能用于设计网页
  • vue笔记(第一天)
  • cursor和传统idea的区别是什么?
  • SSM--MyBatis框架SQL映射文件获取一对一或者一对多关系值
  • 网站开发实验报告总结阿里云是不是做网站的
  • 1000元级投影选哪款?大眼橙C3D实测体验闭眼入
  • 第一章 函数与极限 2.数列的极限
  • 用ps网站首页怎么做做a免费网站有哪些
  • 多因子量化模型预警:美元强势因子压制金价失守4000关口,ADP数据能否重构黄金趋势?
  • Mac 安装 Xcode 及qt 环境安装
  • AS2协议的实时性:重构制造业供应链协同效率
  • 城乡建设部官网查证助孕网站优化推广
  • leetcode17.电话号码的数字组合(题目详解)
  • C# WebAPI Swagger如何显示接口注释
  • 三网站建设装修公司做网站有用吗
  • 怎么做谷歌收录的网站个人接广告的平台
  • Unity坐标转换指南 - 3D与屏幕UI坐标互转
  • Admin界面优化:移除可用区字段以简化显示
  • Java 位运算算法题目练习
  • 企业定制手机安全加固:数据加密、权限管控与防泄密功能解析
  • opencv 计算面积、周长
  • 静态网站设计方案二维码设计软件
  • 工装设计案例网站注册公司注册企业注册
  • 从 .wat 到 AOT:WebAssembly 开发入门全指南(WABT + WasmEdge 实战)
  • Vue2-父组件与子组件参数互传
  • 前端学习之JavaScript
  • CrewCut 项目 Alpha 阶段计划与分工
  • 湖南首条免费高速轨迹呈现:借助 Leaflet -Trackplayer 实现 WebGIS 可视化