常见搜索引擎
应用:用户给定关键词,搜索引擎返回与关键词相关的结果
关系:大数据与搜索引擎工程师之间的联系
过程
step1:用户提交搜索词
大数据分析
step2:搜索引擎对用户的搜索词做分词
step3:将爬取到的所有网页构建倒排索引,构建倒排索引库
将每个网页中的每句话进行分词:将一个网页中所有词拆分出来
网页编号 | 网页的内容 | 分词 |
---|---|---|
1 | 大数据工业化应用 | 大数据、工业化、应用 |
2 | 大数据与人工智能的关系 | 大数据、人工智能、关系 |
3 | 大数据的应用场景 | 大数据、应用、场景 |
构建倒排索引
词 | 网页 的id |
---|---|
大数据 | 1,2,3 |
应用 | 1,3 |
人工智能 | 2 |
假设用户搜索:大数据分析
倒排索引概念
step4:根据用户的关键词分词的结果到索引库中进行匹配,返回所有相关的词对应的所有网页
如果搜索引擎通过MySQL或者Oracle来实现能不能满足需求?
查询的条件:走索引的问题
数据量的负载:关系型数据库的负载量很小
需求:专业的搜索引擎工具
Index:索引库
Type:索引类型/索引表
注意:ES从7.0开始取消了这个Type结构
问题:ES中的所有数据在物理上是直接存储在index中,但是逻辑上数据写入某个Index的某个Type中
对于index而言,如果列名相同,会被认为是同一列数据
index:索引库:person
type:索引表:Sudent
sid sname age[string]
type:索引表:Teacher
tid tname age[int]
Document:文档
Fields:数据列
Shard:分片
Replicas:副本
Mapping:列的映射
Setting:配置管理
用于管理index的架构配置管理
管理分片和副本
概念 | HDFS | Hbase | Kafka | ES |
---|---|---|---|---|
第一层划分 | 目录 | NameSpace | - | Index |
第二层划分 | 文件 | Table | Topic | Type【7.0开始没有这个结构】 |
存储分区 | 分块:Block | 范围:Region | 分区:Partition | Shard |
分区安全 | 副本机制 | WAL+副本机制【就近原则】 | 副本机制【leader+Follower】 | 副本机制:Replicas【leader+Follower】 |
存储单元 | -【行】 | Rowkey+Store【列】 | Segment【行:offset】 | docld+ data[fields] |
架构 | 主从架构:NN、DN | 主从架构:Master、RegionServer | 主从架构:Crontroler、Broker | 主从架构:Master + Worker |
HA | 两个NN + ZK | 两个Master+ZK | ZK | Zen-discovery |
安装需求
环境准备
先用root用户在三台机器创建ES的用户
useradd itcast
passwd itcast
三台机器创建ES的目录
mkdir -p /export/server/es
chown -R itcast:itcast /export/server/es
三台机器配置itcast用户的sudo权限
visudo
100行左右添加:
itcast ALL=(ALL) NOPASSWD: ALL
以后使用管理员的命令时要加上sudo
sudo vim /etc/profile
三台机器使用itcast用户连接登录
三台机器配置免秘钥
ssh-keygen -t rsa
ssh-copy-id node1
ssh-copy-id node2
ssh-copy-id node3
修改三台资源配置
sudo vi /etc/security/limits.conf
#在文件的末尾添加以下内容,*号不能去掉
* soft nofile 65536
* hard nofile 131072
* soft nproc 4096
* hard nproc 4096
sudo sed -i '/^#DefaultLimitNOFILE=/aDefaultLimitNOFILE=4096' /etc/systemd/system.conf
sudo sed -i '/^#DefaultLimitNPROC=/aDefaultLimitNPROC=4096' /etc/systemd/system.conf
#临时设置
sudo sysctl -w vm.max_map_count=262144
#永久设置
sudo vim /etc/sysctl.d/99-sysctl.conf
#添加这一行
vm.max_map_count=262144
#检查是否成功
sudo sysctl -a | grep "vm.max_map_count"
断开重连所有会话
上传安装包到第一台机器的家目录下
cd ~
rz
tar -zxvf elasticsearch-7.6.1-linux-x86_64.tar.gz -C /export/server/es/
cd /export/server/es/
修改核心配置文件
cd /export/server/es/elasticsearch-7.6.1/
vim config/elasticsearch.yml
cluster.name: itcast-es
node.name: node1
path.data: /export/server/es/elasticsearch-7.6.1/data
path.logs: /export/server/es/elasticsearch-7.6.1/log
network.host: node1
http.port: 9200
discovery.seed_hosts: ["node1", "node2", "node3"]
cluster.initial_master_nodes: ["node1", "node2"]
bootstrap.system_call_filter: false
bootstrap.memory_lock: false
http.cors.enabled: true
http.cors.allow-origin: "*"
修改JVM配置文件
vim config/jvm.options
#22-23行:根据虚拟机的情况,给一半内存
-Xms2g
-Xmx2g
分发
cd /export/server/es/
scp -r elasticsearch-7.6.1/ node2:$PWD
scp -r elasticsearch-7.6.1/ node3:$PWD
修改node2和node3的配置文件中的主机名
启动三台ES
cd /export/server/es/elasticsearch-7.6.1/
/export/server/es/elasticsearch-7.6.1/bin/elasticsearch >>/dev/null 2>&1 &
访问端口:9200
安装依赖环境
下载node js
cd ~
wget https://npm.taobao.org/mirrors/node/v8.1.0/node-v8.1.0-linux-x64.tar.gz
tar -zxvf node-v8.1.0-linux-x64.tar.gz -C /export/server/es/
创建链接
sudo ln -s /export/server/es/node-v8.1.0-linux-x64/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm
sudo ln -s /export/server/es/node-v8.1.0-linux-x64/bin/node /usr/local/bin/node
修改环境变量
sudo vim /etc/profile
export NODE_HOME=/export/server/es/node-v8.1.0-linux-x64
export PATH=:$PATH:$NODE_HOME/bin
source /etc/profile
检查是否安装成功
node -v
npm -v
上传到第一台机器解压
cd ~
rz
tar -zxvf elasticsearch-head-compile-after.tar.gz -C /export/server/es/
修改配置文件
修改Gruntfile.js
cd /export/server/es/elasticsearch-head
vim Gruntfile.js
#93行:ES-head的服务地址和端口
hostname: 'node1',
修改app.js
cd /export/server/es/elasticsearch-head/_site
vim app.js
#4354行
this.base_uri = this.config.base_uri || this.prefs.get("app-base_uri") || "http://node1:9200";
启动
cd /export/server/es/elasticsearch-head/node_modules/grunt/bin/
#后台启动
./grunt server >/dev/null 2>&1 &
#查看进程
netstat -atunlp | grep 9100
访问:http://node1:9100/
创建插件目录
mkdir -p /export/server/es/elasticsearch-7.6.1/plugins/ik
上传解压
cd /export/server/es/elasticsearch-7.6.1/plugins/ik/
rz
unzip elasticsearch-analysis-ik-7.6.1.zip
rm -rf elasticsearch-analysis-ik-7.6.1.zip
分发
cd /export/server/es/elasticsearch-7.6.1/plugins
scp -r ik/ node2:$PWD
scp -r ik/ node3:$PWD
重启ES
直接Kill
/export/server/es/elasticsearch-7.6.1/bin/elasticsearch >>/dev/null 2>&1 &
创建一个文件,要以.es结尾,用VScode打开这个文件
配置一下ES服务端地址
测试分词器
--标准分词
post _analyze
{
"analyzer":"standard",
"text":"我爱你中国"
}
--IK分词
post _analyze
{
"analyzer":"ik_max_word",
"text":"我爱你中国"
}
背景
索引库管理
列举
GET _cat/indices
查询当前所有的索引库
创建job_idx索引库
PUT /job_idx
{
"mappings": {
"properties" : {
"area": {
"type": "text", "store": true, "analyzer": "ik_max_word"},
"exp": {
"type": "text", "store": true, "analyzer": "ik_max_word"},
"edu": {
"type": "keyword", "store": true},
"salary": {
"type": "keyword", "store": true},
"job_type": {
"type": "keyword", "store": true},
"cmp": {
"type": "text", "store": true, "analyzer": "ik_max_word"},
"pv": {
"type": "keyword", "store": true},
"title": {
"type": "text", "store": true, "analyzer": "ik_max_word"},
"jd": {
"type": "text", "store": true, "analyzer": "ik_max_word"}
}
},
"settings" : {
"number_of_shards":5,
"number_of_replicas" : 1
}
}
运行结果
查看
GET /job_idx/_mapping
GET /job_idx/_settings
删除
delete /job_idx
插入
put /index/_doc/doc_id
{
JSON:每一列的数据
}
PUT /job_idx/_doc/29097
{
"area": "深圳-南山区",
"exp": "1年经验",
"edu": "大专以上",
"salary": "6-8千/月",
"job_type": "实习",
"cmp": "乐有家",
"pv": "61.6万人浏览过 / 14人评价 / 113人正在关注",
"title": "桃园 深大销售实习 岗前培训",
"jd": "薪酬待遇】 本科薪酬7500起 大专薪酬6800起 以上无业绩要求,同时享有业绩核算比例55%~80% 人均月收入超1.3万 【岗位职责】 1.爱学习,有耐心: 通过公司系统化培训熟悉房地产基本业务及相关法律、金融知识,不功利服务客户,耐心为客户在房产交易中遇到的各类问题; 2.会聆听,会提问: 详细了解客户的核心诉求,精准匹配合适的产品信息,具备和用户良好的沟通能力,有团队协作意识和服务意识; 3.爱琢磨,善思考: 热衷于用户心理研究,善于从用户数据中提炼用户需求,利用个性化、精细化运营手段,提升用户体验。 【岗位要求】 1.18-26周岁,自考大专以上学历; 2.具有良好的亲和力、理解能力、逻辑协调和沟通能力; 3.积极乐观开朗,为人诚实守信,工作积极主动,注重团队合作; 4.愿意服务于高端客户,并且通过与高端客户面对面沟通有意愿提升自己的综合能力; 5.愿意参加公益活动,具有爱心和感恩之心。 【培养路径】 1.上千堂课程;房产知识、营销知识、交易知识、法律法规、客户维护、目标管理、谈判技巧、心理学、经济学; 2.成长陪伴:一对一的师徒辅导 3.线上自主学习平台:乐有家学院,专业团队制作,每周大咖分享 4.储备及管理课堂: 干部训练营、月度/季度管理培训会 【晋升发展】 营销【精英】发展规划:A1置业顾问-A6资深置业专家 营销【管理】发展规划:(入职次月后就可竞聘) 置业顾问-置业经理-店长-营销副总经理-营销副总裁-营销总裁 内部【竞聘】公司职能岗位:如市场、渠道拓展中心、法务部、按揭经理等都是内部竞聘 【联系人】 黄媚主任15017903212(微信同号)"
}
{
"_index": "job_idx",
"_type": "_doc",
"_id": "29097",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 2,
"failed": 0
},
"_seq_no": 0,
"_primary_term": 1
}
更新
POST /job_idx/_update/29097
{
"doc": {
"salary": "15-20千/月"
}
}
删除
DELETE /job_idx/_doc/29097
BulkLoad
查询方式
doc_id查询
GET /job_idx/_search
{
"query": {
"ids": {
"values": ["46313"]
}
}
}
关键词查询
职位描述中包含销售的数据
GET /job_idx/_search
{
"query": {
"match": {
"jd": "销售"
}
}
}
默认只显示10条,如果要显示多条,可以通过size标签实现
GET /job_idx/_search
{
"query": {
"match": {
"jd": "销售"
}
},
"size":"100"
}
职位描述及岗位名称中包含销售
GET /job_idx/_search
{
"query": {
"multi_match": {
"query": "销售",
"fields": ["title", "jd"]
}
}
}
分页查询
from and size
GET /job_idx/_search
{
"from": 0,
"size": 5,
"query": {
"multi_match": {
"query": "销售",
"fields": ["title", "jd"]
}
}
}
scroll
深分页:第一次查询,会将整个所有数据放在内存中,从第二次开始就从内存中来自动遍历每页的数据
size:每一页显示多少条
优点:自动进行翻页
第一次
GET /job_idx/_search?scroll=1m
{
"query": {
"multi_match": {
"query": "销售",
"fields": ["title", "jd"]
}
},
"size": 100
}
第二次开始
GET _search/scroll?scroll=1m
{
"scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAAdFmZSaUhmZWJvVHphVTA0VWFXWGctLUEAAAAAAAAAGhZ2ZmYya3k4ZVNuNnAzbDBOTEttUV9BAAAAAAAAAB4WZlJpSGZlYm9UemFVMDRVYVdYZy0tQQAAAAAAAAAbFnZmZjJreThlU242cDNsME5MS21RX0EAAAAAAAAAHBZ2ZmYya3k4ZVNuNnAzbDBOTEttUV9B"
}
JobDetail:Java Bean对象,用于存储每一个职位的信息
JobFullTextService:封装的所有增删改查的接口,定义所有方法以及返回值
JobFullTextServiceImpl:真正的实现类,实现了所有增删改查的方法
JobFullTextServiceTest:测试工具类,用于测试实现类的方法是否可用
restHighLevelClient = new RestHighLevelClient(RestClient.builder(
new HttpHost("node1", 9200, "http")
, new HttpHost("node2", 9200, "http")
, new HttpHost("node3", 9200, "http")
));
写入数据*
调用index方法来写入ES
构建IndexRequest对象
//往ES中写入一条数据:JSON格式的数据
@Override
public void add(JobDetail jobDetail) {
//构建了一个索引请求器对象,用于写入,制定了请求器请求的索引库的名称
IndexRequest indexRequest = new IndexRequest(JOB_IDX_NAME);
//封装数据到请求器中:doc_Id + JSON数据
//从参数的对象中获取id作为docId
indexRequest.id(jobDetail.getId() +"");
//将JavaBean对象转换为JSON字符串
String jsonString = JSON.toJSONString(jobDetail);
//将JSON数据加载到请求器中,指定数据为JSON格式
indexRequest.source(jsonString, XContentType.JSON);
try {
//客户端连接调用index方法实现写入:第一个参数是索引请求器
restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printStackTrace();
}
}
小结
RestHighLevelClient :客户端连接对象
IndexRequst :写入请求器对象 new该对象的时候里面放索引库名称
调用get方法来查询某个docid对应的数据
构建GetRequest对象
//用于通过docId来查找数据
@Override
public JobDetail findById(long id) throws IOException {
//构建一个Get请求器对象
GetRequest getRequest = new GetRequest(JOB_IDX_NAME);
//指定get请求器的docid
getRequest.id(id+"");
//客户端连接对象调用get方法来获取某个docId对应的数据:传递get请求器
GetResponse documentFields = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
//将每一列对应的数据取出转换为JSON String
String sourceAsString = documentFields.getSourceAsString();
//将JSONString转换为JavaBean独享
JobDetail jobDetail = JSON.parseObject(sourceAsString, JobDetail.class);
return jobDetail;
}
小结
更新数据
调用update方法来实现
构建UpdateRequest对象
//实现更新,将新的数据替换老的数据
@Override
public void update(JobDetail jobDetail) throws IOException {
//todo:1-先判断是否存在
GetRequest getRequest = new GetRequest(JOB_IDX_NAME);
getRequest.id(jobDetail.getId()+"");
//判断是否存在
boolean exists = restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
//如果不存在,方法调用结束
if(!exists) return;
//构建更新请求器:索引库名 + 指定更新的docid
UpdateRequest updateRequest = new UpdateRequest(JOB_IDX_NAME,jobDetail.getId()+"");
//更新请求器加载新数据
updateRequest.doc(JSON.toJSONString(jobDetail),XContentType.JSON);
//调用客户端连接中的更新方法
restHighLevelClient.update(updateRequest,RequestOptions.DEFAULT);
}
删除数据
调用delete方法来实现
构建DeleteRequeset对象
//定义删除功能
@Override
public void deleteById(long id) throws IOException {
//构建删除请求器
DeleteRequest deleteRequest = new DeleteRequest(JOB_IDX_NAME);
//添加指定删除的docid
deleteRequest.id(id+"");
//调用删除方法
restHighLevelClient.delete(deleteRequest,RequestOptions.DEFAULT);
}
小结:
调用search方法
@Override
public List<JobDetail> searchByKeywords(String keywords) throws IOException {
//todo:1-构建返回值对象
List<JobDetail> lists = new ArrayList<>();
//todo:2-根据搜索词查询符合的数据
//构建Search的请求器
SearchRequest searchRequest = new SearchRequest();
//构建条件的建造器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//构建一个符合需求的查询器
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keywords, "jd", "title");
// MatchQueryBuilder c1 = QueryBuilders.matchQuery(keywords, "jd");//关键词单列查询器,模糊查询,做分词
// TermQueryBuilder c2 = QueryBuilders.termQuery(keywords, "jd");//关键词单列查询器,精准查询,不做分词
// RangeQueryBuilder c3 = QueryBuilders.rangeQuery("age").gt("18").lt("30");//范围查询
// QueryBuilders.boolQuery().must(c2).must(c3).should(c1); //must代表并列,should表示或者,多条件组合查询
//条件建造器加载查询器
searchSourceBuilder.query(multiMatchQueryBuilder).size(100);
//加载查询的条件
searchRequest.source(searchSourceBuilder);
//调用客户端连接的search方法,实现查询
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHit[] hits = search.getHits().getHits();
for (SearchHit hit : hits) {
//获取每一条数据,将数据的内容转换为JAVA Bean
JobDetail jobDetail = JSON.parseObject(hit.getSourceAsString(), JobDetail.class);
//将docId设置为id
jobDetail.setId(Long.parseLong(hit.getId()));
lists.add(jobDetail);
}
//todo:3-实现返回
return lists;
}
重点内容:查询器的构建
小结
构建返回值对象 List
RestHighLevelClient:客户端连接对象
SearchRequset :构建Search 请求器
SearchSourceBuilder : 构建条件的建造器
QueryBuilders.multiMatchQuery :构建一个符合需求的查询器
查询器构建
QueryBuilders.multiMatchQuery:多列模糊匹配查询
QueryBuilders.matchQuery:单列模糊匹配
QueryBuilders.termQuery:单列精准匹配
QueryBuilders.rangeQuery:范围匹配
QueryBuilders.boolQuery:条件查询器
条件建造器加载查询器 => 建造器对象.query(查询器).size(查询显示数)
加载查询条件 => 请求器对象.source(建造器对象)
调用客户端的search方法 => 客户端对象.search(请求器对象,RequestOptions.DEFAULT) =>返回值为SearchResponse对象
在调用SearchResponse对象的getHits方法,在调用getHits方法,得到所有数据内容为hit
使用for循环遍历,使用JSON的parseObject方法 (在使用hit.getSourceAsString,javabean.class)
在添加到List集合里
浅分页查询
from and size
/**
* 实现浅分页查询
* @param keywords :查询的关键词
* @param pageNum : 就是从第几条开始查询,from
* @param pageSize :每页显示几条,size
* @return
* @throws IOException
*/
@Override
public Map<String, Object> searchByPage(String keywords, int pageNum, int pageSize) throws IOException {
SearchRequest searchRequest = new SearchRequest();
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery(keywords, "jd", "title");
//建造器中指定查询器,指定from和size
searchSourceBuilder.query(multiMatchQueryBuilder)
.from(pageNum)
.size(pageSize);
searchRequest.source(searchSourceBuilder);
SearchResponse search = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
//取的是第一层hits
SearchHits hits = search.getHits();
List<JobDetail> lists = new ArrayList<>();
for (SearchHit hit : hits) {
JobDetail jobDetail = JSON.parseObject(hit.getSourceAsString(), JobDetail.class);
jobDetail.setId(Long.parseLong(hit.getId()));
lists.add(jobDetail);
}
//构建返回值:返回值为Map集合
Map<String, Object> result = new HashMap<>();
//第一条数据:Key:total,Value:返回值的总条数
result.put("total", hits.getTotalHits().value);
//第二条数据:Key:content,Value:每条数据的List集合
result.put("content", lists);
return result;
}
深分页查询
/**
* 测试深分页
* @param keywords :查询关键词
* @param scrollId : 内存数据的id,第一次是没有,第二次开始根据这个id进行自动分页查询
* @param pageSize :每页的大小
* @return
* @throws IOException
*/
@Override
public Map<String, Object> searchByScrollPage(String keywords, String scrollId, int pageSize) throws IOException {
//构建返回值
Map<String, Object> result = new HashMap<>();
List<JobDetail> jobList = new ArrayList<>();
try {
SearchResponse searchResponse = null;
//如果为null,这是第一次请求
if(scrollId == null) {
// 1. 创建搜索请求
SearchRequest searchRequest = new SearchRequest(JOB_IDX_NAME);
// 2. 构建查询条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.multiMatchQuery(keywords, "title", "jd"));
// 3. 设置分页大小
searchSourceBuilder.size(pageSize);
// 4. 设置查询条件、并设置滚动快照有效时间
searchRequest.source(searchSourceBuilder);
//指定数据在内存中放置时间
searchRequest.scroll(TimeValue.timeValueMinutes(1));
// 5. 发起请求
//提交查询器
searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
}
//不是第一次,直接根据scrollid来查询
else {
//构建深分页查询器,传递scrollid
SearchScrollRequest searchScrollRequest = new SearchScrollRequest(scrollId);
searchScrollRequest.scroll(TimeValue.timeValueMinutes(1));
//调用scroll实现深分页
searchResponse = restHighLevelClient.scroll(searchScrollRequest, RequestOptions.DEFAULT);
}
// 6. 迭代响应结果
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
JobDetail jobDetail = JSONObject.parseObject(hit.getSourceAsString(), JobDetail.class);
jobDetail.setId(Long.parseLong(hit.getId()));
jobList.add(jobDetail);
}
//第一条数据:数据的呃逆荣
result.put("content", jobList);
//第二条数据;scrollid
result.put("scroll_id", searchResponse.getScrollId());
}
catch (IOException e) {
e.printStackTrace();
}
return result;
}
插入:
查询:
更新 :更新得先判断是否存在该数据,
删除:
关键词查询:(别人)
RestHighLevelClient:客户端连接对象
SearchRequest:查询请求器
SearchSourceBuilder:查询建造器
查询器构建
QueryBuilders.multiMatchQuery:多列模糊匹配查询
QueryBuilders.matchQuery:单列模糊匹配
QueryBuilders.termQuery:单列精准匹配
QueryBuilders.rangeQuery:范围匹配
QueryBuilders.boolQuery:条件查询器
(自己总结)
构建返回值对象 List
RestHighLevelClient:客户端连接对象
SearchRequset :构建Search 请求器
SearchSourceBuilder : 构建条件的建造器
QueryBuilders :查询器
.multiMatchQuery :构建一个符合需求的查询器
查询器构建
QueryBuilders.multiMatchQuery:多列模糊匹配查询
QueryBuilders.matchQuery:单列模糊匹配
QueryBuilders.termQuery:单列精准匹配
QueryBuilders.rangeQuery:范围匹配
QueryBuilders.boolQuery:条件查询器
条件建造器加载查询器 => 建造器对象.query(查询器).size(查询显示数)
加载查询条件 => 请求器对象.source(建造器对象)
调用客户端的search方法 => 客户端对象.search(请求器对象,RequestOptions.DEFAULT) =>返回值为SearchResponse对象
在调用SearchResponse对象的getHits方法,在调用getHits方法,得到所有数据内容为hit
使用for循环遍历,使用JSON的parseObject方法 (在使用hit.getSourceAsString,javabean.class)
在添加到List集合里
Master:主节点,负责接收客户端请求,实现集群管理和数据存储的功能
Worker:从节点,负责存储数据,接收客户端请求
Coordinator Node:中心调度节点
举例
step1:客户端请求第三台提交读取一条数据,第三台机器作为中心调度节点
读的方式一:直接读取某个docid的数据
读到方式二:根据search来做query查询
需求
对于所有ES中存储的数据,需要进行增删改查的处理的需求,使用SQL最方便能快速的进行开发和上手
查询所有职位信息
GET /_sql?format=txt
{
"query": "SELECT * FROM job_idx limit 1"
}
转换为DSL
GET /_sql/translate
{
"query": "SELECT * FROM job_idx limit 1"
}
scroll分页
--第一次
GET /_sql?format=json
{
"query": "SELECT * FROM job_idx",
"fetch_size": 10
}
--第二次开始
GET /_sql?format=json
{
"cursor": "5/WuAwFaAXPkAURuRjFaWEo1VkdobGJrWmxkR05vQlFBQUFBQUFBQUF5RmpoSGVUUnZhWHBQVW5BMlpVbEhkV1JqVDNKYVdtY0FBQUFBQUFBQVB4WjZiM2xXTlhrNWIxRkVWMHR6ZVhGNVZsWlpNMmhSQUFBQUFBQUFBREVXT0VkNU5HOXBlazlTY0RabFNVZDFaR05QY2xwYVp3QUFBQUFBQUFCQUZucHZlVlkxZVRsdlVVUlhTM041Y1hsV1Zsa3phRkVBQUFBQUFBQUFNeFpYWTBWclRFaENTVkk1ZVVKYVNFeE5aR1ZKY1hKbv8PCQFmBGFyZWEBBGFyZWEBBHRleHQAAAABZgNjbXABA2NtcAEEdGV4dAAAAAFmA2VkdQEDZWR1AQdrZXl3b3JkAQAAAWYDZXhwAQNleHABBHRleHQAAAABZgJqZAECamQBBHRleHQAAAABZghqb2JfdHlwZQEIam9iX3R5cGUBB2tleXdvcmQBAAABZgJwdgECcHYBB2tleXdvcmQBAAABZgZzYWxhcnkBBnNhbGFyeQEHa2V5d29yZAEAAAFmBXRpdGxlAQV0aXRsZQEEdGV4dAAAAAL/AQ=="
}
--关闭
POST /_sql/close
{
"cursor": "5/WuAwFaAXPkAURuRjFaWEo1VkdobGJrWmxkR05vQlFBQUFBQUFBQUF5RmpoSGVUUnZhWHBQVW5BMlpVbEhkV1JqVDNKYVdtY0FBQUFBQUFBQVB4WjZiM2xXTlhrNWIxRkVWMHR6ZVhGNVZsWlpNMmhSQUFBQUFBQUFBREVXT0VkNU5HOXBlazlTY0RabFNVZDFaR05QY2xwYVp3QUFBQUFBQUFCQUZucHZlVlkxZVRsdlVVUlhTM041Y1hsV1Zsa3phRkVBQUFBQUFBQUFNeFpYWTBWclRFaENTVkk1ZVVKYVNFeE5aR1ZKY1hKbv8PCQFmBGFyZWEBBGFyZWEBBHRleHQAAAABZgNjbXABA2NtcAEEdGV4dAAAAAFmA2VkdQEDZWR1AQdrZXl3b3JkAQAAAWYDZXhwAQNleHABBHRleHQAAAABZgJqZAECamQBBHRleHQAAAABZghqb2JfdHlwZQEIam9iX3R5cGUBB2tleXdvcmQBAAABZgJwdgECcHYBB2tleXdvcmQBAAABZgZzYWxhcnkBBnNhbGFyeQEHa2V5d29yZAEAAAFmBXRpdGxlAQV0aXRsZQEEdGV4dAAAAAL/AQ=="
}
条件检索
GET /_sql?format=txt
{
"query": "select * from job_idx where MATCH(title, 'hadoop') or MATCH(jd, 'hadoop') limit 10"
}
创建索引
PUT /order_idx/
{
"mappings": {
"properties": {
"id": {
"type": "keyword",
"store": true
},
"status": {
"type": "keyword",
"store": true
},
"pay_money": {
"type": "double",
"store": true
},
"payway": {
"type": "byte",
"store": true
},
"userid": {
"type": "keyword",
"store": true
},
"operation_date": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss",
"store": true
},
"category": {
"type": "keyword",
"store": true
}
}
}
}
上传并导入测试数据
curl -H "Content-Type: application/json" -XPOST "node1:9200/order_idx/_bulk?pretty&refresh" --data-binary "@order_data.json"
基于DSL:统计每种支付方式的订单个数
GET /order_idx/_search
{
"size": 0,
"aggs": {
"group_by_state": {
"terms": {
"field": "payway"
}
}
}
}
基于SQL:统计每种支付方式的订单个数
GET /_sql?format=txt
{
"query": "select payway, count(*) as order_cnt from order_idx group by payway"
}
基于SQL:统计每个用户的订单个数和订单总金额
GET /_sql?format=txt
{
"query": "select userid, count(1) as cnt, sum(pay_money) as total_money from order_idx group by userid"
}
介绍:Beats:Elastic Stack中的一个组件,轻量级的数据采集的工具
应用:专门用于实现轻量级的文件采集
功能:实现基于文件的数据采集
特点 | FileBeat | Flume | Logstash |
---|---|---|---|
功能 | 只能采集文件 | 采集文件和网络端口 | 文件、数据库、HDFS、Redis、网络 |
组件 | Input、Output | Source、Channel、SInk | Input、Filter、Output |
语言 | Go | Java | Jruby |
优点 | 轻量级,消耗资源最少 | 对于大数据开发接口的完美支持,性能比较好 | 全场景 |
缺点 | 功能比较少 | 特殊需求需要自定义开发,占用的资源比较多 | 性能比价差,占用资源非常多,设计关注于数据的过滤处理 |
应用 | 专门用于文件采集 | 大数据系统中的网络和文件采集 | 专门用于ES的数据采集,实现ETL,搭配Beats使用 |
对于大数据工程而言
安装
使用文档:https://www.elastic.co/guide/en/beats/filebeat/current/index.html
上传
cd ~
rz
解压
tar -zxvf filebeat-7.6.1-linux-x86_64.tar.gz -C /export/server/es/
开发规则
采集/home/itcast目录下所有以server.log开头的日志文件数据写入ES中
数据源
[
开发
创建配置文件
cd /export/server/es/filebeat-7.6.1-linux-x86_64/
mkdir apps
vim apps/logfile_to_es.filebeat
定义input和output
filebeat.inputs:
- type: log
enabled: true
paths:
- /home/itcast/server.log.*
output.elasticsearch:
hosts: ["node1:9200", "node2:9200", "node3:9200"]
上传数据
测试运行
修改权限
chmod go-w /export/server/es/filebeat-7.6.1-linux-x86_64/apps/logfile_to_es.filebeat
运行
./filebeat -c apps/logfile_to_es.filebeat -e
查看结果
查看数据
GET /filebeat-7.6.1-2021.07.03-000001/_search
实时导入数据:添加新的文件,观察是否能实时的采集
小结
导入错误数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0KigGZEY-1625579351323)(Elastic_Stack.assets/image-20210703102356129.png)]
查看
GET /filebeat-7.6.1-2021.07.03-000001/_search
{
"query": {
"match": {
"log.file.path": "/home/itcast/server.log.error"
}
}
}
问题
错误日志被拆分为多行记录在ES中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q3TNLeY2-1625579351324)(Elastic_Stack.assets/image-20210603102101286.png)]
原因:filebeat将文件中每一行作为ES中的一行
实际需求:需要将错误日志的多行作为一条错误信息存储在ES中
解决
开发
vim apps/logfile_regex_to_es.filebeat
filebeat.inputs:
- type: log
enabled: true
paths:
- /home/itcast/server.log.*
multiline.pattern: '^\['
multiline.negate: true
multiline.match: after
output.elasticsearch:
hosts: ["node1:9200", "node2:9200", "node3:9200"]
测试运行
删除FileBeat的元数据
rm -rf data/registry/filebeat/*
删除之前的数据ES中的数据
delete /filebeat-7.6.1-2021.07.03-000001
运行
chmod go-w /export/server/es/filebeat-7.6.1-linux-x86_64/apps/logfile_regex_to_es.filebeat
./filebeat -c apps/logfile_regex_to_es.filebeat -e
查看ES
GET /filebeat-7.6.1-2021.06.03-000001/_search
{
"query": {
"match": {
"log.file.path": "/home/itcast/server.log.error"
}
}
}
安装
上传
cd ~
rz
解压
unzip logstash-7.6.1.zip -d /export/server/es/
cd /export/server/es/logstash-7.6.1/
开发规则
需求:监听用户的输入,将用户的输入在命令行直接输出
开发
编辑文件
cd /export/server/es/logstash-7.6.1/
mkdir apps
vim apps/stdin-stdout.json
写入配置
input {
stdin { }
}
output {
stdout {}
}
测试
bin/logstash -f apps/stdin-stdout.json
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sdZMqThc-1625579351324)(Elastic_Stack.assets/image-20210603104331618.png)]
需求:实现Logstash采集文件输出到stdout
开发
vim apps/input-file-test.json
input{
file{
path => "/home/itcast/tomcat.log"
type => "log"
start_position => "beginning"
}
}
output{
stdout{
codec=>rubydebug
}
}
测试
bin/logstash -f apps/input-file-test.json
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XJvTrUUh-1625579351325)(Elastic_Stack.assets/image-20210603104848220.png)]
需求:Logstash采集MySQL输出到stdout
创建MySQL数据表
create database test;
use test;
CREATE TABLE `wcresult` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`word` varchar(100) NOT NULL,
`number` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
插入数据
insert into wcresult values(null,'hadoop',8);
insert into wcresult values(null,'hive',16);
全量开发
vim apps/input-jdbc1.json
input {
jdbc {
jdbc_driver_library => "/home/itcast/mysql-connector-java-5.1.38.jar"
jdbc_driver_class => "com.mysql.jdbc.Driver"
jdbc_connection_string => "jdbc:mysql://node3:3306/test"
jdbc_user => "root"
jdbc_password => "123456"
schedule => "*/1 * * * *"
statement => "SELECT * from wcresult where number > 10;"
}
}
output{
stdout{
codec=>rubydebug
}
}
全量测试
bin/logstash -f apps/input-jdbc1.json
增量开发
vim apps/input-jdbc2.json
input {
jdbc {
jdbc_driver_library => "/home/itcast/mysql-connector-java-5.1.38.jar"
jdbc_driver_class => "com.mysql.jdbc.Driver"
jdbc_connection_string => "jdbc:mysql://node3:3306/test"
jdbc_user => "root"
jdbc_password => "123456"
use_column_value => true
tracking_column => "id"
schedule => "*/1 * * * *"
statement => "SELECT * from wcresult where number > 10 and id > :sql_last_value;"
}
}
output{
stdout{
codec=>rubydebug
}
}
增量测试
rm -rf /home/itcast/.logstash_jdbc_last_run
bin/logstash -f apps/input-jdbc2.json
需求:先用fileBeat采集文件,写入Logstash,再用Logstash写入ES
数据格式
90.224.57.84 --ip地址
-
-
[15/Apr/2020:00:27:19 +0800] --访问时间
"POST --请求类型
/report --请求页面路径
HTTP/1.1" --协议
404 --返回状态
21 --返回字节大小
"www.baidu.com" --请求地址
"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.104 Safari/537.36 Core/1.53.4549.400 QQBrowser/9.7.12900" --浏览器信息
开发
step1:使用filebeat采集数据到Logstash
cd /export/server/es/filebeat-7.6.1-linux-x86_64/
vim apps/logfile_to_logstash.filebeat
filebeat.inputs:
- type: log
enabled: true
paths:
- /home/itcast/access.*
multiline.pattern: '^\d+\.\d+\.\d+\.\d+ '
multiline.negate: true
multiline.match: after
output.logstash:
enabled: true
hosts: ["node1:45454"]
chmod go-w apps/logfile_to_logstash.filebeat
step2:使用Logstash将数据采集到ES
cd /export/server/es/logstash-7.6.1/
vim apps/beats_to_es.json
input {
beats {
port => 45454
}
}
output {
elasticsearch {
hosts => [ "node1:9200","node2:9200","node3:9200"]
}
}
测试
删除元数据
rm -rf /export/server/es/filebeat-7.6.1-linux-x86_64/data/registry/filebeat/*
启动Logstash
bin/logstash -f apps/beats_to_es.json
启动Beats
./filebeat -c apps/logfile_to_logstash.filebeat -e
上传测试数据access.log
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ea1eiWR8-1625579351325)(Elastic_Stack.assets/image-20210603112354181.png)]
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xCB6r2kG-1625579351325)(Elastic_Stack.assets/image-20210603112342267.png)]
Logstash插件
列举所有插件
bin/logstash-plugin list
GROK插件
功能:正则匹配,从原始数据中将字段提取出来
语法
%{SYNTAX:SEMANTIC}
需求:匹配日志中的所有常见字段,将每个字段提取写入ES
开发
vim apps/beats_grokall_console.json
input { beats { port => 45454 }}filter { grok { match => { "message" => "%{IP:ip} - - \[%{HTTPDATE:date}\] \"%{WORD:method} %{PATH:uri} %{DATA}\" %{INT:status} %{INT:length} \"%{DATA:reference}\" \"%{DATA:browser}\"" } } }output { stdout { codec => rubydebug }}
测试
启动Logstash
bin/logstash -f apps/beats_grokall_console.json
删除元数据
rm -rf /export/server/es/filebeat-7.6.1-linux-x86_64/data/registry/filebeat/*
启动Filebeat
./filebeat -c apps/logfile_to_logstash.filebeat -e
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E54VMCYG-1625579351326)(Elastic_Stack.assets/image-20210603113021364.png)]
插件
需求:转换日期格式并保留需要的字段写入ES
开发
vim apps/beats_mutate_date_es.json
input { beats { port => 45454 }}filter { grok { match => { "message" => "%{IP:ip} - - \[%{HTTPDATE:date}\] \"%{WORD:method} %{PATH:uri} %{DATA}\" %{INT:status:int} %{INT:length:int} \"%{DATA:reference}\" \"%{DATA:browser}\"" } } mutate { enable_metric => "false" remove_field => ["message", "log", "tags", "@timestamp", "input", "agent", "host", "ecs", "@version"] } date { match => ["date","dd/MMM/yyyy:HH:mm:ss Z","yyyy-MM-dd HH:mm:ss"] target => "date" }}output { stdout { codec => rubydebug } elasticsearch { hosts => ["node1:9200" ,"node2:9200" ,"node3:9200"] index => "apache_web_log" }}
测试
启动Logstash
bin/logstash -f apps/beats_mutate_date_es.json
删除元数据
rm -rf /export/server/es/filebeat-7.6.1-linux-x86_64/data/registry/filebeat/*
启动Filebeat
./filebeat -c apps/logfile_to_logstash.filebeat -e
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vR1Qn53T-1625579351326)(Elastic_Stack.assets/image-20210603113853249.png)]
采集用户输入输出到文件中
编辑文件
vim apps/output-file.json
input {stdin{}}output { file { path => "/export/servers/es/logstash-6.0.0/usercase/datas/%{+YYYY-MM-dd}-%{host}.txt" codec => line { format => "%{message}" } flush_interval => 0 }}
运行
bin/logstash -f apps/output-file.json
采集用户输入输出到ES中
编辑文件
vim apps/output-es.json
input {stdin{}}output { elasticsearch { hosts => ["node1:9200"] index => "logstash-%{+YYYY.MM.dd}" }}
运行
bin/logstash -f apps/output-es.json
采集文件的数据到Kafka中
编辑文件
vim apps/output-kafka.json
input { file{ path => "/home/es/tomcat.log" type => "log" start_position => "beginning" }}output { kafka { topic_id => "bigdata" bootstrap_servers => "node1:9092,node2:9092,node3:9092" batch_size => 5 }}
运行
bin/logstash -f apps/output-kafka.json
采集kafka数据到Es
编辑文件
vim apps/kafka-es.json
input{
kafka {
group_id => "testLogstash"
auto_offset_reset => "earliest"
topics => ["bigdata"]
bootstrap_servers => "node1:9092,node2:9092,node3:9092"
}
}
output {
elasticsearch {
hosts => ["node1:9200"]
index => "kakfatoes"
}
}
运行
bin/logstash -f apps/kafka-es.json
介绍:专门为ES设计的可视化工具
功能:实现ES的可视化开发、查询、分析及数据报表的可视化
应用:固定搭配ES来使用
安装
上传
cd ~
rz
解压
tar -zxvf kibana-7.6.1-linux-x86_64.tar.gz -C /export/server/es/
cd /export/server/es/kibana-7.6.1-linux-x86_64/
配置
cd /export/server/es/kibana-7.6.1-linux-x86_64/
vim config/kibana.yml
# 7行:Kibana服务地址
server.host: "node1"
# 25行:修改显示名称
server.name: "itcast-kibana"
# 28行:修改es地址
elasticsearch.hosts: ["http://node1:9200"]
启动
cd /export/server/es/kibana-7.6.1-linux-x86_64/
bin/kibana >>/dev/null 2>&1 &
查看
#查看进程
ps -ef | grep node
#页面访问
node1:5601
添加数据源
[
指定条件检索
#查询包含zhihu的请求
*zhihu*
#查询页面不存在的请求
status : 404
#查询请求成功和不存在的请求
status: (404 or 200)
#查询方式为POST请求,并请求成功的日志
status: 200 and method: post
#查询方式为GET成功的请求,并且响应数据大于512的日志
status: 200 and method: get and length > 512
#查询请求成功的且URL为「/itcast.cn」开头的日志:注意:因为/为特殊字符,需要使用反斜杠进行转义
uri: "\/itcast.cn\/*"
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HvccFxdI-1625579351328)(Elastic_Stack.assets/image-20210202145857708.png)]
<repositories>
<repository>
<id>aliyunid>
<url>http://maven.aliyun.com/nexus/content/groups/public/url>
<releases>
<enabled>trueenabled>
releases>
<snapshots>
<enabled>falseenabled>
<updatePolicy>neverupdatePolicy>
snapshots>
repository>
repositories>
<dependencies>
<dependency>
<groupId>org.elasticsearch.clientgroupId>
<artifactId>elasticsearch-rest-high-level-clientartifactId>
<version>7.6.1version>
dependency>
<dependency>
<groupId>org.apache.logging.log4jgroupId>
<artifactId>log4j-coreartifactId>
<version>2.11.1version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.62version>
dependency>
<dependency>
<groupId>junitgroupId>
<artifactId>junitartifactId>
<version>4.12version>
<scope>testscope>
dependency>
<dependency>
<groupId>org.testnggroupId>
<artifactId>testngartifactId>
<version>6.14.3version>
<scope>testscope>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.1version>
<configuration>
<target>1.8target>
<source>1.8source>
configuration>
plugin>
plugins>
build>