ElasticSearch概述
ElasticSearch是一个开源的高扩展的分布式全文搜索引擎,可以近实时的存储、检索数据;扩展性很好,可以扩展到上百台服务器,处理PB级别的数据,ES是用java开发并使用Lucene作为其核心来实现所有索引和搜索的功能,目的是通过简单的Restful API来隐藏Lucene的复杂性。
Elastic Search核心概念
ElasticSearch是面向文档的,下面是关系型数据库和ElasticSearch的对比:
Relational DB | Elasticsearch |
---|---|
数据库(database) | 索引(indices) |
表(table) | 类型(types) |
行(rows) | 文档(documents) |
字段(columns) | 字段(fields) |
Elasticsearch集群中可以包含多个索引(数据库),每个索引可以包含多个类型(表),每个类型下又包含多个文档(行),每个文档中又包含多个字段(列)。
1、物理设计
Elasticsearch在后台把每个索引划分成多个分片,每个分片可以在集群的不同服务器中迁移。
2、逻辑设计
一个索引类型中包含多个文档。索引一篇文档时,可以通过索引 > 类型 > 文档ID(不是整数,是个字符串)获取。
索引
存储员工数据:一个文档代表一个员工。存储数据到ElasticSearch的行为叫做索引,但是在索引一个文档之前,需要确定文档的存储位置。
索引(名词):一个索引类似于传统关系型数据库中的一个数据库,是一个存储关系型文档的地方。
索引(动词):索引一个文档就是存储一个文档到一个索引(名词)中以便被检索和查询。
倒排索引:在关系型数据库中,通过增加一个索引比如一个B树(B-Tree)索引到指定的列上,以便于提升数据检索速度。
文档
Elasticsearch是面向文档的,则说明索引和搜索文档的最小单位是文档,文档有几个重要的属性:
- 自我包含,一篇文档包含字段和对应的值,即同时包含key:value。
- 可以是层次型的,一个文档包含自文档,这就是复杂的逻辑实体的来由。
- 灵活的结构,文档不依赖预先定义的模式。
注:数据库的水平拆分和垂直拆分。数据库拆分简单来说,就是指通过某种特定的条件,按照某个特定的维度,将我们存放在同一个数据库的数据分散到多个数据库(主机)上面以达到分散单库(主机)负载的效果。拆分模式:垂直(纵向)拆分、水平拆分。
拆分模式 | 基本概念 | 优点 | 缺点 |
---|---|---|---|
垂直拆分 | 专库专用,一个数据库有很多表,每个表对应着不同的业务,垂直拆分是按照业务将表进行分类,分布到不同的数据库上,这样就将数据或者压力分布到不同的库上面。 | ①拆分后业务清晰,拆分规则明确。②系统之间整合和扩展容易。③数据维护简单。 | ①部分业务表无法join,只能通过接口解决,提高了系统复杂度。②受每种业务不同的限制存在单裤性能瓶颈,不易数据扩展跟性能提高。③事务处理复杂。 |
水平拆分 | 垂直拆分后遇到单机瓶颈,可以使用水平拆分。垂直拆分是把不同的表拆分到不同的数据库中,相对于垂直拆分,水平拆分则是把同一个表拆分到不同的数据库中。 | ①不存在单库大数据,高并发的性能瓶颈。②对应用透明,应用改造较少。③按照合理拆分规则拆分,join操作基本避免跨库。④提高了系统的稳定性和负载能力。 | ①拆分规则难以抽象。②分片事务一致性难以解决。③数据多次扩展难度和维护量极大。④跨库join性能较差。 |
基本查询
索引员工文档:
PUT /megacorp/employee/1
{
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
路径 /megacorp/employee/1
包含了三部分信息,即/索引名称/类型名称/特定雇员的ID。
检索文档:
GET /megacorp/employee/1
注:将 HTTP 命令由 PUT
改为 GET
可以用来检索文档,同样的,可以使用 DELETE
命令来删除文档,以及使用 HEAD
指令来检查文档是否存在。如果想更新已存在的文档,只需再次 PUT
。
结果:
{
"_index" : "megacorp",
"_type" : "employee",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : {
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
}
轻量搜索:
GET /megacorp/employee/_search
结果:
{
"took": 6,
"timed_out": false,
"_shards": { ... },
"hits": {
"total": 3,
"max_score": 1,
"hits": [
{
"_index": "megacorp",
"_type": "employee",
"_id": "3",
"_score": 1,
"_source": {
"first_name": "Douglas",
"last_name": "Fir",
"age": 35,
"about": "I like to build cabinets",
"interests": [ "forestry" ]
}
},
{
"_index": "megacorp",
"_type": "employee",
"_id": "1",
"_score": 1,
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
},
{
"_index": "megacorp",
"_type": "employee",
"_id": "2",
"_score": 1,
"_source": {
"first_name": "Jane",
"last_name": "Smith",
"age": 32,
"about": "I like to collect rock albums",
"interests": [ "music" ]
}
}
]
}
}
注:返回结果不仅告诉匹配了哪些文档,还包含了整个文档本身:显示搜索结果给最终用户所需的全部信息。
搜索姓氏为"Smith"的雇员。使用高亮搜索,命令行如下:
GET /megacorp/employee/_search?q=last_name:Smith
结果:
{
...
"hits": {
"total": 2,
"max_score": 0.30685282,
"hits": [
{
...
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
},
{
...
"_source": {
"first_name": "Jane",
"last_name": "Smith",
"age": 32,
"about": "I like to collect rock albums",
"interests": [ "music" ]
}
}
]
}
}
使用查询表达式(body体)搜索:
查询姓氏为"Smith"的雇员,写法如下:
GET /megacorp/employee/_search
{
"query":{
"match":{
"last_name":"Smith"
}
}
}
更复杂的搜索:
同样搜索姓氏为"Smith"的员工,但是只需要年龄大于30的。此处需要使用过滤器filter,它支持高效的执行一个结构化查询。
GET /megacorp/employee/_search
{
"query":{
"bool":{
"must":{
"match":{
"last_name":"smith" ①
}
}
},
"filter":{
"age":{
"gt":30 ②
}
}
}
}
①match查询。②是***range***过滤器,能找到年龄大于30的文档,其中gt表示大于(great_than)。
全文搜索:
搜索所有喜欢攀岩(rock climbing)的员工:
GET /megacorp/employee/_search
{
"query":{
"match":{
"about":"rock climbing"
}
}
}
结果如下:
{
...
"hits": {
"total": 2,
"max_score": 0.16273327,
"hits": [
{
...
"_score": 0.16273327, ①
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
},
{
...
"_score": 0.016878016, ①
"_source": {
"first_name": "Jane",
"last_name": "Smith",
"age": 32,
"about": "I like to collect rock albums",
"interests": [ "music" ]
}
}
]
}
}
①相关性得分:ElasticSearch默认按照相关性得分排序,即每个文档跟查询的匹配程度。这个案例很好的阐述了ElasticSearch如何在全文属性上搜索并返回相关性最强的概念。ElasticSearch中的相关性概念非常重要,也是完全区别于传统关系型数据库的一个概念,数据库中的一条记录要么匹配要么不匹配。
短语搜索:
精确的匹配某个单词或者短语。比如,仅匹配同时包含”rock“和”climbing“,并且二者以短语形式紧挨着的雇员记录。
GET /megacorp/employee/_search
{
"query":{
"match_phrase":{
"about":"rock climbing"
}
}
}
结果如下:
{
...
"hits": {
"total": 1,
"max_score": 0.23013961,
"hits": [
{
...
"_score": 0.23013961,
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
}
]
}
}
高亮搜索:
执行前面的查询,增加highlight参数。
GET /megacorp/employee/_search
{
"query":{
"match_phrase":{
"about":"rock climbing"
}
},
"high lignt":{
"fields":{
"about":{}
}
}
}
结果如下:
{
...
"hits": {
"total": 1,
"max_score": 0.23013961,
"hits": [
{
...
"_score": 0.23013961,
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": [ "sports", "music" ]
},
"highlight": {
"about": [
"I love to go <em>rock</em> <em>climbing</em>" ①
]
}
}
]
}
}
①原始文本中的高亮片段。
高亮搜索片段其他详细信息:https://www.elastic.co/guide/en/elasticsearch/reference/5.6/search-request-highlighting.html
分析:
Elasticsearch的聚合功能(aggrgations),允许我们基于数据生成一些精细的分析结果。
挖掘出员工中最受欢迎的兴趣爱好:
GET /megacorp/employee/_search
{
"aggs":{
"all_interests":{
"terms":{
"fields":{"interests"}
}
}
}
}
结果如下:
{
...
"hits": { ... },
"aggregations": {
"all_interests": {
"buckets": [
{
"key": "music",
"doc_count": 2
},
{
"key": "forestry",
"doc_count": 1
},
{
"key": "sports",
"doc_count": 1
}
]
}
}
}
叫 Smith 的员工中最受欢迎的兴趣爱好,可以直接构造一个组合查询:
GET /megacorp/employee/_search
{
"query":{
"match":{
"last_name":"smith"
}
},
"aggs":{
"all_interests":{
"terms":{
"field":"interests"
}
}
}
}
结果如下:aggregations聚合已经变成只包含匹配查询的文档。
...
"all_interests": {
"buckets": [
{
"key": "music",
"doc_count": 2
},
{
"key": "sports",
"doc_count": 1
}
]
}
聚合还支持分级汇总 。比如,查询特定兴趣爱好员工的平均年龄:
GET /megacorp/employee/_search
{
"aggs":{
"all_interests":{
"terms":{
"field":"interests",
}
"aggs":{
"avg_age":{
"avg":{"field":"age"}
}
}
}
}
}
结果如下:
...
"all_interests": {
"buckets": [
{
"key": "music",
"doc_count": 2,
"avg_age": {
"value": 28.5
}
},
{
"key": "forestry",
"doc_count": 1,
"avg_age": {
"value": 35
}
},
{
"key": "sports",
"doc_count": 1,
"avg_age": {
"value": 25
}
}
]
}
字段数据类型