Elasticsearch(简称 es)是一款高性能的实时分布式搜索和分析引擎,它可以从海量数据中快速的找到相关信息。作为一款功能强大的分布式搜索引擎,支持近实时的存储、搜索数据。被许多公司广泛的应用
Elasticsearch 有几个核心概念,这里先讲解几个概念,对下面的学习过程比较有帮助
接近实时
Elasticsearch 是一个近实时的搜索引擎,从插入一条数据到数据能被检索到大概有一个 1s 的轻微延迟。所以 Elasticsearch 不适合用于对实时性要求很高的业务
分布式
对于大多数数据库而言,如果想很想扩展,需要对你的程序做相应的改动,大部分时候改动还不小。而Elasticsearch 天生就是分布式的,这意味着它能自动管理节点来提供高扩展和高可用,你不必关心这些,只需要部署多一台服务器即可
索引(index)
一个索引就是一个拥有相似特征的文档的集合,比如在一个订单系统中,可以有客户数据的索引、产品数据的索引、订单数据的索引,当需要检索特定数据的时候,只需要操作对应的索引即可
文档
Elasticsearch 跟 mysql 这种关系型数据库不同, Elasticsearch 不会将信息存储为列数据的行,而是将数据序列化成 JSON 文档的结构进行存储。比如在客户索引中,一条客户数据就可以称之为一个文档
字段类型
Elasticsearch 支持以下简单字段类型
类型 | 表示的数据类型 |
---|---|
String | string |
Whole number | byte,short,integer,long |
Floating point | float,double |
Boolean | boolean |
Date | date |
Elasticsearch 安装很简单,我们只需要到 Elasticsearch 官网 下载最新版的 ElasticSearch 压缩包(目前最新版是 7.6.0),然后解压运行 bin/elasticsearch.sh 脚本就行啦(Windows 运行bin\elasticsearch.bat)
ElasticSearch 默认启动端口是 9200,测试 ElasticSearch 是否启动成功,访问 http://localhost:9200,如果浏览器返回以下信息证明启动成功
{
"name" : "es01",
"cluster_name" : "es-cluster-name",
"cluster_uuid" : "rfEcJdBuQYi951oNAA76cw",
"version" : {
"number" : "7.6.0",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "7f634e9f44834fbc12724506cc1da681b0c3b1e3",
"build_date" : "2020-02-06T00:09:00.449973Z",
"build_snapshot" : false,
"lucene_version" : "8.4.0",
"minimum_wire_compatibility_version" : "6.8.0",
"minimum_index_compatibility_version" : "6.0.0-beta1"
},
"tagline" : "You Know, for Search"
}
任何程序都能使用 RESTful API,通过 9200 端口与 Elasticsearch 进行通信。向 Elasticsearch 发出的请求的组成部分与其他普通的 HTTP 请求是一样的,举例说明,想要计算集群中的文档数量,我们可以这样做:
curl -XGET 'http://localhost:9200/_count?pretty' -H 'Content-Type: application/json' -d '
{
"query": {
"match_all": {}
}
}
'
Elasticsearch 返回一个类似 200 OK 的 HTTP 状态码和 JSON 格式的响应主题(除了 HEAD 请求)。上面的请求会得到如下的 JSON 格式的相应主体:
{
"count" : 0,
"_shards" : {
"total" : 0,
"successful" : 0,
"skipped" : 0,
"failed" : 0
}
}
为简单起见,下面统一将请求简写成这样
GET /_count
{
"query": {
"match_all": {}
}
}
下面演示如何通过 Elasticsearch 进行数据的存储与检索,假设我们要存储一些员工数据,先创建员工索引
POST /employee
{
"mappings": {
"properties": {
"first_name": {
"type": "keyword"
},
"last_name": {
"type": "keyword"
},
"age": {
"type": "integer"
},
"about": {
"type": "text"
},
"interests": {
"type": "keyword"
}
}
}
}
POST /employee/_doc/1
{
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
可以看到 path:/employee/_doc/1 包含三部分信息
名字 | 说明 |
---|---|
employee | 索引名称 |
_doc | 文档类型,es 7 之后取消自定义文档类型,这里可以简单理解为一个关键字 |
1 | 这个员工的ID |
很简单吧,接下来准备多2条员工数据
POST /employee/_doc/2
{
"first_name": "Jane",
"last_name": "Smith",
"age": 32,
"about": "I like to colelct rock albums",
"interests": [ "music" ]
}
POST /employee/_doc/3
{
"first_name": "Douglas",
"last_name": "Fir",
"age": 35,
"about": "I like to buid cabinets",
"interests": [ "forestry" ]
}
GET /employee/_doc/1
响应的内容中包含一些文档的元信息,John Smith的原始JSON文档包含在 _source 字段中
{
"_index": "employee",
"_type": "_doc",
"_id": "1",
"_version": 1,
"_seq_no": 0,
"_primary_term": 1,
"found": true,
"_source":{
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I like to buid cabinets",
"interests":[
"sports",
"music"
]
}
}
上面是直接根据 id 搜索,下面让搜索稍微再变的复杂一些。我们依旧想要找到姓氏为“Smith”的员工,但是我们只想得到年龄大于30岁的员工。复杂搜索中的请求关键字是_search
,我们的语句将添加过滤器(filter),它使得我们高效率的执行一个结构化搜索:
GET /megacorp/employee/_search
{
"query": {
"bool": {
"must": [
{
"range": {
"age": {
"gt": 30 <1>
}
}
},
{
"match": {
"last_name": "Smith" <2>
}
}
]
}
}
}
<1> 这部分查询属于“区间过滤器”(range filter),它用于查询所有年龄大于 30 岁的数据
<2> 这部分查询用于查询 last_name 为 smith 的数据
整条查询语句的意思就是查询出“在employee索引哩查处年龄大于30岁,姓氏为smith的员工“
Elasticsearch 返回结果为
{
"took" : 41,
"timed_out" : false,
"_shards" : {
"total" : 2,
"successful" : 2,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.4700036,
"hits" : [
{
"_index" : "employee",
"_type" : "_doc",
"_id" : "2",
"_score" : 1.4700036,
"_source" : {
"first_name" : "Jane",
"last_name" : "Smith",
"age" : 32,
"about" : "I like to colelct rock albums",
"interests" : [
"music"
]
}
}
]
}
}
如果说上面的功能都很简单的话,那么接下来的搜索就是传统数据库很难实现的功能。如搜索喜欢“rock climbing"的员工:
GET /employee/_search
{
"query": {
"match": {
"about": "rock climbing"
}
}
}
该查询会得到两个匹配文档:
{
"took" : 690,
"timed_out" : false,
"_shards" : {
"total" : 2,
"successful" : 2,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 2,
"relation" : "eq"
},
"max_score" : 1.4167401,
"hits" : [
{
"_index" : "employee",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.4167401,
"_source" : {
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests" : [
"sports",
"music"
]
}
},
{
"_index" : "employee",
"_type" : "_doc",
"_id" : "2",
"_score" : 0.4589591,
"_source" : {
"first_name" : "Jane",
"last_name" : "Smith",
"age" : 32,
"about" : "I like to colelct rock albums",
"interests" : [
"music"
]
}
}
]
}
}
可以看到返回的两条文档都有一个 _score 字段,该字段为结果相关性评分,所谓结果相关性评分就是文档与查询条件的匹配程度,得分越高表示匹配相关性越高
我们搜索的是 about 字段有 ”rock climbing“ 关键字的文档,但是 Jane Smith 为什么也会出现在结果中呢?这是因为 Jane Smith 的 about 字段中也有 “rock” 关键字,所以它他也会被检索到,但是结果相关性评分比较低
这个例子很好的解释了 Elasticsearch 是如何在各种文本字段中进行全文搜索,并返回相关性结果结合的。相关性(relevance)的概念在Elasticsearch中非常重要,而这个概念在传统关 系型数据库中是不可想象的,因为传统数据库对记录的查询只有匹配或者不匹配