第9章:搜索架构¶
9.1 搜索架构概述¶
搜索架构的作用¶
搜索架构用于提供高效的全文搜索功能,支持复杂的查询条件,快速检索大量数据。
搜索架构的特点¶
- 全文检索:支持全文搜索
- 高性能:快速检索大量数据
- 灵活查询:支持复杂的查询条件
- 可扩展:支持水平扩展
9.2 Elasticsearch¶
9.2.1 Elasticsearch概述¶
Elasticsearch是一个基于Lucene的分布式搜索引擎,提供全文搜索、分析、存储等功能。
特点: - 分布式架构 - 实时搜索 - 高性能 - 易于扩展
9.2.2 基础概念¶
索引(Index):类似于数据库的数据库
类型(Type):类似于数据库的表(ES 7.x已废弃)
文档(Document):类似于数据库的行
字段(Field):类似于数据库的列
分片(Shard):索引的水平拆分
副本(Replica):分片的复制
9.2.3 索引设计¶
Python
from elasticsearch import Elasticsearch
# 连接Elasticsearch实例(默认端口9200)
es = Elasticsearch(['http://localhost:9200'])
# 创建索引——定义字段映射(mappings)和索引设置(settings)
index_mapping = {
"mappings": {
"properties": {
"title": {
"type": "text", # text类型:支持全文检索,会被分词器处理
"analyzer": "ik_max_word" # ik_max_word:最细粒度分词,召回率更高
},
"content": {
"type": "text",
"analyzer": "ik_max_word"
},
"author": {
"type": "keyword" # keyword类型:精确匹配,不会被分词
},
"publish_date": {
"type": "date" # date类型:支持日期范围查询和排序
},
"views": {
"type": "integer" # integer类型:支持数值范围查询和聚合
}
}
},
"settings": {
"number_of_shards": 3, # 主分片数:决定数据的水平拆分数量,创建后不可更改
"number_of_replicas": 1 # 副本数:每个主分片的副本数量,提高可用性和读性能
}
}
# 调用API创建名为'articles'的索引
es.indices.create(index='articles', mappings=index_mapping['mappings'], settings=index_mapping['settings'])
9.2.4 数据索引¶
Python
# 索引单个文档——将一条数据写入指定索引
article = {
"title": "Elasticsearch入门教程",
"content": "Elasticsearch是一个基于Lucene的分布式搜索引擎",
"author": "张三",
"publish_date": "2024-01-01",
"views": 1000
}
# 指定索引名、文档ID和文档内容,写入单条数据
es.index(index='articles', id=1, document=article)
# 批量索引——使用bulk API一次性写入多条数据,比逐条写入性能更高
articles = [
{
"title": "Python编程入门",
"content": "Python是一种简单易学的编程语言",
"author": "李四",
"publish_date": "2024-01-02",
"views": 2000
},
{
"title": "机器学习基础",
"content": "机器学习是人工智能的一个分支",
"author": "王五",
"publish_date": "2024-01-03",
"views": 3000
}
]
from elasticsearch.helpers import bulk
# 构造批量操作列表:每个action包含目标索引、文档ID和文档内容
actions = [
{
"_index": "articles", # 目标索引名
"_id": i + 2, # 文档ID(从2开始,避免与上面的id=1冲突)
"_source": article # 文档内容
}
for i, article in enumerate(articles) # enumerate同时获取索引和值
]
# 执行批量写入,内部会自动分批发送请求
bulk(es, actions)
9.2.5 查询优化¶
基本查询¶
Python
# 匹配查询(match):对查询词进行分词后匹配,是最常用的全文检索查询
query = {
"match": {
"title": "Elasticsearch" # 在title字段中搜索,会先对查询词分词再匹配
}
}
result = es.search(index='articles', query=query)
# 多字段查询(multi_match):同时在多个字段中搜索,取最高评分
query = {
"multi_match": {
"query": "搜索引擎", # 查询关键词
"fields": ["title", "content"] # 同时搜索title和content两个字段
}
}
result = es.search(index='articles', query=query)
布尔查询¶
Python
# 布尔查询(bool):通过组合多个子查询实现复杂的搜索逻辑
query = {
"bool": {
"must": [ # must:所有条件必须满足(类似AND)
{"match": {"title": "Elasticsearch"}},
{"match": {"content": "搜索引擎"}}
],
"should": [ # should:满足则加分,不满足也可以(类似OR加权)
{"match": {"author": "张三"}}
],
"must_not": [ # must_not:必须不满足(类似NOT),不影响评分
{"match": {"content": "Python"}}
]
}
}
result = es.search(index='articles', query=query)
范围查询¶
Python
# 范围查询(range):根据数值或日期范围筛选文档
query = {
"range": {
"publish_date": {
"gte": "2024-01-01", # gte = greater than or equal,大于等于
"lte": "2024-01-31" # lte = less than or equal,小于等于
}
}
}
# 也支持gt(大于)、lt(小于)等操作符
result = es.search(index='articles', query=query)
聚合查询¶
Python
# 聚合查询(aggs):类似SQL中的GROUP BY,用于数据统计和分析
query = {
"match_all": {} # 匹配所有文档(不做过滤)
}
aggs = {
"author_stats": { # 自定义聚合名称
"terms": { # terms聚合:按字段值分桶(类似GROUP BY)
"field": "author" # 按author字段分组
},
"aggs": { # 嵌套聚合:在每个分桶内再做统计
"avg_views": { # 计算每个作者的平均浏览量
"avg": {
"field": "views"
}
}
}
}
}
# 执行查询,结果中aggregations字段包含聚合统计数据
result = es.search(index='articles', query=query, aggs=aggs)
9.2.6 性能优化¶
索引优化¶
Python
# 索引优化:选择合适的分析器以平衡搜索精度和性能
# ik_smart:粗粒度分词,速度更快,适合对性能要求高的场景
# ik_max_word:细粒度分词,召回率更高,但索引体积更大
index_mapping = {
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "ik_smart" # ik_smart比ik_max_word更快,分词粒度更粗
}
}
}
}
查询优化¶
Python
# 性能优化:对不需要相关性评分的条件使用filter而非must
# filter不计算_score评分,且结果会被缓存,查询速度显著提升
query = {
"bool": {
"filter": [ # filter上下文:只做过滤,不影响评分
{"term": {"author": "张三"}}, # term精确匹配(keyword字段推荐用term)
{"range": {"publish_date": {"gte": "2024-01-01"}}} # 范围过滤
]
}
}
result = es.search(index='articles', query=query)
分页优化¶
Python
# 深分页优化:from/size在深分页时性能急剧下降(需要跳过大量文档)
# search_after通过上一页最后一条记录的排序值定位下一页,性能恒定
result = es.search(
index='articles',
query={"match_all": {}},
size=10, # 每页返回10条
sort=[
{"publish_date": {"order": "desc"}}, # 主排序:按发布日期倒序
{"_id": {"order": "asc"}} # 次排序:确保排序唯一性(避免同值文档顺序不确定)
]
)
# 提取最后一条文档的排序值,作为下一页的游标
last_sort = result['hits']['hits'][-1]['sort'] # 负索引:从末尾倒数访问元素
# 下一页:传入search_after参数,从上一页结尾处继续
result = es.search(
index='articles',
query={"match_all": {}},
size=10,
sort=[
{"publish_date": {"order": "desc"}},
{"_id": {"order": "asc"}}
],
search_after=last_sort # 游标分页:基于排序值定位,无深分页性能问题
)
9.3 搜索架构设计¶
9.3.1 单节点架构¶
适用场景: - 数据量小 - 查询量小 - 测试环境
9.3.2 集群架构¶
适用场景: - 数据量大 - 查询量大 - 生产环境
9.3.3 读写分离架构¶
适用场景: - 读多写少 - 需要高可用
9.3.4 多集群架构¶
适用场景: - 大规模数据 - 高并发查询 - 需要灾备
9.4 搜索优化¶
9.4.1 索引优化¶
- 合理设置分片数
- 使用合适的分析器
- 优化字段映射
- 使用索引别名
9.4.2 查询优化¶
- 使用filter代替query
- 避免深分页
- 使用search_after
- 合理使用缓存
9.4.3 集群优化¶
- 合理分配分片
- 增加副本数
- 使用专用节点
- 监控集群状态
9.5 实战练习¶
练习1:设计一个文章搜索系统¶
设计一个文章搜索系统: 1. 设计索引结构 2. 实现数据索引 3. 实现搜索功能 4. 优化搜索性能
练习2:实现一个电商商品搜索¶
实现一个电商商品搜索: 1. 设计商品索引 2. 实现多条件搜索 3. 实现聚合统计 4. 优化搜索性能
练习3:设计一个搜索架构¶
设计一个大规模搜索架构: 1. 确定集群规模 2. 设计分片策略 3. 设计读写分离 4. 设计灾备方案
9.6 面试准备¶
常见面试题¶
- 什么是Elasticsearch?它有什么特点?
- 如何设计Elasticsearch索引?
- 如何优化Elasticsearch查询?
- Elasticsearch的分片和副本是什么?
- 如何设计一个搜索架构?
项目经验准备¶
准备一个搜索项目: - 使用的搜索技术 - 遇到的挑战 - 解决方案 - 项目成果
9.7 总结¶
本章介绍了搜索架构,包括Elasticsearch的基础使用、索引设计、查询优化和架构设计。搜索是现代应用的重要功能。
关键要点¶
- Elasticsearch是一个分布式搜索引擎
- 索引设计需要考虑字段类型和分析器
- 查询优化包括使用filter、避免深分页、使用search_after
- 搜索架构包括单节点、集群、读写分离、多集群
- 搜索优化需要综合考虑索引、查询、集群
下一步¶
下一章将深入学习大数据处理架构,包括批处理、流处理等内容。