搜索引擎基础
倒排索引
-
常见搜索引擎
- 网页搜索引擎:百度、谷歌
- 业务搜索引擎:商品搜索、订单搜索
- 日志搜索引擎:运维查看日志
-
应用:用户给定关键词,搜索引擎返回与关键词相关的结果
-
关系:大数据与搜索引擎工程师之间的联系
- 搜索引擎中的数据一般由大数据工程师实现存储
-
过程
-
step1:用户提交搜索词
大数据分析
-
step2:搜索引擎对用户的搜索词做分词
- 搜索引擎会获取用户的输入,调用分词器对用户的搜索词进行分词
- 大数据
- 分析
-
step3:将爬取到的所有网页构建倒排索引,构建倒排索引库
-
将每个网页中的每句话进行分词:将一个网页中所有词拆分出来
网页编号 网页的内容 分词 1 大数据工业化应用 大数据、工业化、应用 2 大数据与人工智能的关系 大数据、人工智能、关系 3 大数据的应用场景 大数据、应用、场景 -
构建倒排索引
词 网页 的id 大数据 1,2,3 应用 1,3 人工智能 2 -
假设用户搜索:大数据分析
- 根据用户查询的分词从倒排索引中返回所有相关的网页的id
- 根据算法对所有网页进行评分排名
- 3:90
- 2:85
- 1:70
-
-
倒排索引概念
- 传统索引:根据id查询内容
- 倒排索引:根据内容查id
-
step4:根据用户的关键词分词的结果到索引库中进行匹配,返回所有相关的词对应的所有网页
-
实现框架
- 传统的存储检索工具
-
如果搜索引擎通过MySQL或者Oracle来实现能不能满足需求?
- 答案:不能
-
查询的条件:走索引的问题
- 需求:不论用户查询的是什么内容,都要走索引返回
- SQL:like %keyword%
-
数据量的负载:关系型数据库的负载量很小
-
需求:专业的搜索引擎工具
-
- 专业的搜索引擎工具
- Lucene:最早的搜索引擎工具
- 优点:奠定了整个搜索索引的框架和核心思想
- 缺点:不好用,接口比较复杂,学习和使用成本比较高,现在几乎看不到
- Solr:基于Lucene封装的一个工具
- 优点:接口相对变的好用了
- 缺点:数据量大的情况,性能会差一些,高度依赖于ZK
- ElasticSearch:吸收了前两代工具的特点,解决了对应的缺点问题
- 接口【JSON】友好,主攻搜索引擎实现,其他所有的功能都通过插件来实现,自己实现集群管理
- 自己实现了类似于ZK的分布式管理机制:Zen-discovery机制
- Lucene:最早的搜索引擎工具
ElasticSearch
介绍
- 基本介绍
- ELK官网:https://www.elastic.co/
- ELK官网文档:https://www.elastic.co/guide/index.html
- ELK中文手册:https://www.elastic.co/guide/cn/elasticsearch/guide/current/index.html
- ELK中文社区:https://elasticsearch.cn/
- Elastic Stack
- ElasticSearch:搜索引擎工具
- 分布式存储、分布式分析计算
- Logstash:用于实现数据采集
- 重量级数据采集,功能比Flume多:文件、数据流、数据库
- 采集的功能非常完善,后来更新发展侧重数据ETL
- Kibana:可视化工具
- 专门为ES设计的可视化工具,搭配ES使用
- Beats:轻量级数据采集
- 功能相对来说单一化,简单并且性能很好
- ElasticSearch:搜索引擎工具
- 应用
- 所有搜索引擎的场景都可以使用Elastic Stack来实现
- 主要的应用场景:日志搜索引擎
- 特点
- 分布式:高可用、扩展性、高性能的
- NRT:近实时的分布式存储系统,数据从写入到读取之间会有1s延迟
- 全文索引:任何东西写入ES以后,都会自动创建索引
- 区别
- Redis:实时、内存、NoSQL数据库、大数据量缓存
- Hbase:实时、内存+磁盘、NoSQL数据库、大数据量持久性
- Kafka:实时、内存+磁盘、消息队列、大数据量缓存
- ES:近实时,内存+磁盘、全文索引、大数据量持久化
存储概念
-
Index:索引库
- 所有数据都是存储在index中,用于构建索引的
- 类似于:数据库或者命名空间的概念
- Redis:数据库
- Hbase:命名空间
-
Type:索引类型/索引表
-
注意:ES从7.0开始取消了这个Type结构
- 6.0开始:每个Index库只允许有一个Type
-
问题:ES中的所有数据在物理上是直接存储在index中,但是逻辑上数据写入某个Index的某个Type中
-
对于index而言,如果列名相同,会被认为是同一列数据
-
index:索引库:person
-
type:索引表:Sudent
sid sname age[string]
-
type:索引表:Teacher
tid tname age[int]
-
-
-
-
Document:文档
- 就是ES中存储的一条数据,每一条数据就是一个document
- 构成:documentId + data【每一列构成】
- docuementId:可以自己指定,唯一标识一行,如果不给,ES会自动生成一个
- 功能
- 作为主键,唯一标识一行,有主键索引
- 决定分区的规则:按照documentID的Hash值取余分区个数
- 功能
- docuementId:可以自己指定,唯一标识一行,如果不给,ES会自动生成一个
- 类比于Hbase
- document = Rowkey的数据
- documentID = rowkey
- data【fields】 = Cell[]
- document作为ES中的存储/索引的最小单元
- 写:按照整个document写入
- 读:索引返回的是整个document的数据
-
Fields:数据列
- 就是以前讲的column,每一列的数据
- 构建索引的最小单元:列,可以指定哪些列构建索引,哪些列不构建索引
-
Shard:分片
- 就是分区,shards是index级别的,一个index可以有多个shard分区,不同的shard分区分布在不同的节点上
- 默认每个index有1个分片
-
Replicas:副本
- 为了保证分片高可用性,默认每个分片有1个副本,分片 + 副本不允许超过机器个数
- 注意:副本的个数不包含分片,如果1个分片有2个副本,总共存储了3份
- 角色
- leader:对外提供读写
- follower:与leader同步数据,如果leader故障,follower会选举新的leader
-
Mapping:列的映射
- 用于实现对于整个Index的列的管理:实现列的定义
- 列:名称、类型、是否构建索引、是否进行分词
-
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 |
小结
- index :索引库 (类似于数据库的概念)
- Type:索引类型/索引表
- Document:文档 (ES中存储的一条数据) = documentld(唯一标识符)+date
- Fields :数据列 (构建索引的最小单元:列)
- Shard :分片(分区,它是index级别的,一个index可以有多个shard分区)
- Repliacs :副本
- Mapping : 列的映射 (用于实现对整个index的列的管理 列 : 名称,类型,是否构建索引,是否进行分词)
- Setting :配置管理 (用于管理index的架构)
ES部署
需求及环境准备
-
安装需求
- ElasticSearch:分布式搜索引擎,三台都有,先装一台,然后分发
- 没有可视化界面
- ElasticSearch-Head:可视化ES工具,第一台机器安装即可
- 类似于Kafka-eagle
- IK分词器:中文解析分词器 ,三台都有
- 默认只带英文分词器,普通中文分词器比较鸡肋,使用IK中文分词器
- ElasticSearch:分布式搜索引擎,三台都有,先装一台,然后分发
-
环境准备
- 严禁使用root用户
-
先用root用户在三台机器创建ES的用户
useradd itcast passwd itcast
- 设置密码: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"
-
断开重连所有会话
ES的分布式安装
-
上传安装包到第一台机器的家目录下
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
es-head的安装
-
安装依赖环境
-
下载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/
IK分词器的安装
-
创建插件目录
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 &
-
VSCode集成ES插件
创建一个文件,要以.es结尾,用VScode打开这个文件
配置一下ES服务端地址
-
测试分词器
--标准分词 post _analyze { "analyzer":"standard", "text":"我爱你中国" } --IK分词 post _analyze { "analyzer":"ik_max_word", "text":"我爱你中国" }
RESTful API
背景及索引构建
-
背景
- 基于招聘网站中招聘信息的数据检索
- 需求:用户可以根据地区、经验、岗位名称、岗位描述等信息快速的检索用户想要的招聘数据
- RestFul API
- 主要应用于DDL
- 创建、删除、修改索引库
- ES基于基于HTTP请求方式来实现的
- PUT:插入或者更新数据
- POST:可以做各种写入操作请求
- GET:读取数据
- DELETE:用于删除
- 都以JSON形式来实现数据的请求管理
- 主要应用于DDL
-
索引库管理
-
列举
GET _cat/indices
-
查询当前所有的索引库
- SQL:show databases
-
-
创建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
-
插入/更新/删除 DDL操作
-
插入
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
-
上传文件到Linux中
-
执行批量加载
curl -H "Content-Type: application/json" -XPOST "node1:9200/job_idx/_bulk?pretty&refresh" --data-binary "@job_info.json"
-
查询 DML操作
-
查询方式
- 根据doc_id进行查询:类似于MySQL中根据主键进行查询
- 类似于Hbase中基于rowkey查询
- 使用查询器进行查询:各种查询器满足各种查询需求
- QueryBuilder:Match、Term、Range
- 根据doc_id进行查询:类似于MySQL中根据主键进行查询
-
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"] } } }
- multi_match:多列字符串匹配查询器
-
-
分页查询
-
from and size
GET /job_idx/_search { "from": 0, "size": 5, "query": { "multi_match": { "query": "销售", "fields": ["title", "jd"] } } }
- 浅分页:类似于SQL中的limit M,N
- from:从第几条开始显示,第一条的值为0
- size:每一页的显示几条
- 每次必须手动修改from的值
- 浅分页:类似于SQL中的limit M,N
-
scroll
-
深分页:第一次查询,会将整个所有数据放在内存中,从第二次开始就从内存中来自动遍历每页的数据
- scroll = 1m :做深分页,将查询的结果保存在内存中1分钟
-
size:每一页显示多少条
-
优点:自动进行翻页
-
第一次
GET /job_idx/_search?scroll=1m { "query": { "multi_match": { "query": "销售", "fields": ["title", "jd"] } }, "size": 100 }
-
第二次开始
GET _search/scroll?scroll=1m { "scroll_id": "DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAAAdFmZSaUhmZWJvVHphVTA0VWFXWGctLUEAAAAAAAAAGhZ2ZmYya3k4ZVNuNnAzbDBOTEttUV9BAAAAAAAAAB4WZlJpSGZlYm9UemFVMDRVYVdYZy0tQQAAAAAAAAAbFnZmZjJreThlU242cDNsME5MS21RX0EAAAAAAAAAHBZ2ZmYya3k4ZVNuNnAzbDBOTEttUV9B" }
-
-
Java API
环境准备
-
JobDetail:Java Bean对象,用于存储每一个职位的信息
- 整个ES的读写都是以JSON形式实现的,不方便处理
- 将数据封装在JobDetail对象,方便处理数据
-
JobFullTextService:封装的所有增删改查的接口,定义所有方法以及返回值
-
JobFullTextServiceImpl:真正的实现类,实现了所有增删改查的方法
-
JobFullTextServiceTest:测试工具类,用于测试实现类的方法是否可用
插入数据
- 构建连接对象 RestHighLevelClient
restHighLevelClient = new RestHighLevelClient(RestClient.builder(
new HttpHost("node1", 9200, "http")
, new HttpHost("node2", 9200, "http")
, new HttpHost("node3", 9200, "http")
));
-
写入数据*
-
调用index方法来写入ES
-
构建IndexRequest对象
- IndexRequest:写入数据必须构建的对象
- .id:设置写入的doc_id
- .source:设置写入的数据列
//往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(); } }
- IndexRequest:写入数据必须构建的对象
-
小结
-
RestHighLevelClient :客户端连接对象
- .index:写入方法 (第一个参数是索引请求器,RequestOptions.DEFAULT)
-
IndexRequst :写入请求器对象 new该对象的时候里面放索引库名称
- .id (里面放docid,类型为string )
- .source (指定数据格式 ,数据 )
查询数据
-
调用get方法来查询某个docid对应的数据
-
构建GetRequest对象
- GetRequest:根据DOCID进行请求,读取某个Doc的数据
//用于通过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; }
小结
- GetRequest :get请求器对象,new的时候里面放索引库名称
- .id :指定请求器的docid
- RestHighLevelClient :客户端连接对象
- .get :按照docId进行查询(第一个参数为get请求器,RequestOptions.DEFAULT)返回的对象类型为GetResponse
- 在使用GetResponse对象的getSourceAsString方法,将数据转换为Json数据
- 使用JSON的parseObject方法把json数据转为javebean对象
更新与删除
-
更新数据
-
调用update方法来实现
-
构建UpdateRequest对象
- 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对象
- DeleteRequest:用于实现删除请求
//定义删除功能 @Override public void deleteById(long id) throws IOException { //构建删除请求器 DeleteRequest deleteRequest = new DeleteRequest(JOB_IDX_NAME); //添加指定删除的docid deleteRequest.id(id+""); //调用删除方法 restHighLevelClient.delete(deleteRequest,RequestOptions.DEFAULT); }
-
小结:
- 更新 :更新得先判断是否存在该数据,
- GetRequest :请求器对象 (索引库名称)
- .id :(指定请求器的docid )
- RestHighLevelClient:客户端连接对象
- .exists : 判断是否存在的方法 (第一参数是GetRequest对象,RequestOptions.DEFAULT)返回一个布尔值
- .update(UpdateRequest):实现更新操作
- UpdateRequest:更新请求器对象 (索引库名称,更新的docId要string类型)
- .doc(JSON字符串的更新数据)
- GetRequest :请求器对象 (索引库名称)
- 删除
- RestHighLevelClient:客户端连接对象
- .delete(DeleteRequest):实现删除操作
- DeleteRequest:删除请求器
- .id(docId)
- RestHighLevelClient:客户端连接对象
关键词查询
-
调用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; }
-
重点内容:查询器的构建
- QueryBuilders
小结
-
构建返回值对象 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; }
-
深分页查询
- 分为两步
- step1:第一次运行
- step2:第二次开始
/** * 测试深分页 * @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 :客户端连接对象
- .index:写入方法 (第一个参数是索引请求器,RequestOptions.DEFAULT)
- IndexRequst :写入请求器对象 new该对象的时候里面放索引库名称
- .id (里面放docid,类型为string )
- .source (指定数据格式 ,数据 )
查询:
- GetRequest :get请求器对象,new的时候里面放索引库名称
- .id :指定请求器的docid
- RestHighLevelClient :客户端连接对象
- .get :按照docId进行查询(第一个参数为get请求器,RequestOptions.DEFAULT)返回的对象类型为GetResponse
- 在使用GetResponse对象的getSourceAsString方法,将数据转换为Json数据
- 使用JSON的parseObject方法把json数据转为javebean对象
更新 :更新得先判断是否存在该数据,
- GetRequest :请求器对象 (索引库名称)
- .id :(指定请求器的docid )
- RestHighLevelClient:客户端连接对象
- .exists : 判断是否存在的方法 (第一参数是GetRequest对象,RequestOptions.DEFAULT)返回一个布尔值
- .update(UpdateRequest):实现更新操作
- UpdateRequest:更新请求器对象 (索引库名称,更新的docId要string类型)
- .doc(JSON字符串的更新数据)
删除:
- RestHighLevelClient:客户端连接对象
- .delete(DeleteRequest):实现删除操作
- DeleteRequest:删除请求器
- .id(docId)
关键词查询:(别人)
-
RestHighLevelClient:客户端连接对象
- .search(SearchRequest)
-
SearchRequest:查询请求器
- .source(SearchSourceBuilder):设置查询建造器
-
SearchSourceBuilder:查询建造器
- .query(查询器):根据不同的需求设置不同的查询器
-
查询器构建
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:中心调度节点
- 谁接收的客户端请求,谁就是中心调度节点
- 负责接收请求,转发处理请求,返回结果给客户端
-
举例
- 三台机器:node1,node2,node3
- index:两个分区,每个分区有两个副本
- shard0:p0 , r0 ,r0
- shard1:p1,r1,r1
写入流程
- step1:客户端请求第一条提交写入一条数据,第一台机器作为中心调度节点
- 解析这个请求,判断是否是一个DDL请求,如果是,转发给Master
- 如果不是,就根据分片规则,查询元数据,将这个请求转发给对应分区所在的primary shard【Leader副本】的节点
- step2:中心调度节点发现这条数据要写入0分区,0分区的主副本在第三台,转发给第三台机器
- step3:第三台机器接受写入请求,将数据写入p0,p0会将这个写入广播给所有r0,超过半数的r0写入成功,就直接返回写入成功
- step4:第三台机器将写入结果返回给中心调度节点,中心调度节点将结果返回给客户端
读取流程
-
step1:客户端请求第三台提交读取一条数据,第三台机器作为中心调度节点
-
读的方式一:直接读取某个docid的数据
- 中心调度节点根据分片规则,将这个请求转发给对应的分区所在的节点
- 对应分区查询这个docid对应的数据进行返回
-
读到方式二:根据search来做query查询
- 查询这个索引库所有分区的数据,将符合条件的结果全部返回到中心调度节点
- 中心调度节点根据评分降序排序返回给客户端
物理存储
- step1:先将数据写入分区对应buffer,内存区域,用于缓冲,这时候没有索引,数据不能被读取
- step2:每隔1s,会将每个分区的buffer数据写入os cache
- 会构建索引
- 写入操作会记录在translog:也是存储在内存中,每隔一段时候同步磁盘
- 现在的数据可以被读取到
- step3:每隔半小时,将数据flush到磁盘文件:segment文件中
- step4:将segment进行合并,将所有被标记删除和更新的数据真正的删除,产生新的segment
ES SQL
需求
需求
对于所有ES中存储的数据,需要进行增删改查的处理的需求,使用SQL最方便能快速的进行开发和上手
设计
- 所有ES的概念可以映射一张分布式的表的概念
- 缺点
- 没有免费或者开源的SQL开发工具
- SQL语法支持的并不全面
测试
-
查询所有职位信息
GET /_sql?format=txt { "query": "SELECT * FROM job_idx limit 1" }
-
转换为DSL
- DSL:将SQL语法转换成每个独立的关键字
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" }
FileBeat
介绍
介绍:Beats:Elastic Stack中的一个组件,轻量级的数据采集的工具
- FileBeat:专门用于实现数据文件采集的工具
- 采集文件数据,类似于Flume:exec、taildir
- PackageBeat:专门用于实现网络监听流量采集工具
- 对网络数据传输的采集
- MetricBeat:专门用于收集服务器性能指标的采集工具
- CPU利用率、内存利用率
- HeartBeat:专门用于监听服务可用性的信息采集工具
- WindowsBeat:专门用于监听windows系统中产生的事件信息的采集工具
应用:专门用于实现轻量级的文件采集
功能:实现基于文件的数据采集
特点 | FileBeat | Flume | Logstash |
---|---|---|---|
功能 | 只能采集文件 | 采集文件和网络端口 | 文件、数据库、HDFS、Redis、网络 |
组件 | Input、Output | Source、Channel、SInk | Input、Filter、Output |
语言 | Go | Java | Jruby |
优点 | 轻量级,消耗资源最少 | 对于大数据开发接口的完美支持,性能比较好 | 全场景 |
缺点 | 功能比较少 | 特殊需求需要自定义开发,占用的资源比较多 | 性能比价差,占用资源非常多,设计关注于数据的过滤处理 |
应用 | 专门用于文件采集 | 大数据系统中的网络和文件采集 | 专门用于ES的数据采集,实现ETL,搭配Beats使用 |
对于大数据工程而言
- 如果只采集文件,需要低消耗高性能:filebeat
- 如果采集分布式网络端口或者文件比较多,比较复杂:Flume
安装以及开发规则
-
安装
-
使用文档: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/
-
-
开发规则
-
基本组件:所有的FileBeat的程序都有两个部分Input和Output组成
-
step1:开发一个配置文件
-
定义Input:读取哪些文件的数据
-
定义Output:将读取到的数据发送到哪个目标地中
-
-
step2:运行文件即可
- filebeat运行开发的配置文件即可
-
案例
- 需求
-
采集/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"]
-
-
上传数据
- 上传测试文件到/home/itcast目录下
-
测试运行
-
修改权限
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
- -c:指定要运行的配置文件
- -e:表示执行
-
-
查看结果
-
查看数据
GET /filebeat-7.6.1-2021.07.03-000001/_search
-
实时导入数据:添加新的文件,观察是否能实时的采集
小结
- 实现FileBeatas案例的测试开发
- 注意:filebeat会自动记录采集的偏移量
解决多行合并问题
导入错误数据
- 导入
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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"]
- multiline.pattern: ‘^[’:中括号开头的数据作为新的一行
- multiline.negate: true:不符合的行进行合并
- multiline.match: after:合并在上一行的后面
-
测试运行
-
删除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" } } }
-
Logstash
介绍
- 介绍:ELK中的L组件:用于实现各种场景下数据采集的功能
- 功能:全场景下的数据采集:核心特点,注重于数据的转换处理
- 应用
- FileBeat:对数据进行采集
- |
- Logstash:对FileBeat传递过来的数据进行处理,ETL
- |
- ElasticSearch:实现数据的存储和分析
- |
- Kibana:实现可视化报表和可视化分析
- 特点
- 优点:功能全面,可以实现数据的预处理,支持全场景的数据采集
- 缺点:侧重于数据处理,性能较差,开发接口不友好,现在一般不用于做数据采集工具
安装及开发规则
-
安装
-
上传
cd ~ rz
-
解压
unzip logstash-7.6.1.zip -d /export/server/es/ cd /export/server/es/logstash-7.6.1/
-
-
开发规则
- 文档:https://www.elastic.co/guide/en/logstash/current/index.html
- 基本规则:所有的Logstash的程序都可以有三个部分Input、Filter【可选】、Output组成
- step1:先开发一个文件,定义Input、Filter、Output
- Input:定义从什么地方读取
- https://www.elastic.co/guide/en/logstash/current/input-plugins.html
- Filter:可选的,实现对Input的数据进行预处理
- https://www.elastic.co/guide/en/logstash/current/filter-plugins.html
- Output:定义将数据保存到什么地方
- https://www.elastic.co/guide/en/logstash/current/output-plugins.html
- Input:定义从什么地方读取
- step2:提交运行文件即可
采集stdin输出到stdout (监听用户的输入)
-
需求:监听用户的输入,将用户的输入在命令行直接输出
-
开发
-
编辑文件
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
- 选项:三个选项只能使用一个
- -f:指定运行某个文件
- -e:在命名行执行代码
- -t:测试代码的语法是否正确
- 选项:三个选项只能使用一个
-
结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sdZMqThc-1625579351324)(Elastic_Stack.assets/image-20210603104331618.png)]
采集文件输出搭配stdout
-
需求:实现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)]
采集MySQL输出到stdout
-
需求: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
采集FileBeats输出到ES
-
需求:先用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)]
实现数据转换
-
插件
- mutate插件功能:实现字段添加、处理、重命名等
- date插件功能:实现日期转换处理
-
需求:转换日期格式并保留需要的字段写入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
-
Kibana
介绍
-
介绍:专门为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)]
构建可视化报表
附录一:Maven依赖
<repositories>
<repository>
<id>aliyun</id>
<url>http://maven.aliyun.com/nexus/content/groups/public/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
<updatePolicy>never</updatePolicy>
</snapshots>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.6.1</version>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.62</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.14.3</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<target>1.8</target>
<source>1.8</source>
</configuration>
</plugin>
</plugins>
</build>