安装Elasticsearch 全文搜索/大数据分析
ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java开发的,并作为Apache许可条款下的开放源码发布,是当前流行的企业级搜索引擎。设计用于云计算中,能够达到实时搜索,稳定,可靠,快速,安装使用方便。
Elasticsearch的应用场景
- 一个线上商城系统,用户需要搜索商城上的商品。在这里你可以用es存储所有的商品信息和库存信息,用户只需要输入”空调”就可以搜索到他需要搜索到的商品。
- 一个运行的系统需要收集日志,用这些日志来分析、挖掘从而获取系统业务未来的趋势。你可以用logstash(ELK中的一个产品,elasticsearch/logstash/kibana)收集、转换你的日志,并将他们存储到es中。一旦数据到达es中,就你可以在里面搜索、运行聚合函数等操作来挖掘任何你感兴趣的信息。
- 如果你有想基于大量数据(数百万甚至数十亿的数据)快速调查、分析并且要将分析结果可视化的需求。你可以用es来存储你的数据,用kibana构建自定义的可视化图形、报表,为业务决策提供科学的数据依据。
安装ElasticSearch
版本说明:ES5 需要 Java 8 及以上的版本。ES7 开始内置了 Java 环境
首先确认下jdk有没有安装
java -version
下载源文件,解压,重新建一个用户,将目录的所属组修改为此用户,因为 elasticsearch 无法用 root 用户启动。
wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.3.0.tar.gz
tar zxvf elasticsearch-6.3.0.tar.gz
useradd elasticsearch
passwd elasticsearch
chown -R elasticsearch:elasticsearch elasticsearch-6.3.0
cd elasticsearch-6.3.0
./bin/elasticsearch // 启动
./bin/elasticsearch -d //后台启动
cd config
vim jvm.options
# 修改参数如下(服务器小,不配大容量)
-Xms256m
-Xmx256m
可能存在问题
Exception in thread "main" java.nio.file.AccessDeniedException: /usr/local/src/elasticsearch-6.3.0/config/jvm.options
at sun.nio.fs.UnixException.translateToIOException(UnixException.java:84)
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102)
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107)
at sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:214)
at java.nio.file.Files.newByteChannel(Files.java:361)
at java.nio.file.Files.newByteChannel(Files.java:407)
at java.nio.file.spi.FileSystemProvider.newInputStream(FileSystemProvider.java:384)
at java.nio.file.Files.newInputStream(Files.java:152)
at org.elasticsearch.tools.launchers.JvmOptionsParser.main(JvmOptionsParser.java:58)
报这个错是因为用户的权限不足,因此在配置和启动ElasticSearch节点的时候要注意给用户赋予对应的权限
chown -R myuser:myuser /usr/local/src/elasticsearch-6.3.0
连接Elasticsearch:
1、Elasticsearch开启之后,可以直接通过http://127.0.0.1:9200/查看基本信息。
{
"name": "iEX7av1",
"cluster_name": "elasticsearch",
"cluster_uuid": "8TMqWFwnTLiBlRKtU7a8YQ",
"version": {
"number": "6.3.0",
"build_flavor": "default",
"build_type": "tar",
"build_hash": "424e937",
"build_date": "2018-06-11T23:38:03.357887Z",
"build_snapshot": false,
"lucene_version": "7.3.1",
"minimum_wire_compatibility_version": "5.6.0",
"minimum_index_compatibility_version": "5.0.0"
},
"tagline": "You Know, for Search"
}
安装中文分词 elasticsearch-analysis-ik
ik有两种分词模式,ik_max_word,和ik_smart模式
- ik_max_word: 会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合;
- ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”。
索引时,为了提供索引的覆盖范围,通常会采用ik_max_word分析器,会以最细粒度分词索引,搜索时为了提高搜索准确度,会采用ik_smart分析器,会以粗粒度分词。使用ik_max_word会占用更多的存储空间
#在线安装
./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.3.0/elasticsearch-analysis-ik-6.3.0.zip
#本地安装
./bin/elasticsearch-plugin install file:///usr/local/src/elasticsearch-analysis-ik-6.3.0.zip
安装好后 重启es会看到 加载了 ik分词了

安装完毕后,发现在ES的安装目录下的plugins目录下多了一个analysis-ik目录(内容是ik的zip包解压后根目录下的所有文件,一共是5个jar文件和1个properties配置文件),另外ES的安装目录下的config目录下多了一个analysis-ik目录(内容是ik的zip包解压后根目录下的config目录下所有文件,用于放置ik的自定义词库)
可视化Elasticvue
- 平台:Windows / Mac / Linux 三端原生桌面 App + 浏览器插件
- 费用:完全免费、开源 MIT 协议,无任何功能阉割
- 核心能力(完美适配你的阿里云 ES)
- 支持账号密码认证、HTTPS、阿里云公网 ES 连接、IP 白名单环境
- 索引可视化浏览、文档表格 / JSON 双视图、单条文档增删改查
- DSL 编辑器语法高亮、自动格式化、保存常用查询语句
- Mapping 查看 / 编辑、集群健康监控、分片状态、批量 Bulk 操作
- 支持 ES 7.x/ 8.x 全版本,兼容 OpenSearch
- 适合人群:日常查 CRM 客户文档、调试 DSL、多环境集群切换,90% 开发者首选
- 缺点:重度集群监控图表不如 Kibana 专业
安装Kibana
Kibana是一个针对Elasticsearch的开源分析及可视化平台,用来搜索、查看交互存储在Elasticsearch索引中的数据。安装Kibana要装与ES相同的版本
wget https://artifacts.elastic.co/downloads/kibana/kibana-6.3.0-linux-x86_64.tar.gz
tar -zxvf kibana-6.3.0-linux-x86_64.tar.gz
cd kibana-6.3.0-linux-x86_64
#启动Kibana
./bin/kibana
http://localhost:5601访问web页面;
使用POSTMAN工具发送PUT请求创建
ES的基本用法:ES以RESTFul风格来命名API的, 其API的基本格式如下
http://<ip>:<port>/<索引>/<类型>/<文档id>
ES的动作是以http方法来决定的: 常用的http方法: GET/PUT/POST/DELETE
es支持多字段结构,例如:我们将常用的搜索条件和排序字段(销量,时间)配置到mappings.properties中
{
"settings":{
"number_of_shards": 3,
"number_of_replicas": 1
},
"mappings":{
"man":{
"properties":{
"word_count":{
"type": "integer"
},
"author":{
"type": "keyword"
},
"title":{
"type": "text"
},
"publish_date":{
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss || yyyy-MM-dd || epoch_millis"
}
}
}
}
}
如下图
观察返回结果已经成功。
插入文档:PUT 127.0.0.1:9200/people/man
{
"name": "chenjie",
"country": "China",
"age": 30,
"date": "1994-09-27"
}
修改文档:POST 127.0.0.1:9200/people/man/1/_update
{
"doc":{
"name":"chenjie_update"
}
}
删除文档
DELETE 127.0.0.1:9200/people/man/1
批量操作
批量操作 bulk
- 支持在一次 API 调用中,对不同的索引进行操作
- 支持四种类型操作Index/Create/Update/Delete
- 可以在 URI 中指定 Index,也可以在请求的 Payload 中进行
- 单条操作失败,并不会影响其他操作
- 返回结果包括了每一条操作执行的结果
测试命令:
POST _bulk
{ "index" : { "_index" : "test", "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_id" : "2" } }
{ "create" : { "_index" : "test2", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }
ES 三种主流分页对比

方案 1:from + size 浅分页
优点
- 使用最简单,和 MySQL LIMIT offset,size 逻辑一致,前端页码直接映射:
from=(page-1)*size - 支持跳页:直接点第 100 页、第 50 页无任何代码改动
- 不需要维护游标、上下文,无状态,接口好设计
- 小页码(前几十页)性能极好
缺点(致命)
- 深度分页性能雪崩 ES 需要把
from+size条数据全部拿到内存排序、打分、截取,from=10000之后 CPU、内存暴涨,官方默认限制index.max_result_window:10000,超过直接报错。 - 排序字段无唯一值时,分页数据重复、漏数据(比如按 score 排序,多条分数相同)
- 不适合大数据量导出、全量遍历
方案 2:scroll 滚动分页(游标快照)
原理
第一次请求创建一个快照上下文(scroll_id),后续根据 scroll_id 持续拉取下一批数据,不会重复打分排序。
优点
- 可以百万级、千万级全量遍历导出,深度分页性能稳定
- 不需要一次性加载大量数据到内存,分片逐个读取
- 适合数据同步、离线 ETL、索引重建、批量计算
缺点(生产几乎不再推荐)
- 无法跳页,只能顺序一页一页往下翻,不能直接跳到第 N 页
- scroll 上下文占用内存,设置超时时间过长会堆内存泄漏,必须手动清理
clear scroll - 快照是创建时的数据,遍历期间新写入 / 更新的文档看不到,非实时视图
- ES7 开始官方弱化 scroll,优先推荐 search_after
- 并发多 scroll 容易造成分片压力
- scroll 超时,“上下文丢失”崩溃模式 search_context_missing_exception
方案 3:search_after 游标分页(最优生产方案)
原理
依靠排序字段最后一条的唯一值作为游标,下一次传入 search_after:[值1,值2] 从该位置继续取数,本质是主键游标分页。 必须:排序字段组合全局唯一(通常 _id 兜底)
优点
- 深度分页性能稳定,几万、十几万页压力几乎不变,不受 max_result_window 限制
- 无 scroll 上下文内存开销,无过期清理问题
- 视图是实时的,能看到分页过程中新写入的数据
- 官方主推,ES6+ 标准深度分页方案
- 内存友好,只读取需要的数据,不做大规模排序缓存
- 基于唯一排序键时天然不重不漏,实时新增不会造成偏移重复;天然支持断点续传(记住最后一条 id 即可)。
缺点
- 不支持跳页,只能上一页、下一页顺序翻页,无法直接跳转到第 100 页
- 前端不能展示总页数、总条数(统计 total 开销大),只能展示 “下一页” 按钮
- 排序规则一旦变更,游标直接失效;必须设计唯一排序键(例:
create_time desc, _id asc) - 如果中间数据被删除,会出现游标断层漏一条
使用PHPAPi构建和查询
创建表和测试数据,索引相当于MySQL中的表,文档相当于 MySQL 中的行记录。程序在新增/修改/删除记录的时候调用ES的接口对ES文档进行同步的新增/修改/删除操作。同步构建有很大缺陷。比如:ES宕机或者ES处理文档消耗了很长的时间影响用户的体验。可以使用KaFka中间件来进行异步构建文档。
在 composer.json 文件中引入 elasticsearch-php
{
"require": {
"elasticsearch/elasticsearch": "~6.0"
}
}
CREATE TABLE articles ( id INT NOT NULL PRIMARY KEY auto_increment, title VARCHAR ( 200 ) NOT NULL COMMENT '标题', content text COMMENT '内容' );
INSERT INTO articles ( title, content )
VALUES
( 'Laravel 测试1', 'Laravel 测试文章内容1' ),
( 'Laravel 测试2', 'Laravel 测试文章内容2' ),
( 'Laravel 测试3', 'Laravel 测试文章内容3' );
实例化
require './vendor/autoload.php';
use Elasticsearch\ClientBuilder;
$client = ClientBuilder::create()->build();
elasticsearch的动态性质,在添加第一个文档的时候自动创建了索引和一些默认设置。
创建索引
$params['index'] = 'articles_index';
$params['body']['settings']['number_of_shards'] = 2;
$params['body']['settings']['number_of_replicas'] = 0;
$client->indices()->create($params);
将文档加入索引
foreach ($lists as $row) {
$params = [
'body' =>
[
'id' => $row['id'],
'title' => $row['title'],
'content' => $row['content']
],
'id' => 'article_' . $row['id'], //maping db id
'index' => 'articles_index',
'type' => 'articles_type'
];
$client->index($params);
}
从索引中获取文档
$params = ['index' => 'articles_index', 'type' => 'articles_type', 'id' => 'articles_1'];
$res = $client->get($params);
print_r($res);
删除索引
$params = [
'index' => 'articles_index'
];
$res = $client->indices()->delete($params);
print_r($res);
搜索文档,如果需要获取更多明细信息可以根据mysql表ID获取
<?php
$params = ['index' => 'articles_index', 'type' => 'articles_type'];
$params['body']['query']['match']['content'] = 'Laravel';
$params['body']['size'] = 10;
$params['body']['from'] = 200;
$params['body']['sort'] = ['content' => ['order' => 'desc']];
$res = $client->search($params);
print_r($res);
//相当于sql语句:select * from articles where content='Laravel' limit 200,10 order by content desc;
从索引中删除文档
$params = ['index' => 'articles_index', 'type' => 'articles_type', 'id' => 'articles_1'];
$res = $client->delete($params);
print_r($res);
Elasticsearch更新和删除文档的过程。
- 删除和更新也都是写操作,但是Elasticsearch中的文档是不可变的,因此不能被删除或者改动以展示其变更;
- 磁盘上的每个段都有一个相应的.del文件。当删除请求发送后,文档并没有真的被删除,而是在.del文件中被标记为删除。该文档依然能匹配查询,但是会在结果中被过滤掉。当段合并时,在.del文件中被标记为删除的文档将不会被写入新段。
- 在新的文档被创建时,Elasticsearch会为该文档指定一个版本号,当执行更新时,旧版本的文档在.del文件中被标记为删除,新版本的文档被索引到一个新段。旧版本的文档依然能匹配查询,但是会在结果中被过滤掉。
ElasticSearch搜索term和terms的区别
在查询的字段只有一个值的时候,应该使用term而不是terms,在查询字段包含多个的时候才使用terms(类似于sql中的in、or),使用terms语法,JSON中必须包含数组。
第一种(单个值,term):
{
"query":{
"bool":{
"must":[
{
"range":{
"update_time":{
"gt":"1488556800000"
}
}
},
{
"term":{
"lang":1
}
},
{
"terms":{
"domain":[
"dailymasala.co",
"goldenmob.com"
]
}
},
{
"prefix":{
"user_id":"errVideo_setInterval_"
}
}
]
}
},
"from":0,
"size":10
}
第二种(数组形式,terms):
{
"query": {
"bool": {
"must": [
{
"range": {
"update_time": {
"gt": "1488556800000"
}
}
},
{
"terms": {
"lang": [1]
}
},
{
"terms": {
"domain": [
"dailymasala.co",
"goldenmob.com"
]
}
},
{
"prefix": {
"user_id": "errVideo_setInterval_"
}
}
]
}
},
"from": 0,
"size": 10
}
elasticsearch 中term与match区别
term是代表完全匹配,也就是精确查询,搜索前不会再对搜索词进行分词,所以我们的搜索词必须是文档分词集合中的一个。比如说我们要找标题为北京奥运的所有文档
match查询会先对搜索词进行分词,分词完毕后再逐个对分词结果进行匹配,因此相比于term的精确搜索,match是分词匹配搜索。要搜索词的分词集合中的一个或多个存在于文档中即可,例如,当我们搜索中国杭州,搜索词会先分词为中国和杭州,只要文档中包含搜索和杭州任意一个词,都会被搜索到。对于match搜索,可以按照分词后的分词集合的or或者and进行匹配,默认为or
curl -XGET http://localhost:9200/index/doc/_search?pretty -d
'{
"query": {
"match": {
"content": {
"query": "中国世界",
"operator": "and"
}
}
}
}'
ES性能优化
{
"settings":{
"number_of_shards": 3,
"number_of_replicas": 1,
"index.store.type": "niofs",
"index.query.default_field": "content",
"index.unassigned.node_left.delayed_timeout": "5m",
},
"mappings":{
"man":{
"dynamic":true,
"_all":{"enabled":false},
"properties":{
}
}
}
}
安全控制可以使用apache/nginx的http认证
在 Elasticsearch 中,写入和打开一个新段的轻量的过程叫做 refresh 。 默认情况下每个分片会每秒自动刷新一次。这就是为什么我们说 Elasticsearch 是 近 实时搜索: 文档的变化并不是立即对搜索可见,但会在一秒之内变为可见.
更新延迟问题:搜索时加上?refresh=wait_for,表示如果1秒内有请求立即更新并可见。
更多推荐

所有评论(0)