docker pull elasticsearch:7.4.2
docker pull kibana:7.4.2
mkdir -p /mydata/elasticsearch/config
mkdir -p /mydata/elasticsearch/data
echo "http.host: 0.0.0.0" >> /mydata/elasticsearch/config/elasticsearch.yml
chmod -R 777 /mydata/elasticsearch/
docker run --name elasticsearch -p 9200:9200 -p 9300:9300 \
-e "discovery.type=single-node" \
-e ES_JAVA_OPTS="-Xms64m -Xmx512m" \
-v /mydata/elasticsearch/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml \
-v /mydata/elasticsearch/data:/usr/share/elasticsearch/data \
-v /mydata/elasticsearch/plugins:/usr/share/elasticsearch/plugins \
-d elasticsearch:7.4.2
docker run --name kibana -e ELASTICSEARCH_HOSTS=http://192.168.56.10:9200 -p 5601:5601 \
-d kibana:7.4.2
注:启动kibana的ip地址要换成自己的ip
打开192.168.56.10:5601,进入kibana,打开下面的界面,这个界面就是执行api的界面,因为elasticsearch是对 Lucene 的封装,提供了 REST API 的操作接口,在不接触底层原理的情况下,学习es就是针对api的学习。
参照mysql中概念一一对应:
elasticsearch | mysql |
---|---|
index(索引) | database(库) |
type(类型) | table(表) |
document(文档) | row(每一行数据) |
mysql中的索引是针对字段来的,类似主键索引是对主键id建立一个索引,然后去查数据。
elasticsearch是对导入的数据建立索引,将数据分词,正对每一个词建立索引,搜索的时候包含的词越多相关度也越高。
在打开的kibana中调试输入,查看效果
#查看所有节点
GET /_cat/nodes
#查看 es 健康状况
GET /_cat/health
#查看主节点
GET /_cat/master
#查看所有索引
GET /_cat/indices
#customer是索引,external是类型,1是唯一标识,这个请求的意思就是保存一个数据,内容是下如下
PUT customer/external/1
{
"name": "John Doe"
}
执行返回结果
{
"_index" : "customer", #表示索引名称
"_type" : "external", #表示类型名称
"_id" : "1", #文档唯一标识,在PUT方式下需要指定id,在POST方式下不需要指定,会随机生成
"_version" : 1, #版本号,再次点击版本号会累加
"result" : "created", #操作结果,create表示新增,updated表示修改,再次点击就是修改
"_shards" : { #包含关于分片执行的信息,后续介绍
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0, #表示文档的序列号。用于实现乐观并发控制
"_primary_term" : 1 #表示文档所在分片的主要分片数。用于实现乐观并发控制
}
#和put一样,就是不需要指定id,会随机生成一个
POST customer/external
{
"name": "John Doe"
}
执行返回结果
{
"_index" : "customer",
"_type" : "external",
"_id" : "i-4HNYwBxXNzdWrbY4el", #会返回一个随机的id
"_version" : 1, #再次点击不会新增版本号,每次都是新增一个
"result" : "created", #每次点击都是created
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 2, #并发控制字段,每次更新就会+1,用来做乐观锁
"_primary_term" : 1
}
#?if_seq_no=0&if_primary_term=1这一段是指在当前分片中进行并发控制,只有seq_no=0才执行修改
PUT customer/external/1?if_seq_no=0&if_primary_term=1
{
"name": "John Doe"
}
PUT 和 POST 都可以,
POST 新增。如果不指定 id,会自动生成 id。指定 id 就会修改这个数据,并新增版本号
PUT 可以新增可以修改。PUT 必须指定 id;由于 PUT 需要指定 id,我们一般都用来做修改
操作,不指定 id 会报错。
GET customer/external/1
执行返回结果
{
"_index" : "customer",
"_type" : "external",
"_id" : "1",
"_version" : 3,
"_seq_no" : 4, #并发控制字段,每次更新就会+1,用来做乐观锁
"_primary_term" : 1,
"found" : true,
"_source" : { #这里面就是之前存入的值
"name" : "John Doe"
}
}
#会对比原来数据与原来一样就什么都不做,version和seq_no都不变
POST customer/external/1/_update
{
"doc":{
"name": "John Doew"
}
}
或者
#不检查原来的数据,直接更新数据,version和seq_no都会变
POST customer/external/1
{
"name": "John Doe2"
}
或者
#和上一个一样,不检查原来的数据,直接更新数据,version和seq_no都会变
PUT customer/external/1
{
"name": "John Doe"
}
POST customer/external/1/_update
{
"doc": {
"name": "Jane Doe",
"age": 20
}
}
总结:
看场景;
对于大并发更新,不带 update;
对于大并发查询偶尔更新,带 update;对比更新,重新计算分配规则
#删除这个索引下的这个类型下的数据
DELETE customer/external/1
#删除整个索引
DELETE customer
POST customer/external/_bulk
{"index":{"_id":"1"}} #一行操作
{"name": "John Doe" } #一行数据
{"index":{"_id":"2"}}
{"name": "Jane Doe" }
执行返回结果
{
"took" : 53, #花费了多少毫秒
"errors" : false, #标识没有发生错误
"items" : [ #多个数据之间相互不影响,及时第一个发生报错也不影响后面的操作
{
"index" : { #index表示新增如果存在就替换,create也表示新增如果存在操作失败,update表示批量更新更新,delete表示批量删除
"_index" : "customer", #索引
"_type" : "external", #类型
"_id" : "1", #唯一标识
"_version" : 8, #版本号
"result" : "updated", #表示原来存在,批量操作后更新
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 10,
"_primary_term" : 1,
"status" : 200
}
},
{
"index" : {
"_index" : "customer",
"_type" : "external",
"_id" : "2",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 11,
"_primary_term" : 1,
"status" : 201
}
}
]
}
POST /_bulk
{ "delete": { "_index": "website", "_type": "blog", "_id": "123" }} #表示删除website索引下blog这个类型id为123的数据
{ "create": { "_index": "website", "_type": "blog", "_id": "123" }} #表示新增website索引下blog这个类型id为123的数据
{ "title": "My first blog post" } #新增的数据就是这个一行
{ "index": { "_index": "website", "_type": "blog" }} #存在就替换上面新增的数据
{ "title": "My second blog post" }
{ "update": { "_index": "website", "_type": "blog", "_id": "123"} } #更新上面新增的数据值为后面一行的内容
{ "doc" : {"title" : "My updated blog post"} }
执行返回结果
{
"took" : 353,
"errors" : false,
"items" : [
{
"delete" : { #表示删除操作
"_index" : "website",
"_type" : "blog",
"_id" : "123",
"_version" : 1,
"result" : "not_found", #表示没有找到要删除的值
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1,
"status" : 404
}
},
{
"create" : { #表示新增操作
"_index" : "website",
"_type" : "blog",
"_id" : "123",
"_version" : 2,
"result" : "created", #新增成功
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 1,
"_primary_term" : 1,
"status" : 201
}
},
{
"index" : { #也是表示新增操作
"_index" : "website",
"_type" : "blog",
"_id" : "ju6BNYwBxXNzdWrbzYfj",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 2,
"_primary_term" : 1,
"status" : 201
}
},
{
"update" : { #表示更新操作
"_index" : "website",
"_type" : "blog",
"_id" : "123",
"_version" : 3,
"result" : "updated", #更新了上面由create创建的id为123的数据
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 3,
"_primary_term" : 1,
"status" : 200
}
}
]
}
这里可以去下载批量导入的数据
https://wws.lanzoui.com/iaoMKppmpzc
#这里批量导入bank这个索引account这个类型下数据
POST /bank/account/_bulk
。。。上面链接上下载的数据
#bank就是之前批量导入进去的索引,_search是固定写法表示查询,?之后都是参数q=*表示查询所有,sort=account_number:asc查询到的数据按照account_number这个字段升序排序
GET bank/_search?q=*&sort=account_number:asc
GET bank/_search
{
"query": { #查询条件
"match_all": {} #查询所有
},
"from":0, #和mysql中的limit类似,从第几条开始
"size":5, #展示多少条
"sort": [ #排序条件
{
"account_number": {
"order": "desc" #这个字段升序排序
}
},
{
"balance":{
"order":"asc"
}
}
],
"_source":["age","balance"] #表示查询结果只返回这几个字段
}
执行返回结果
{
"took" : 385, #执行耗时
"timed_out" : false, #是否超时
"_shards" : { #分片信息
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : { #命中的记录
"total" : {
"value" : 1000, #表示有多少条记录被匹配到了,虽然这里匹配了1000条但是es只给我们展示了10条类似分页查询
"relation" : "eq" #检索的关系,eq就是等于
},
"max_score" : null, #得分,本次查找是查全文不存在相关性得分
"hits" : [
{
"_index" : "bank",
"_type" : "account",
"_id" : "0",
"_score" : null,
"_source" : { #当时保存记录的元信息
"account_number" : 0,
"balance" : 16623,
"firstname" : "Bradshaw",
"lastname" : "Mckenzie",
"age" : 29,
"gender" : "F",
"address" : "244 Columbus Place",
"employer" : "Euron",
"email" : "[email protected]",
"city" : "Hobucken",
"state" : "CO"
},
"sort" : [ #排序,第一个
0
]
}
]
}
}
GET bank/_search
{
"query": {
"match": { #模糊匹配,只要包含这个字段就展示
"account_number": "20" #指定某个字段的值,类似sql中的where后的查询条件
"address":"Kings" #可以模糊查询,只要包含Kings的都会查出来
},
"match_phrase":{ #短语匹配,必须包含这个短语才展示
"address":"mill road" #只有包含mill road这个短语才匹配
},
"multi_match": { #多字段匹配,可以选择多个字段包含指定的值
"query": "mill road", #表示我要查询包含 mill 或者 road的数据
"fields": ["city","address"] #city字段或者address字段包含mill 或者 road都展示出来
}
}
}
执行返回结果
{
"took" : 9,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.0, #下面数字中最大的得分
"hits" : [
{
"_index" : "bank",
"_type" : "account",
"_id" : "20",
"_score" : 1.0, #数字越大表示相关性越高,数据展示就是按照这个字段排序
"_source" : {
"account_number" : 20,
"balance" : 16418,
"firstname" : "Elinor",
"lastname" : "Ratliff",
"age" : 36,
"gender" : "M",
"address" : "282 Kings Place",
"employer" : "Scentric",
"email" : "[email protected]",
"city" : "Ribera",
"state" : "WA"
}
}
]
}
}
bool 用来做复合查询:
复合语句可以合并 任何 其它查询语句,包括复合语句,了解这一点是很重要的。这就意味着,复合语句之间可以互相嵌套,可以表达非常复杂的逻辑。
结果过滤filter:
并不是所有的查询都需要产生分数,特别是那些仅用于 “filtering”(过滤)的文档。为了不计算分数 Elasticsearch 会自动检查场景并且优化查询的执行。
GET bank/_search
{
"query": {
"bool": { #表示复合查询,并且可以相互嵌套
"must": [ #表示必须匹配
{"match": {"address": "mill"}},
{"match": {"gender": "M"}},
{"range": {"age":{"gte":20,"lte":30}}} #表示查询年龄范围的数据
],
"should": [ #表示应该匹配,不匹配也会展示,匹配的话_score会更高
{"match": {"address": "lane"}}
],
"must_not": [ #表示必须不匹配
{"match": {"email": "baluba.com"}}
],
"filter": { #和must的区分是filter不计算_score相关性得分
"range": {
"age": {
"gte": 20,
"lte": 30
}
}
}
}
}
}
GET bank/_search
{
"query": {
"term": { #用于查询精确的值,不适用文本检索,因为文本在存入的时候会进行数据分析(分词等)操作
"age":20
}
}
}
GET bank/_search
{
"query": {
"match": {
"address.keyword":"McDonald" #字段后面加上.keyword表示只address的值等于McDonald不会模糊查询
}
}
}
聚合提供了从数据中分组和提取数据的能力。最简单的聚合方法大致等于SQL GROUPBY 和 SQL 聚合函数。在 Elasticsearch 中,您有执行搜索返回 hits(命中结果),并且同时返回聚合结果,把一个响应中的所有 hits(命中结果)分隔开的能力。这是非常强大且有效的,您可以执行查询和多个聚合,并且在一次使用中得到各自的(任何一个的)返回结果,使用一次简洁和简化的 API 来避免网络往返。
#搜索 address 中包含 mill 的所有人的年龄分布以及平均年龄,但不显示这些人的详情
GET bank/_search
{
"query": { #这个先查询出所有address里包含mill的数据
"match": {
"address": "mill"
}
},
"aggs": { #每次需要分组聚合查询的时候都需要加上aggs
"ageAgg": { #这个是可以自己命名的
"terms": { #是一种聚合方式,表示统计分布,每个年龄占多少
"field": "age", #统计age字段
"size": 10 #限制分组数量,只展示10个分组
}
},
"avgAgg":{ #自己定义的
"avg": { #一种聚合方式,求平均值
"field": "age" #表示根据age的平均值
}
},
"balanceAgg":{ #自己定义的
"avg": { #一种聚合方式,求平均值
"field": "balance" #表示根据balance的平均值
}
}
},
"size": 0 #为0表示不展示搜索出来的数据,仅展示聚合结果,如下
}
执行返回结果
{
"took" : 19,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 4,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ] #size=0的时候hits就是空的
},
"aggregations" : { #聚合操作后的数据都在这里
"ageAgg" : { #自己定义的结果
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : 38, #表示年龄是38的有2个
"doc_count" : 2
},
{
"key" : 28,
"doc_count" : 1
},
{
"key" : 32,
"doc_count" : 1
}
]
},
"balanceAgg" : { #自己定义的结果
"value" : 25208.0
},
"avgAgg" : { #自己定义的结果
"value" : 34.0
}
}
}
再列举一些更复杂的操作
#按照年龄聚合,并且请求这些年龄段的这些人的平均薪资
GET bank/_search
{
"query": {
"match_all": {}
},
"aggs": {
"ageAgg": { #自己定义的
"terms": { #聚合操作,按照age分组取1000个
"field": "age",
"size": 1000
},
"aggs": { #在上面的值中继续聚合操作,嵌套在里面
"ageAvg": {
"avg": { #按照balance求平均值
"field": "balance"
}
}
}
}
}
}
聚合操作有很多上面使用了Terms Aggregation和Average Aggregation,其他的可以看看,学习学习
这个映射就和mysql中的DDL语句类似,用于创建、查看索引的字段的类型
**注意:**只能创建和查看,不能修改已经存在的字段的类型,如果想修改只能用数据迁移
#查看bank索引下所有字段的类型
GET /bank/_mapping
执行结果返回
{
"bank" : { #索引名
"mappings" : {
"properties" : {
"account_number" : { #字段名
"type" : "long" #字段类型
},
"address" : {
"type" : "text",
"fields" : { #address字段的子类型
"keyword" : { #表示可以使用address.keyword做全词匹配
"type" : "keyword",
"ignore_above" : 256
}
}
},
"age" : { #下面都是类似。。。。
"type" : "long"
},
"balance" : {
"type" : "long"
},
"city" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"email" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"employer" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"firstname" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"gender" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"lastname" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
},
"state" : {
"type" : "text",
"fields" : {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
}
注:es7及以上移除了type的概念,统一替换成了_doc,也就是你创建一个索引后,默认跟这这个
elasticsearch的字段类型有很多,下面列举一二
PUT /my-index
{
"mappings": {
"properties": {
"age": { "type": "integer" },
"email": { "type": "keyword" },
"name": { "type": "text" }
}
}
}
PUT /my-index/_mapping
{
"properties": {
"employee-id": {
"type": "keyword",
"index": false #默认为true,false表示该字段内容不会被索引,不可搜索
}
}
}
对于已经存在的映射字段,我们不能更新。更新必须创建新的索引进行数据迁移
可以先创建新的索引
PUT /newbank
{
"mappings": {
"properties": {
"age": { "type": "integer" },
"email": { "type": "keyword" },
"name": { "type": "text" }
}
}
}
然后将老的数据迁移到新的索引下
POST _reindex
{
"source": {
"index": "bank", #老的索引名
"type": "account" #es7之前有type这里就要写上,如果没有type这里就不写
},
"dest": {
"index": "newbank" #这里就是新的索引名
}
}
什么是分词,es在创建索引的时候会对数据内容进行分词,如果你的数据中有大量中文需要检索,使用默认的分词器效果非常不好,因为es是外国佬干出来的,默认的分词工具对中文并不友好,所以需要安装中文的分词器。
因为原文使用的es版本是7.4.2,所以IK分词器的版本也要与之对应,IK分词器下载地址:https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.4.2/elasticsearch-analysis-ik-7.4.2.zip
#下载到linux上/mydata/elasticsearch/plugins目录下(因为已经和容器内的目录挂载了)
#没安装unzip的自行安装
unzip elasticsearch-analysis-ik-7.4.2.zip
#切记解压完要删除elasticsearch-analysis-ik-7.4.2.zip,不然后面会报错
rm -f elasticsearch-analysis-ik-7.4.2.zip
#创建ik目录,然后把解压的文件都放进取
mkdir ik
...
#都移入后进入重启es
docker restart elasticsearch
#进入容器内部
docker exec -it elasticsearch /bin/bash
#切换到bin
cd bin
#检查ik是否安装成功
elasticsearch-plugin list
#显示ik说明成功
#默认分词
POST _analyze
{
"text": "这个B班谁爱上谁上"
}
执行返回结果
#都是一个一个字拆分的
{
"tokens" : [
{
"token" : "这",
"start_offset" : 0,
"end_offset" : 1,
"type" : "" ,
"position" : 0
},
{
"token" : "个",
"start_offset" : 1,
"end_offset" : 2,
"type" : "" ,
"position" : 1
},
{
"token" : "b",
"start_offset" : 2,
"end_offset" : 3,
"type" : "" ,
"position" : 2
},
{
"token" : "班",
"start_offset" : 3,
"end_offset" : 4,
"type" : "" ,
"position" : 3
},
{
"token" : "谁",
"start_offset" : 4,
"end_offset" : 5,
"type" : "" ,
"position" : 4
},
{
"token" : "爱",
"start_offset" : 5,
"end_offset" : 6,
"type" : "" ,
"position" : 5
},
{
"token" : "上",
"start_offset" : 6,
"end_offset" : 7,
"type" : "" ,
"position" : 6
},
{
"token" : "谁",
"start_offset" : 7,
"end_offset" : 8,
"type" : "" ,
"position" : 7
},
{
"token" : "上",
"start_offset" : 8,
"end_offset" : 9,
"type" : "" ,
"position" : 8
}
]
}
POST _analyze
{
"analyzer": "ik_max_word", #这个替换成"analyzer": "ik_smart", 也是可以的
"text": "这个B班谁爱上谁上"
}
执行返回结果
{
"tokens" : [
{
"token" : "这个",
"start_offset" : 0,
"end_offset" : 2,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "b",
"start_offset" : 2,
"end_offset" : 3,
"type" : "ENGLISH",
"position" : 1
},
{
"token" : "班",
"start_offset" : 3,
"end_offset" : 4,
"type" : "CN_CHAR",
"position" : 2
},
{
"token" : "谁",
"start_offset" : 4,
"end_offset" : 5,
"type" : "CN_CHAR",
"position" : 3
},
{
"token" : "爱上",
"start_offset" : 5,
"end_offset" : 7,
"type" : "CN_WORD",
"position" : 4
},
{
"token" : "谁上",
"start_offset" : 7,
"end_offset" : 9,
"type" : "CN_WORD",
"position" : 5
}
]
}
#ik分词配置目录
cd /mydata/elasticsearch/plugins/ik/config
#修改IKAnalyzer.cfg.xml
vi IKAnalyzer.cfg.xml
#可以配置本地字典、远程字段等,这个字典可以是txt文本形式,然后把指定文字输入其中,分词的时候就会根据这个去创建倒排索引了
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 扩展配置</comment>
<!--用户可以在这里配置自己的扩展字典 -->
<entry key="ext_dict"></entry>
<!--用户可以在这里配置自己的扩展停止词字典-->
<entry key="ext_stopwords"></entry>
<!--用户可以在这里配置远程扩展字典 -->
<entry key="remote_ext_dict">http://192.168.56.10/es/fenci.txt</entry>
<!--用户可以在这里配置远程扩展停止词字典-->
<!-- <entry key="remote_ext_stopwords">words_location</entry> -->
</properties>
#下载镜像
docker pull logstash:7.4.2
#新建logstash启动配置文件依旧是mydata文件夹下
mkdir logstash
#然后依次创建如下文件夹并修改权限
#这个文件夹下的所有文件都加一下权限
chmod -R 777 logstash
#在config下增加logstash.conf、logstash.yml、pipelines.yml
vi logstash.yml
#修改成如下内容
node.name: logstash-1
path.logs: /usr/share/logstash/logs
config.test_and_exit: false
config.reload.automatic: false
config.reload.interval: 60s
config.debug: true
log.level: debug
http.host: 0.0.0.0
######################分割线#######################
vi pipelines.yml
#修改成如下内容
- pipeline.id: main
path.config: /usr/share/logstash/config/logstash.conf
######################分割线#######################
vi logstash.conf
#修改成如下内容
input{
#这个jdbc是做全量更新,表示logstash启动后执行一次,用sql查询语句查出数据全部导入es
jdbc {
#指定mysql驱动位置,这个位置是指docker容器内部的位置
jdbc_driver_library => "/usr/share/logstash/driver/mysql-connector-java-8.0.30.jar"
jdbc_driver_class => "com.mysql.cj.jdbc.Driver"
#监听的mysql地址
jdbc_connection_string => "jdbc:mysql://192.168.17.98:3306/es_test?serverTimezone=Asia/Shanghai&useSSL=true&useUnicode=true&characterEncoding=UTF-8"
jdbc_user => "root"
jdbc_password => "root"
jdbc_paging_enabled => true
jdbc_page_size => "50000"
#全量查询的sql文件保存地址,这个文件里的内容就是一行sql:SELECT * FROM 表名
statement_filepath => "/usr/share/logstash/pipeline/all.sql"
}
#这个是增量更新
jdbc {
jdbc_driver_library => "/usr/share/logstash/driver/mysql-connector-java-8.0.30.jar"
jdbc_driver_class => "com.mysql.cj.jdbc.Driver"
jdbc_connection_string => "jdbc:mysql://192.168.17.98:3306/es_test?serverTimezone=Asia/Shanghai&useSSL=true&useUnicode=true&characterEncoding=UTF-8"
jdbc_user => "root"
jdbc_password => "root"
jdbc_paging_enabled => true
jdbc_page_size => "50000"
#增量sql文件保存地址,文件内容也是一行sql:SELECT * FROM document WHERE update_time > :sql_last_value
statement_filepath => "/usr/share/logstash/pipeline/incr.sql"
#表示开启监听字段的功能
use_column_value => true
#表示执行的频率,全*默认就是一分钟一次
schedule => "* * * * *"
#指定要监听的数据库字段
tracking_column => "update_time"
}
}
output {
#设置es目标地址
elasticsearch {
hosts => ["192.168.56.10:9200"]
#要导入的索引是哪个
index => "document"
#索引id取数据库的哪个字段
document_id => "%{id}"
}
#输出到控制台,使用docker logs logstash可以看到打印的信息
stdout {
codec => json_lines
}
}
配置好这些后重启docker,在你的表中加入一些数据,然后查看容器日志,再查询索引中的数据是否更新上来
引入依赖,版本最好和你使用的es版本相同
<dependency>
<groupId>org.elasticsearch.clientgroupId>
<artifactId>elasticsearch-rest-high-level-clientartifactId>
<version>7.4.2version>
dependency>
配置类
@Configuration
public class ElasticsearchConfig {
public static final RequestOptions COMMON_OPTIONS;
static {
RequestOptions.Builder builder = RequestOptions.DEFAULT.toBuilder();
COMMON_OPTIONS = builder.build();
}
@Bean
public RestHighLevelClient restHighLevelClient() {
return new RestHighLevelClient(RestClient.builder(new HttpHost("192.168.56.10", 9200, "http")));
}
}
测试类
@SpringBootTest
@RunWith(SpringJUnit4ClassRunner.class)
@Slf4j
public class ESTest {
@Resource
private DocumentMapper documentMapper;
@Resource
private RestHighLevelClient client;
/**
* 查询数据库数据批量导入es
* @throws IOException
*/
@Test
public void runTest() throws IOException {
List<Document> documents = documentMapper.selectList(null);
BulkRequest bulkRequest = new BulkRequest();
documents.forEach(document -> {
bulkRequest.add(new IndexRequest("document").id(document.getId()).source(JSON.toJSONString(document), XContentType.JSON));
});
BulkResponse bulk = client.bulk(bulkRequest, ElasticsearchConfig.COMMON_OPTIONS);
System.out.println(JSON.toJSONString(bulk));
}
/**
* 查询es数据批量导入数据库
* @throws IOException
*/
@Test
public void runTest2() throws IOException {
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("document");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//只取50条数据导入数据库
sourceBuilder.size(50);
searchRequest.source(sourceBuilder);
SearchResponse search = client.search(searchRequest, ElasticsearchConfig.COMMON_OPTIONS);
System.out.println(JSON.toJSONString(search));
SearchHit[] hits = search.getHits().getHits();
for (SearchHit hit : hits) {
Document document = JSON.parseObject(hit.getSourceAsString(), Document.class);
documentMapper.insert(document);
}
}
}
这里使用官方封装的api便捷操作es