ElasticSearch又称ES,是一个开源的高扩展的分布式全文搜索引擎,它可以近乎实时的存储、检索数据;本身扩展性很好,可以扩展到上百台服务器,处理 PB 级别的数据。Elasticsearch是面向文档型数据库,一 条数据在这里就是一个文档。
全文搜索引擎指的是目前广泛应用的主流搜索引擎。它的工作原理是计算机索引程序通过扫描文章中的每一个词,对每一个词建立一个索引,指明该词在文章中出现的次数和位置,当用户查询时,检索程序就根据事先建立的索引进行查找,并将查找的结果反馈给用户的检索方式。这个过程类似于通过字典中的检索字表查字的过程。
下图是ES和MySQL中一些概念的对应,其中type的概念已经被弱化,在ES 7.X之后被删除。
一个索引就是一个拥有几分相似特征的文档的集合。一个索引由一个名字来标识(必须全部是小写字母),并且当我们要对这个索引中的文档进行索引、搜索、更新和删除(CRUD)的时候,都要使用到这个名字。在一个集群中,可以定义任意多的索引。相当于MySQL中的数据库。
一个索引中可以定义一种或多种类型。一个类型是索引的一个逻辑上的分类/分区,其语义完全由你来定。相当于MySQL中的表。
一个文档是一个可被索引的基础信息单元,也就是一条数据。相当于MySQL中的一条记录。
相当于是数据表的字段,对文档数据根据不同属性进行的分类标识。相当于MySQL中表的字段。
mapping是处理数据的方式和规则方面做一些限制,如某个字段的数据类型、默认值、分析器、是否被索引等等。相当于MySQL中的建表过程中设置默认值、主外键、索引等。
一个集群就是由一个或多个节点组织在一起,它们共同持有整个的数据,并一起提供索引和搜索功能。
一个节点是集群中的一个服务器,作为集群的一部分,它存储数据,参与集群的索引和搜索功能。
Elasticsearch提供了将索引划分成多份的能力,这些份就叫做分片。它允许水平分割/扩展你的内容容量,或者在多个节点上上进行分布式的、并行的操作,进而提高性能/吞吐量。
分片很重要,主要有两方面的原因:
Elasticsearch允许你创建分片的一份或多份备份,这些备份叫做复制分片,或者直接叫副本。
复制分片之所以重要,有两个主要原因:
在分片/节点失败的情况下,提供了高可用性
扩展你的搜索量/吞吐量,因为搜索可以在所有的副本上并行运行
将分片分配给某个节点的过程,包括分配主分片或者副本。如果是副本,还包含从主分片复制数据的过程。这个过程是由master节点完成的。
词条:索引中最小存储和查询单元
词典:字典,词条的集合,具体实现有B+树、HashMap等
Elasticsearch使用一种称为倒排索引的结构,它适用于快速的全文搜索。有倒排索引,肯定会对应有正向索引(forward index)。倒排索引也称反向索引(inverted index)。所谓的正向索引,就是搜索引擎会将待搜索的文件都对应一个文件ID,搜索时将这个ID和搜索关键字进行对应,形成K-V对,然后对关键字进行统计计数。
但是互联网上收录在搜索引擎中的文档的数目是个天文数字,这样的索引结构根本无法满足实时返回排名结果的要求。所以,搜索引擎会将正向索引重新构建为倒排索引,即把文件ID对应到关键词的映射转换为关键词到文件ID的映射,每个关键词都对应着一系列的文件,这些文件中都出现这个关键词。
早期的全文检索会为整个文档集合建立一个很大的倒排索引并将其写入到磁盘。 一旦新的索引就绪,旧的就会被其替换,这样最近的变化便可以被检索到。倒排索引被写入磁盘后是不可改变的,它永远不会修改。
优点:
不需要锁。如果你从来不更新索引,你就不需要担心多进程同时修改数据的问题。
一旦索引被读入内核的文件系统缓存,便会留在哪里,由于其不变性。只要文件系统缓存中还有足够的空间,那么大部分读请求会直接请求内存,而不会命中磁盘。这提供了很大的性能提升。
其它缓存(像filter缓存),在索引的生命周期内始终有效。它们不需要在每次数据改变时被重建,因为数据不会变化。
写入单个大的倒排索引允许数据被压缩,减少磁盘IO和需要被缓存到内存的索引的使用量。
缺点:由于其不可改变,所以如果你需要让一个新的文档可被搜索,你需要重建整个索引。这要么对一个索引所能包含的数据量造成了很大的限制,要么对索引可被更新的频率造成了很大的限制。
如何在保留不变性的前提下实现倒排索引的更新?答案是用更多的索引。通过增加新的补充索引来反映新近的修改,而不是直接重写整个倒排索引。每一个倒排索引都会被轮流查询到,从最早的开始查询完后再对结果进行合并。
Elasticsearch基于Lucene,这个java库引入了按段搜索的概念。每一段本身都是一个倒排索引,但索引在 Lucene 中除表示所有段的集合外,还增加了提交点的概念——一个列出了所有已知段的文件。
当一个查询被触发,所有已知的段按顺序被查询。词项统计会对所有段的结果进行聚合,以保证每个词和每个文档的关联都被准确计算。这种方式可以用相对较低的成本将新文档添加到索引。
段是不可改变的,所以既不能从把文档从旧的段中移除,也不能修改旧的段来进行反映文档的更新。取而代之的是,每个提交点会包含一个.del 文件,文件中会列出这些被删除文档的段信息。当一个**文档被“删除”**时,它实际上只是在 .del 文件中被标记删除。一个被标记删除的文档仍然可以被查询匹配到,但它会在最终结果被返回前从结果集中移除。
文档更新也是类似的操作方式:当一个文档被更新时,旧版本文档被标记删除,文档的新版本被索引到一个新的段中。可能两个版本的文档都会被一个查询匹配到,但被删除的那个旧版本文档在结果集返回前就已经被移除。
分析器实际上是将三个功能封装到了一个包里:
字符过滤器:首先,字符串按顺序通过每个 字符过滤器 。他们的任务是在分词前整理字符串。一个字符过滤器可以用来去掉 HTML,或者将 & 转化成 and。
分词器:其次,字符串被分词器分为单个的词条。一个简单的分词器遇到空格和标点的时候,可能会将文本拆分成词条。
Token 过滤器:最后,词条按顺序通过每个 token 过滤器 。这个过程可能会改变词条(例如,小写化Quick ),删除词条(例如, 像 a, and, the 等无用词),或者增加词条(例如,像jump和leap这种同义词)
标准分析器:标准分析器是Elasticsearch 默认使用的分析器。它是分析各种语言文本最常用的选择。它根据Unicode 联盟定义的单词边界划分文本。删除绝大部分标点。最后,将词条小写。
简单分析器:简单分析器在任何不是字母的地方分隔文本,将词条小写。
空格分析器:空格分析器在空格的地方划分文本。
语言分析器:特定语言分析器可用于很多语言。它们可以考虑指定语言的特点。例如,英语分析器附带了一组英语无用词(常用单词,例如and或者the ,它们对相关性没有多少影响),它们会被删除。由于理解英语语法的规则,这个分词器可以提取英语单词的词干。
自定义分析器:虽然Elasticsearch带有一些现成的分析器,然而在分析器上Elasticsearch真正的强大之处在于,你可以通过在一个适合你的特定数据的设置之中组合字符过滤器、分词器、词汇单元过滤器来创建自定义的分析器。一个分析器就是在一个包里面组合了三种函数的一个包装器,三种函数按照顺序被执行:
字符过滤器:字符过滤器用来整理一个尚未被分词的字符串。
分词器:一个分析器必须有一个唯一的分词器。分词器把字符串分解成单个词条或者词汇单元。
词单元过滤器:经过分词,作为结果的词单元流会按照指定的顺序通过指定的词单元过滤器。词单元过滤器可以修改、添加或者移除词单元。
ES中主要有GET/PUT/POST/DELETE这四种操作,分别表示查询、更新或创建、创建、删除操作。其中PUT和POST的区别是:PUT在插入新数据的时候需要指定_id
,而POST则不需要,这个_id
是ES中文档的唯一标识。换种说法,PUT是幂等的,即多次执行的结果相同,而POST不是幂等的,多次执行会插入多条数据。
_mapping/_settings/_search/_count/_doc
_mapping/_settings/_search/_count/_doc
分别是对映射、索引设置、查询记录值、查询记录数、查询文档信息的操作,具体看下文中的列子。
# 创建索引的设置信息,也可以直接创建索引字段,此时设置信息取默认值
PUT index_name
{
"settings" : {
"index" : {
"refresh_interval" : "60s",
"number_of_shards" : "3",
"translog" : {
"durability" : "async"
},
"max_result_window" : "10000000",
"unassigned" : {
"node_left" : {
"delayed_timeout" : "30m"
}
},
"number_of_replicas" : "1"
}
}
}
其中number_of_shards为分片数
# 查询索引设置信息
GET trade_bill_detail/_settings
# 删除索引
DELETE trade_bill_detail
# 为索引添加新字段
PUT trade_bill_detail/_mapping
{
"properties" : {
"begin_time" : {
"type" : "date"
},
"expense_begin_time" : {
"type" : "keyword"
},
"account_id" : {
"type" : "long"
},
"price_new" : {
"type" : "double"
}
}
}
# 查看索引的所有字段
GET trade_bill_detail/_mapping
# 查询文档信息,1798023是文档的id
GET trade_bill_detail/_doc/1798023
# 查询数据总量,以kb为单位。用来在进行数据同步时查询同步情况
GET _cat/indices/trade_bill_detail?v&h=index,docs.count,store.size&bytes=kb
# 相等条件查询
GET trade_bill_detail/_search
{
"query": {
"match": {
"BillID": "Bill7037412215733195052"
}
}
}
# 相等条件查询
GET trade_bill_detail/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"ID": "1648022"
}
}
]
}
}
}
# 不等条件查询
GET trade_bill_detail/_search
{
"query": {
"bool": {
"must_not": {
"match": {
"DataDisplayRule": "1"
}
}
}
}
}
# 模糊查询
GET trade_bill_detail/_search
{
"query": {
"wildcard": {
"BillID": {
"value": "*Bill7025695920478818604*"
}
}
}
}
# 对多个字段执行相同的查询
GET trade_bill_detail/_search
{
"query": {
"multi_match":{
"query": "0",
"fields": ["Price","PriceNew"]
}
}
}
# 排序
GET trade_bill_detail/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"ID": {
"order": "desc"
},
"BeginT": {
"order": "asc"
}
}
]
}
# 集合查询,in条件查询
GET trade_bill_detail/_count
{
"query": {
"terms": {
"ID": [1492462,1648018,1648022,1692420,1692421,1692422,1692423,1692424]
}
}
}
# 搜索某字段符合特定条件的记录数
GET trade_bill_detail/_count
{
"query": {
"match": {
"DataDisplayRule": "1"
}
}
}
# 查询总记录数
GET trade_bill_detail/_count
# range查询,gte是大于等于,gt是大于,lte是小于等于,lt是小于
GET index_name/_search
{
"query":{
"range":{
"Price":{
"gte": 10,
"lte": 30
}
}
}
}
GET index_name/_search
{
"from":0,
"query":{
"bool":{
"filter":[
{
"range":{
"BeginTime":{
"from":"2021-01-01T00:00:00+08:00",
"include_lower":true,
"include_upper":true,
"to":null
}
}
},
{
"range":{
"BeginTime":{
"from":null,
"include_lower":true,
"include_upper":false,
"to":"2022-01-01T00:00:00+08:00"
}
}
},
{
"term":{
"BillType":"normal"
}
},
{
"term":{
"SubjectNo":"3423"
}
},
{
"terms":{
"DataDisplayRule":[
"1",
"3"
]
}
},
{
"bool":{
"should":[
{
"bool":{
"filter":{
"wildcard":{
"BillProperty":{
"wildcard":"*system*"
}
}
}
}
},
{
"bool":{
"filter":{
"wildcard":{
"BillProperty":{
"wildcard":"*orderNew*"
}
}
}
}
}
]
}
},
{
"bool":{
"should":[
{
"bool":{
"filter":{
"term":{
"OrderNo":"Bill7025276297373585708"
}
}
}
},
{
"bool":{
"filter":{
"term":{
"BillID":"Bill7025276297373585708"
}
}
}
}
]
}
}
]
}
},
"size":10,
"sort":[
{
"BeginTime":{
"order":"desc"
}
},
{
"InstanceNo":{
"order":"asc"
}
},
{
"ID":{
"order":"asc"
}
}
],
"track_total_hits":true
}