系统设计速查表:ElasticSearch
弹性搜索速查表:ElasticSearch的系统设计
了解何时以及如何在系统中使用ElasticSearch,附带三个实际系统设计示例
介绍
什么是搜索?为什么搜索很重要?
如果你读过我之前关于搜索的文章,你就会了解搜索对一个应用程序的关键性。想想看:在你每天使用的各种网络应用和移动应用中,如Netflix、亚马逊、Swiggy等,搜索栏可能是唯一共同的UI元素,而且通常位于主页的顶部。如果你正在设计一个系统,99次中有99次你都会考虑如何提供搜索功能。
构建一个搜索系统可不是一件小事,不过ElasticSearch是一个很好的起点。如果你对搜索或推荐系统如何工作一无所知,这篇博文就是你的起点。我们将讨论ElasticSearch是什么、它在哪些方面运作,以及使用ElasticSearch的三个常见设计。一个搜索系统还有很多特性,不过我们在文章末尾再讨论。
什么是ElasticSearch?
ElasticSearch是一个流行的数据库,处理了大部分数据库难以应付的事情:搜索。搜索对于ElasticSearch来说非常核心,它的名字就是搜索笔者。
但是,如果你没有听说过ElasticSearch,你可能会想:为什么搜索这么困难?为什么关系型数据库不能执行搜索?大多数关系型数据库支持各种查找和过滤数据的方式,例如使用WHERE
查询、LIKE
关键字或者索引。为什么文档数据库如MongoDB不能工作?在MongoDB中,你也可以编写find
查询。
为了理解答案,想象一下你正在构建一个新闻网站。当用户使用你的搜索栏搜索新闻,例如“印度新德里的COVID19感染情况”,用户对所有涉及印度新德里的COVID感染文章感兴趣。在一个简单的搜索系统中,这意味着扫描数据库中的所有文章,返回包含“COVID19”、“感染”或“新德里”这些词的文章。但是关系型数据库做不到这一点。关系型数据库只允许你根据特定属性搜索文章,例如特定作者撰写的文章或者今天发布的文章等,但它不能(至少不是高效地)进行扫描数以百万计的新闻文章并返回包含特定词汇的文章。
此外,还有很多需要考虑的复杂因素。你如何给这些文章评分?也许有一篇文章讨论了COVID19感染的传播,还有一篇讨论新感染情况,如何知道哪一篇与用户查询更相关,或者换句话说,如何根据相关性对这些文章进行排序?
答案是:ElasticSearch!ElasticSearch可以做到这一切,而且还能更多。
但是,像世界上的其他事情一样,它也有自己的缺点。让我们讨论一下ElasticSearch是什么,何时使用它,尤其是何时不适用它。
ElasticSearch
搜索能力
ElasticSearch提供了执行“全文搜索”的方法。全文搜索是指在大量文档中搜索特定短语或单词。我们继续使用之前的例子,想象一下你正在构建一个包含数百万新闻文章的新闻网站。每篇文章都包含一些数据,例如标题、副标题、文章内容、发布时间等。在ElasticSearch的上下文中,每篇文章都被存储为一个JSON文档。
你可以将所有这些文档装载到ElasticSearch中,然后在几毫秒内在每篇文档中搜索特定的词语或短语。因此,如果你加载了所有新闻文章,然后进行搜索,例如“印度新德里的COVID19感染情况”,ElasticSearch返回包含“COVID19”、“感染”或“德里”的所有文章。
为了演示ElasticSearch的搜索功能,让我们先设置Elasticsearch并加载一些数据。对于这篇文章,我将使用我在Kaggle找到的这个新闻数据集(Misra, Rishabh. “News Category Dataset.” arXiv preprint arXiv:2209.11429 (2022)) (来源) (许可证)。数据集非常简单,包含大约21万份新闻文章,包括它们的标题、简短描述、作者等一些我们不太关心的字段。我们不需要所有21万份文档,所以我将在ES中加载大约1万份文档并开始搜索。
以下是数据集中的几个文档示例:
[ { "link": "https://www.huffpost.com/entry/new-york-city-board-of-elections-mess_n_60de223ee4b094dd26898361", "headline": "为什么纽约市选举委员会一团糟", "short_description": "“拥有党派选举委员会存在根本问题,”纽约选举律师表示。", "category": "政治", "authors": "Daniel Marans", "country": "IN", "timestamp": 1689878099 }, ....]
每个文档代表一篇新闻文章。每篇文章包含一个link
、一个headline
、一个short_description
、一个category
、authors
、country
(随机值,由我添加)、timestamp
(再次是随机值,由我添加)。
Elasticsearch查询是用JSON编写的。我们先从简单的全文搜索查询multi_match
开始(不用太担心在ElasticSearch中查询数据,它非常简单,我们将在文章结束时讨论它)。这个想法很简单,你编写一个查询,Elasticsearch执行一个全文搜索,实质上扫描数据库中的所有文档,查找其中包含查询中的单词的文档,为它们分配一个分数并返回它们。例如,
GET news/_search{ "query": { "multi_match": { "query": "COVID19 infections" } }}
上述查询会找到与查询“COVID19 infections”相关的文章。这是我得到的结果:
[ { "_index" : "news", "_id" : "czrouIsBC1dvdsZHkGkd", "_score" : 8.842152, "_source" : { "link" : "https://www.huffpost.com/entry/china-shanghai-lockdown-coronavirus_n_62599aa1e4b0723f8018b9c2", "headline" : "中国新冠封城令继续执行,感染人数上升", "short_description" : "访问广州,一个靠近香港的1900万人口工业中心,本周被暂停了。", "category" : "世界新闻", "authors" : "Joe McDonald, AP", "country" : "IN", "timestamp" : 1695106458 } }, { "_index" : "news", "_id" : "ODrouIsBC1dvdsZHlmoc", "_score" : 8.064016, "_source" : { "link" : "https://www.huffpost.com/entry/who-covid-19-pandemic-report_n_6228912fe4b07e948aed68f9", "headline" : "COVID-19病例和死亡继续全球下降,WHO表示", "short_description" : "世界卫生组织表示,上周新增感染人数下降了5%,继续下降趋势", "category" : "世界新闻", "authors" : "", "country" : "US", "timestamp" : 1695263499 } }, ....]
正如你所见,它返回的是讨论COVID19感染的文档。它还按照相关性的顺序返回它们(_score
字段表示特定文档的相关性有多高)。
ElasticSearch拥有丰富的查询语言和许多功能,但就目前而言,只需知道构建一个简单的搜索系统非常容易,只需要将所有数据加载到ElasticSearch中,并使用我们讨论过的简单查询即可。我们有很多选项来改善、配置和调整搜索性能和相关性(关于搜索查询的更多内容将在本文末尾讨论)。
分布式架构
ElasticSearch作为一个分布式数据库运行。这意味着在一个ElasticSearch集群中有多个节点。如果一个节点不可用或故障,通常不会导致系统停机,其他节点通常会接手额外的工作并继续提供用户请求。因此,多个节点提高了系统的可用性。
多个节点也有助于扩展我们的系统,数据和用户请求可以分配到这些节点上,从而减少每个节点的负载。例如,如果您想在ElasticSearch中存储1亿篇新闻文章,您可以将这些数据分成多个节点,每个节点存储一定数量的文章。而且这非常容易做到,在事实上,ElasticSearch具备了内置功能,使这一过程尽可能简单和无缝。
可扩展性
ElasticSearch可以水平扩展,能够将数据分区到多个节点上。这意味着您可以通过向ElasticSearch集群添加更多节点来提高查询性能。
关于设计ElasticSearch集群的架构还有很多需要考虑的问题,不仅仅是运行更多服务器。有不同类型的节点,这些节点运行称为“分片”的进程,每个分片、节点可以有多个类型和配置选项。关于ElasticSearch集群的架构和工作原理,有很多可以讨论的内容,所以如果您想更深入了解,我已经在这里写了一篇完整的文章。
简而言之:您可以增加更多机器来扩展您的集群并提高性能。数据和查询将被分割成多个机器。这有助于提高性能和可扩展性。
基于文档的数据建模
ElasticSearch是一个文档数据库,它以JSON文档格式存储数据,类似于MongoDB。因此,在我们的例子中,每篇新闻文章都以JSON文档的形式存储在集群中。
实时数据分析
实时数据分析是指实时查看用户操作并了解用户模式和行为的过程。我们可以通过绘制用户行为图表并更好地了解我们的用户来改进我们的产品。例如,假设我们测量我们新闻网站上每一次点击、滚动事件和用户阅读时间。我们将这些指标绘制到仪表板上,并观察几天。借助这些,我们可以收集许多有关改进我们新闻应用的可执行见解。我们发现用户通常在早上9点至10点使用该网站,并且我们还发现用户通常点击与其所在国家相关的文章。利用这些信息,我们可以在高峰时间(上午9点至10点)加大资源配置,并且可能在用户的主页上显示其所在国家的文章。
由于其分布式架构和强大的搜索能力,Elasticsearch非常适合实时数据分析。在处理实时数据(如日志、指标或社交媒体更新)时,Elasticsearch能够高效地索引和存储这些信息。它的准实时索引使得数据几乎可以在摄入后立即进行搜索。ElasticSearch还与其他工具良好配合,比如用于可视化的Kibana,以及用于收集指标的Logstash和Beats。
本文将在最后讨论一个促成这一切的架构。
成本
ElasticSearch的运行和维护成本很高。就像世界上的所有事物一样,一切优秀的东西都是要付出代价的。为了进行全文搜索,ElasticSearch将大量数据存储在内存中并构建复杂的索引。这意味着它需要大量的内存才能运行,而这是昂贵的。
所以,简而言之,它在执行全文搜索时能给您带来出色的性能,但并非便宜。
不适合使用ElasticSearch的情况
ACID一致性
和大多数NoSQL数据库一样,ElasticSearch对ACID的支持非常有限,因此,如果您需要强一致性或事务支持,ElasticSearch可能不是您选择的数据库。由此带来的后果是,如果您在ElasticSearch中插入一个文档(称为在ElasticSearch中“索引”文档),它可能不会立即对其他节点可见,可能需要几毫秒的时间才能对其他节点可见。
让我们假设您正在构建一个银行系统;如果用户向其账户存入资金,您希望该数据立即对用户执行的每笔交易都可见。另一方面,如果您正在使用ElasticSearch为您的新闻网站提供搜索功能,当一篇新文章发表时,该文章在最初的几毫秒内可能对所有用户都不可见,这可能是可以接受的。
当您需要复杂的连接操作时
ElasticSearch不支持JOIN操作或不同表之间的关系。如果您一直在使用关系型数据库,这可能会让您有点震惊,因为大多数NoSQL数据库对这些类型的操作支持有限。
如果您想要执行连接操作或使用外键处理高度相关的结构化数据,ElasticSearch可能不是您的最佳选择。
小型数据集或简单查询需要
ElasticSearch是复杂且昂贵的。运行和管理大型ElasticSearch集群不仅需要软件工程师和DevOps工程师的知识和技能,甚至可能需要优秀的ElasticSearch集群管理和架构专家,称为“ElasticSearch架构师”。有大量的配置选项和架构选择可供尝试,每个选项对您的查询和数据摄入都会产生重大影响,从而对系统中核心流程的用户体验产生间接影响。
如果您只需要执行简单查询或拥有相对较少的数据,那么使用简单的数据库可能更适合您的应用程序。
如何在系统设计中使用ElasticSearch
一个软件系统通常需要多个数据库,每个数据库为不同的功能提供支持。让我们通过一个示例来了解使用ElasticSearch的设计选择。
假设您想要构建一个视频流媒体服务,类似于Netflix。让我们看看ElasticSearch在这个示例中的适用性。
作为搜索系统
ElasticSearch的一个非常常见的用途是作为支持全文搜索查询的辅助数据库。这对于我们的视频流媒体应用程序非常有用。我们不能将视频存储在ElasticSearch中,而且我们可能也不希望将与账单或用户相关的数据存储在ElasticSearch中。
为此,我们可以有其他数据库,但我们可以在ElasticSearch中存储电影的标题、描述、流派、评级等等。
我们可以有类似于这样的架构:

我们可以将想要使用全文搜索功能的数据导入ElasticSearch中。当用户执行搜索操作时,我们可以查询ElasticSearch集群。这样,我们就可以获得ElasticSearch的全文搜索能力,当我们想要更新用户信息时,我们可以在主要存储中执行这些更新。
作为实时数据分析管道
正如我们之前讨论的那样,了解用户行为和模式是决定产品如何发展的重要步骤。我们可以发布事件,例如点击事件和滚动事件,以更好地了解用户如何使用我们的产品。
例如,在我们的视频流媒体应用程序中,当用户点击某部电影或节目时,我们可以发布一个带有用户和电影数据的事件。然后,我们可以进行分析和聚合,来更好地了解用户如何使用我们的产品。例如,我们可能会注意到用户在晚上使用我们的产品比下午多,或者用户可能更喜欢以本地语言而不是其他语言的节目或电影。通过这样的分析,我们可以改进产品,提升用户体验。
使用ElasticSearch和与其兼容的仪表盘工具Kibana,一个基本的实时数据分析系统将如下所示:

作为推荐系统
我们可以在ElasticSearch中构建查询,使某些属性具有更高的优先级(称为boosting)。例如,我们可以不使用简单的查询
我们可以通过ElasticSearch构建基本的推荐系统。我们可以存储关于用户的信息,如用户的国家、年龄、喜好等,并生成查询以获取适合该用户的热门电影或系列。
了解查询语言、如何提升特定字段以及执行聚合是一个庞大的主题,但我在这里撰写了一篇讲解基础知识的博文:
精通Elasticsearch:强大搜索和精确度的初学者指南 — 第1部分
解锁Elasticsearch的力量-第1部分:深入Elasticsearch,掌握基本搜索查询并探索词汇
towardsdatascience.com
总结
如何设计ElasticSearch集群?
设计ElasticSearch集群并不容易,它需要对节点、分片、索引以及如何协调它们有所了解。在此方面有无数的架构选择,并且该领域在不断发展(特别是随着人工智能和人工智能驱动搜索的普及)。想要更深入地讨论,我写了一篇完整的博文,从基础知识开始,包含了你需要了解的有关设计搜索集群的一切内容:
系统设计系列:ElasticSearch,用于搜索的架构设计
理解Elasticsearch架构和全文搜索
betterprogramming.pub
理解搜索查询和改善搜索系统
搜索是复杂的,非常复杂。我们可以通过多种方式改善搜索系统,使其更强大并且能够理解用户需求。你已经了解了ElasticSearch以及它是什么。继续这个学习之旅,我们将从这里开始,构建一个基本的搜索查询,理解查询和系统中的问题,并通过示例逐步发展和改善系统。
精通Elasticsearch:强大搜索和精确度的初学者指南 — 第1部分
解锁Elasticsearch的力量-第1部分:深入Elasticsearch,掌握基本搜索查询并探索词汇
towardsdatascience.com
上下文感知搜索
最近我读到了一个很好的关于搜索系统的类比。您可以将迄今为止我们讨论的搜索系统理解为一种机械的、僵化的搜索。当用户输入一个词时,我们会找到所有包含该词的文档并返回它们。
或者您也可以将搜索系统视为一位图书管理员。当用户提出一个问题,比如,“温斯顿·丘吉尔在第二次世界大战中扮演的角色是什么?”,图书管理员不仅告诉他哪些书中包含有“温斯顿”、“丘吉尔”或“第二次世界大战”这些词。相反,图书管理员评估并理解客户和上下文。也许这是个学生,所以她不会推荐一本庞大的教科书,而是找一本更适合年轻孩子的书。或者也许她没有任何一本书的标题是关于温斯顿·丘吉尔的,所以她找到一本讲述第二次世界大战或英国首相的书,然后推荐那本书。图书管理员甚至可能为考试推荐不同的书,为暑假作业推荐不同的书(也许有些人不知道,但在一些国家,暑假会有大量的作业)
对于您和我来说,这很容易理解,但我们的系统如何知道温斯顿·丘吉尔是英国首相并推荐关于第二次世界大战期间的英国书籍,或者我们的系统如何理解讨论的上下文,理解用户并推荐合适的书籍?
尽管这似乎很困难,但实际上并不难。这被称为语义搜索,也是大多数大型技术公司构建其搜索系统的一种搜索技术。语义搜索是一组搜索技术,旨在理解用户查询背后的意思和内容的上下文,通过考虑词之间的关系和搜索背后的意图,提供更准确和与上下文相关的搜索结果。
这是一个广泛的话题,我仍在阅读和了解更多相关内容,但将很快发布一篇从基础开始的博文,所以如果你想了解更多关于这个话题的内容,请在VoAGI上关注我。
其他数据库
我写过一些关于系统设计概念的内容,比如数据库、队列和发布-订阅系统,所以请在VoAGI上关注我,了解更多相关文章。我在LinkedIn上也写了很多简洁的内容(例如,这篇文章介绍了RabbitMQ和Kafka之间的区别),所以请在LinkedIn上关注我,获取更简短的内容。
同时,你可以查看我的博文,其中包括其他数据库和系统设计概念:
Sanil Khurana在VoAGI上精选了一些列表
开始探索Linux、Cassandra、面试问题等
VoAGI.com