Download Elasticsearch官网下载地址
一、下载elasticsearch-7.12.0,上传到linux服务器,解压。
//进入es配置文件夹,修改基础配置:
$ cd elasticsearch-7.12.0/config/
$ vim elasticsearch.yml
修改elasticsearch.yml
配置:
1.集群名称:一个运行elasticsearch
实例称为一个节点,而集群是由一个或者多个拥有相同 cluster.name
配置的节点组成, 它们共同承担数据和负载的压力。当有节点加入集群中或者从集群中移除节点时,集群将会重新平均分布所有的数据
cluster.name: fanqie-es
2.设置节点名称:当一个节点被选举成为 主 节点时, 它将负责管理集群范围内的所有变更,例如增加、删除索引,或者增加、删除节点等。 而主节点并不需要涉及到文档级别的变更和搜索等操作,所以当集群只拥有一个主节点的情况下,即使流量的增加它也不会成为瓶颈。 任何节点都可以成为主节点。我们的示例集群就只有一个节点,所以它同时也成为了主节点
node.name: node-1
注意:如果若存在多个主(master)节点,需要要设置cluster.initial_master_nodes
参数,详见官网 重要参数配置,若设置不当会发生脑裂,集群就会处在丢失数据的危险中。
3、数据文件夹设置(可选)
默认情况下,Elasticsearch
会把插件、日志以及你最重要的数据放在安装目录下。这会带来不幸的事故, 如果你重新安装 Elasticsearch
的时候不小心把安装目录覆盖了。如果你不小心,你就可能把你的全部数据删掉了。
最好的选择就是把你的数据目录配置到安装目录以外的地方, 同样你也可以选择转移你的插件和日志目录,当然你也选择不修改。
更改如下:
#/path/to/data2 是安装目录路径 可以设定到其他地方
path.data: /path/to/data1,/path/to/data2
# Path to log files:
path.logs: /path/to/logs
4、集群引导设置(cluster.initial_master_nodes参数官网说明)
旧版elasticssearch的(遗弃)
- discovery.zen.minimum_master_nodes
Elasticsearch7新增参数,写入候选主节点的节点名称,cluster.initial_master_nodes替换上面的配置
- cluster.initial_master_nodes=[master-node-a,master-node-b,master-node-c]
当你第一次启动一个全新的 Elasticsearch
集群时,有一个集群引导步骤,它决定了在第一次选举中投票的主合格节点集。在开发模式下,发现是没有配置的,这一步由节点自己自动执行。由于这种自动引导本质上是不安全的,当您在生产模式下启动一个全新的集群时,您必须明确列出在第一次选举中应计算其选票的主合格节点。该列表是使用设置来 cluster.initial_master_nodes
设置的。
作用:
静态配置符合主节点的并且可能处于活动状态的候选主节点的服务名称列表。当你第一次启动一个全新的 Elasticsearch 集群时,有一个集群引导步骤,它决定了在第一次选举中投票的主合格节点集,在重新启动集群或向现有集群添加新节点时,不应使用此设置。
如果集群以完全默认的配置运行,那么它将根据在启动后的短时间内发现在同一主机上运行的节点自动引导集群。这意味着默认情况下可以在一台机器上启动多个节点并让它们自动形成一个集群,这对于开发环境和实验非常有用。然而,由于节点可能并不总是足够快地成功地发现彼此,因此不能依赖这种自动引导,也不能在生产部署中使用。风险点提示:
如果您在不同的主机上启动一些Elasticsearch
节点,那么默认情况下它们不会相互发现,并且会在每个主机上形成不同的集群。Elasticsearch
不会在单独的集群形成后将它们合并在一起,即使您随后尝试将所有节点配置为一个集群也是如此。这是因为没有办法在不存在数据丢失风险的情况下将这些单独的集群合并在一起 , 如果你出现这种失误,解决方式参考官网,你应该会哭的。
5、发现和集群形成设置(discovery.seed_hosts)
此设置为老版本设置,旧名称已被弃用,但继续工作以保持向后兼容性,在未来的版本中将删除对旧名称的支持。(遗弃)
- discovery.zen.ping.unicast.hosts
Elasticsearch7新增参数,写入候选主节点的设备地址,来开启服务时就可以被选为主节点
- discovery.seed_hosts: ["127.0.0.1:9300","127.0.0.1:9301"]
- 作用:静态配置符合主节点的并且可能处于活动状态的候选主节点的设备地址列表。
在进入生产环境之前,应该配置两个重要的发现和集群形成设置,以便集群中的节点可以相互发现并选举主节点,他们之间的本质区别:
cluster.initial_master_nodes
:仅在集群首次启动会使用。
discovery.seed_hosts
:每次启动都需要。
5、节点主从以及节点功能设置
在生产环境下,如果不修改elasticsearch
节点的角色信息,在高数据量,高并发的场景下集群容易出现脑裂等问题(一种两个主节点同时存在于一个集群的现象)。
默认情况下,elasticsearch
集群中每个节点都有成为主节点的资格,也都存储数据,还可以提供查询服务。这些功能是由两个属性控制:node.master
和node.data
:
默认情况下这两个属性的值都是true
。下面详细介绍一下这两个属性的含义以及不同组合可以达到的效果。
node.master
:
这个属性表示节点是否具有成为主节点的资格
注意:此属性的值为true,并不意味着这个节点就是主节点。因为真正的主节点,是由多个具有主节点资格的节点进行选举产生的。所以,这个属性只是代表这个节点是不是具有主节点选举资格。node.data
:
这个属性表示节点是否存储数据。
2、四种属性组合
这两个属性可以有四种组合:
(1)这种组合表示这个节点即有成为主节点的资格,又存储数据。这个时候如果某个节点被选举成为了真正的主节点,那么他还要存储数据,这样对于这个节点的压力就比较大了。elasticsearch
默认每个节点都是这样的配置,在测试环境下这样做没问题。实际工作中建议不要这样设置,这样相当于主节点和数据节点的角色混合到一块了。
node.master: true
node.data: true
(2)这种组合表示这个节点没有成为主节点的资格,也就不参与选举,只会存储数据。这个节点我们称为data
(数据)节点。在集群中需要单独设置几个这样的节点负责存储数据,后期提供存储和查询服务。
node.master: false
node.data: true'
(3)这种组合表示这个节点不会存储数据,有成为主节点的资格,可以参与选举,有可能成为真正的主节点。这个节点我们称为master
节点。
node.master: true
node.data: false
(4)这种组合表示这个节点即不会成为主节点,也不会存储数据,这个节点的意义是作为一个client
(客户端)节点,主要是针对海量请求的时候可以进行负载均衡。
node.master: false
node.data: false
默认情况下,每个节点都有成为主节点的资格,也会存储数据,还会处理客户端的请求。在一个生产集群中我们可以对这些节点的职责进行划分。
建议:
(1)集群中设置3台以上的节点作为master
节点【node.master: true node.data: false
】。这些节点只负责成为主节点,维护整个集群的状态。
(2)再根据数据量设置一批data
节点【node.master: false node.data: true
】。这些节点只负责存储数据。
(3)后期提供建立索引和查询索引的服务,如果用户请求比较频繁,这些节点的压力也会比较大。所以在集群中建议再设置一批client
节点【node.master: false node.data: false
】。这些节点只负责处理用户请求,实现请求转发,负载均衡等功能。
(4)配置。
master
节点:普通服务器即可(CPU 内存 消耗一般);
data
节点:主要消耗磁盘,内存;
client
节点:普通服务器即可(如果要进行分组聚合操作的话,建议这个节点内存也分配多一点)。
二、启动es
ES 是不能使用 root 用户来启动,必须创建一个普通用户来安装启动,否则会报错。
#添加普通用户esuser
$ useradd esuser
#可以查看所有的用户的列表
$ cat /etc/passwd
#授权esuser用户对es的最大操作权限
$ chown -R esuser:esuser elasticsearch-7.12.0
#切换到esuser普通用户下
$ su - esuser
后台启动,并输出启动日志到nohup
日志文件(nohup自己随便写都行):
./elasticsearch > nohup &
ok,上面就可以完成一个单机单节点的elasticsearch
服务后台启动了。
如果想实现elasticsearch
集群,可以在同一目录下复制一下elasticsearch-7.12.0
已经配置好的目录就好可以了(服务器资源有限):
#在当前目录下,复制一份elasticsearch-7.12.0
$ cp -r elasticsearch-7.12.0 elasticsearch-7.12.0-two
遇到错误一:单机多节点配置,可能出现的错误:
java.lang.IllegalStateException: failed to obtain node locks, tried [[/usr/local/es_path/data]] with lock id [0]; maybe these locations are not writable or multiple nodes were started without increasing [node.max_local_storage_nodes] (was [1])?
报错原因:
后台已经存在运行elasticsearch
的进程。node.max_local_storage_nodes
这个配置限制了单节点上可以开启的ES存储实例的个数,我们需要开多个实例,因此需要把这个配置写到配置文件中,根据同一台服务器上部署的节点个数,为这个配置赋值为2或者更高,。
遇到错误二:单机双主节点,在初始化的时候报错,两个竞争master
后启动的一直报错:
[node-2] master not discovered yet, this node has not previously joined a bootstrapped (v7+) cluster, and this node must discover master-eligible nodes [node-1, node-2] to bootstrap a cluster: have discovered [{node-2}{tW7GbJF1Qi6zAilVvNKLvw}{mFRhAlJQTx2RUmLvyfAzkQ}{10.100.4.10}{10.100.4.10:9301}{cdfhilmrstw}{ml.machine_memory=8203501568, xpack.installed=true, transform.node=true, ml.max_open_jobs=20, ml.max_jvm_size=1073741824}]; discovery will continue using [10.100.4.10:9200, 10.100.4.10:9201] from hosts providers and [{node-2}{tW7GbJF1Qi6zAilVvNKLvw}{mFRhAlJQTx2RUmLvyfAzkQ}{10.100.4.10}{10.100.4.10:9301}{cdfhilmrstw}{ml.machine_memory=8203501568, xpack.installed=true, transform.node=true, ml.max_open_jobs=20, ml.max_jvm_size=1073741824}] from last-known cluster state; node term 0, last-accepted version 0 in term 0
{"type": "server", "timestamp": "2021-08-03T08:29:13,536+08:00", "level": "WARN", "component": "o.e.c.NodeConnectionsService", "cluster.name": "stamp-es", "node.name": "node-2", "message": "failed to connect to {node-1}{b4nENEqfQzSP9B0LHWZsHg}{J-qzfhihTCii-IHDCklIEg}{10.100.4.10}{10.100.4.10:9300}{cdfhilmrstw}{ml.machine_memory=8203501568, ml.max_open_jobs=20, xpack.installed=true, ml.max_jvm_size=1073741824, transform.node=true} (tried [4921] times)", "cluster.uuid": "-WWW-aT8ThylLVHhEWqDQQ", "node.id": "tW7GbJF1Qi6zAilVvNKLvw" ,
"stacktrace": ["org.elasticsearch.transport.ConnectTransportException: [node-1][10.100.4.10:9300] connect_exception",
"at org.elasticsearch.transport.TcpTransport$ChannelsConnectedListener.onFailure(TcpTransport.java:968) ~[elasticsearch-7.12.0.jar:7.12.0]",
"at org.elasticsearch.action.ActionListener.lambda$toBiConsumer$2(ActionListener.java:202) ~[elasticsearch-7.12.0.jar:7.12.0]",
"at org.elasticsearch.common.concurrent.CompletableContext.lambda$addListener$0(CompletableContext.java:31) ~[elasticsearch-core-7.12.0.jar:7.12.0]",
"at java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:859) ~[?:?]",
"at java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:837) ~[?:?]",
"at java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506) ~[?:?]",
"at java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2152) ~[?:?]",
"at org.elasticsearch.common.concurrent.CompletableContext.completeExceptionally(CompletableContext.java:46) ~[elasticsearch-core-7.12.0.jar:7.12.0]",
"at org.elasticsearch.transport.netty4.Netty4TcpChannel.lambda$addListener$0(Netty4TcpChannel.java:57) ~[?:?]",
"at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:577) ~[?:?]",
"at io.netty.util.concurrent.DefaultPromise.notifyListeners0(DefaultPromise.java:570) ~[?:?]",
"at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:549) ~[?:?]",
"at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:490) ~[?:?]",
"at io.netty.util.concurrent.DefaultPromise.setValue0(DefaultPromise.java:615) ~[?:?]",
"at io.netty.util.concurrent.DefaultPromise.setFailure0(DefaultPromise.java:608) ~[?:?]",
"at io.netty.util.concurrent.DefaultPromise.tryFailure(DefaultPromise.java:117) ~[?:?]",
"at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.fulfillConnectPromise(AbstractNioChannel.java:321) ~[?:?]",
"at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:337) ~[?:?]",
"at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:702) ~[?:?]",
"at io.netty.channel.nio.NioEventLoop.processSelectedKeysPlain(NioEventLoop.java:615) ~[?:?]",
"at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:578) ~[?:?]",
"at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:493) ~[?:?]",
"at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:989) ~[?:?]",
"at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[?:?]",
"at java.lang.Thread.run(Thread.java:832) [?:?]",
"Caused by: io.netty.channel.AbstractChannel$AnnotatedConnectException: 拒绝连接: 10.100.4.10/10.100.4.10:9300",
"Caused by: java.net.ConnectException: 拒绝连接",
"at sun.nio.ch.Net.pollConnect(Native Method) ~[?:?]",
"at sun.nio.ch.Net.pollConnectNow(Net.java:660) ~[?:?]",
"at sun.nio.ch.SocketChannelImpl.finishConnect(SocketChannelImpl.java:875) ~[?:?]",
"at io.netty.channel.socket.nio.NioSocketChannel.doFinishConnect(NioSocketChannel.java:330) ~[?:?]",
"at io.netty.channel.nio.AbstractNioChannel$AbstractNioUnsafe.finishConnect(AbstractNioChannel.java:334) ~[?:?]",
解决方式:
开始我设置discovery.seed_hosts: ["10.100.4.10:9200","10.100.4.10:9201"]
端口是外部访问端口,一直形成不了集群,后来详细看了下启动日志,elasticsearch
设置节点间交互的tcp
端口,默认是9300,然后修改为discovery.seed_hosts: ["10.100.4.10:9300","10.100.4.10:9301"]
,完美启动了,构成集群。如果要自定义内部节点之间沟通端口,使用这个transport.tcp.port:
设置就好了。
完整的elasticsearch .xml
配置如下:
#跨域配置
http.cors.enabled: true
http.cors.allow-origin: "*"
#节点1的配置信息:
#集群名称
cluster.name: elasticsearch
#节点名称
node.name: node-1
#是不是有资格竞选主节点
node.master: true
#是否存储数据
node.data: true
#最大集群节点数
node.max_local_storage_nodes: 3
#端口
http.port: 9201
#内部节点之间沟通端口
transport.tcp.port: 9301
#es7.x 之后新增的配置,写入候选主节点的设备地址,在开启服务后可以被选为主节点
discovery.seed_hosts: ["10.100.4.10:9300","10.100.4.10:9301"]
#es7.x 之后新增的配置,初始化一个新的集群时需要此配置来选举master
cluster.initial_master_nodes: ["node-1", "node-1"]
#数据存储路径
path.data: /usr/local/es_path/logs
#日志存储路径
path.logs: /usr/local/es_path/logs
以上配置如果在多个节点使用,只需要修改端口设置以及节点名称就可以了,其他配置不用动,启动后这两个节点会竞争,其中一个成为主节点。
上面使用Google的
ElasticSearch Head
插件,可以用于数据的浏览查询,节点状态观测,当然还有其他方式安装ElasticSearch Head
,但是我比较懒,这个是最简单的方式。
集群健康值
集群健康值status 字段指示着当前集群在总体上是否工作正常,如上图(集群信息展示),正常状态为绿色。它的三种颜色含义如下:
status | 健康描述 |
---|---|
green |
所有的主分片和副本分片都正常运行。 |
yellow |
所有的主分片都正常运行,但不是所有的副本分片都正常运行。 |
red |
有主分片没能正常运行。 |
上面提到的这些颜色的实际意义。集群的健康状况为yellow
则表示全部主分片都正常运行(集群可以正常服务所有请求),但是副分片没有全部处在正常状态。 也就说如果有索引的3个副本分片都是unassigned
,表示它们都没有被分配到任何节点。 在同一个节点上既保存原始数据又保存副本是没有意义的,因为一旦失去了那个节点,我们也将丢失该节点上的所有副本数据。
通过Kibana可视化工具,监控集群的配置信息、状态信息
Kibana是一个针对Elasticsearch
的开源分析及可视化平台,用来搜索、查看交互存储在Elasticsearch
索引中的数据。使用Kibana
,可以通过各种图表进行高级数据分析及展示。它操作简单,基于浏览器的用户界面,实时显示Elasticsearch
查询动态。设置Kibana
非常简单,几分钟内就可以完成Kibana
安装并启动 Elasticsearch
索引监测,这个安装步骤就不详细介绍了,网上一大堆。
Download Kibana 官网下载地址
安装完成后,我们就可以使用开发工具,对现有的集群获取一些信息操作:
- 集群健康状态查询
#查看集群状态
GET /_cluster/health
#查看集群是否健康
GET /_cat/health?v
#查看集群节点
GET /_cat/nodes
更多命令详见官网: 集群健康
- 信息统计:内存、线程池、GC、CPU等使用情况
节点统计(重点有用)
GET _nodes/stats
集群统计,官网没解释返回数据
GET _cluster/stats
节点是排列在nodes
,以节点的 UUID 作为键名。还显示了节点网络属性的一些信息(比如传输层地址和主机名)。这些值对调试诸如节点未加入集群这类自动发现问题很有用。通常你会发现是端口用错了,或者节点绑定在错误的 IP 地址/网络接口上了。可以又处于解决遇到的错误二。
此命令可以返回线程池信息、jvm信息,Elasticsearch 被配置为当 heap 达到 75% 的时候开始 GC。如果你的节点一直 >= 75%,你的节点正处于 内存压力 状态。这是个危险信号,不远的未来可能就有慢 GC 要出现了。,字段详解见官网: GET _nodes/stats数据返回详解
- 创建一个my_index索引,分片为3,副分片为1
#创建索引
PUT /my_index
{
"settings" : {
"number_of_shards" : 3,
"number_of_replicas" : 1
}
}
- 查看索引大小,按照bytes排序
# | grep -v marvel 表示剔除 Marval 索引
GET /_cat/indices?bytes=b' | sort -rnk8 | grep -v marvel
- 索引统计
#统计 my_index 索引
GET my_index/_stats
#使用逗号分隔索引名可以请求多个索引统计值
GET my_index,another_index/_stats
#使用特定的 _all 可以请求全部索引的统计值
GET _all/_stats
索引可以辨别或验证集群中的热门索引,或者试图找出某些索引比其他索引更快或者更慢的原因。
实践中,节点为中心的统计还是显得更有用些。瓶颈往往是针对整个节点而言,而不是对于单个索引。因为索引一般是分布在多个节点上的,这导致以索引为中心的统计值通常不是很有用,因为它们是从不同环境的物理机器上汇聚的数据。索引为中心的统计作为一个有用的工具可以保留在你的技能表里,但是通常它不会是第一个用的上的工具。详解见官网: 索引统计
索引模板
索引模板,简而言之,是一种复用机制,就像一些项目的开发框架一样,省去了大量的重复,体力劳动。当新建一个 elasticsearch
索引时,自动匹配模板,完成索引的基础部分搭建。在开发中,elasticsearch
很大一部分工作是用来处理日志信息的,比如某公司对于日志处理策略是以日期为名创建每天的日志索引。并且每天的索引映射类型和配置信息都是一样的,只是索引名称改变了。如果手动的创建每天的索引,将会是一件很麻烦的事情。为了解决类似问题,elasticsearch
提供了预先定义的模板进行索引创建,这个模板称作为Index Templates
。通过索引模板可以让类似的索引重用同一个模板。模板只在创建索引时应用。更改模板不会对现有索引产生影响,一部分定义的设置/映射将优先于模板中定义的任何匹配设置/映射。
这一段话可以简单理解类似于咱们需要对Mysql根据时间自动分库分表,每个月创建一个新表的过程。索引模板,就类似于create库时候每个字段的要定义什么类型。当然这仅仅是帮助你去理解什么是索引模板,索引模板并不仅仅只有这么简单的行为。在使用shardingsphere
或者 Mycat
,进行分库分表的时候,所用库表都需要提前创建的,这个就是我为什么研究elasticsearch
原因,因为我不想每次提前就建表。
template for index-pattern
只有匹配 logstash-* 的索引才会应用这个模板。有时候我们会变更 Logstash 的默认索引名称,记住你也得通过 PUT 方法上传可以匹配你自定义索引名的模板。当然,我更建议的做法是,把你自定义的名字放在 "logstash-" 后面,变成 index => "logstash-custom-%{+yyyy.MM.dd}" 这样。
- 查看所有模板
GET _cat/templates/
GET _template // 查看所有模板
GET _template/my_index* // 查看与通配符相匹配的模板
GET _template/my_index1,my_index2 // 查看多个模板
GET _template/my_index // 查看指定模板
- 删除指定模板
DELETE _template/my_index
- 获取模板
HEAD _template/my_index
- 添加模板
PUT _template/my_index
{
"index_patterns": ["my_index*"],
"settings": {
"number_of_shards": 3,
"number_of_replicas": 2
},
"mappings": {
"properties": {
"userId": {
"type": "long"
},
"userName": {
"type": "text"
},
"userDeptName": {
"type": "text",
"analyzer": "ik_smart"
},
"businessType": {
"type": "keyword"
},
"actionTime": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
}
}
}
}
index_patterns
:是索引模式,意思就是当以my_index开头的索引,都会匹配这个模板,如my_index_1
,my_index_20210803
settings
:设置主分片3,复制分片2
mappings
:在这个指定存储数据的映射关系
创建 mapping
时,可以为字符串(专指type
: keyword
) 指定ignore_above
,用来限定字符长度。超过 ignore_above
的字符会被存储,但不会被索引。注意,是字符长度,一个英文字母是一个字符,一个汉字也是一个字符。在动态生成的 mapping
中,keyword
类型会被设置ignore_above: 256
。ignore_above
可以在创建 mapping
时指定。不为关键词的时候,analyzer
可以设定中文或者一段文件,被切分的程度:ik_smart
和ik_max_word
。其中 ik_smart
为最少切分,k_max_word
: 细粒度分词,会穷尽一个语句中所有分词可能。
- 创建所有后,不可能修改分片,但是可以修改索引的副本数
PUT _commodity/_settings
{
"number_of_replicas": 3
}
测试一下
简单创建一个索引并插入数据,不使用模板,ES会自己决定的映射规则:
创建了一个索引为testindex,type为testtype,id为1数据
PUT /testindex/testtype/1
{
"first_name" : "张三",
"last_name" : "法外狂徒张三",
"age" : 25,
"about" : "我是个好人"
}
只创建了索引不插入数据
PUT /testindex2
{
}
索引模板差异比对参考
- 知识点
Elasticsearch 7.x
后移除type
自定义了 ,之前es
将ndex、type
类比于关系型数据库(例如mysql
)中database
、table
,这么考虑的目的是“方便管理数据之间的关系”,后来人们在一个index
中建立很多实体type
,没有相同的字段,会导致数据稀疏,最终结果是干扰了Lucene
有效压缩文档的能力,也就是瞎球用影响ES
的存储、检索效率。所以在自定义索引模板时,你会发现有些博客的模板是无法用的,因为自定义了type
,去掉自定义的type
就可以了。新版本无法使用,现在新版本统一默认_doc
。
Elasticsearch 时间存储后展示问题
毕竟是初次使用Elasticsearch
,这个时间问题,把我坑的不轻,毕竟还是关系型数据库思维,我想存入索引的时间数据是格式化后的数据,但是一直存的是时间戳,曾一度怀疑是版本的问题,后来看见一个帖子才明白,是入参json的问题,java定义的Date类型,不能直接json转化后传入,不然你在索引模板中设置任何date
格式都没用:
如我在上面设置的date
格式,设置索引中的actionTime
:
"actionTime": {
"type": "date",
"format": "yyyy-MM-dd HH:mm:ss"
}
开发工具输入参数
- 正确输入:
POST /my_index/_doc/2
{
"actionTime": "2018-07-29 08:00:00"
}
相应的我们在代码中需要加入以下对时间的注解:
使用JSONField注解,别使用@JsonFormat 它只会在类似@ResponseBody返回json数据的时候,才会返回格式化的yyyy-MM-dd HH:mm:ss时间,
你直接使用System.out.println()输出的话,仍然是类似“Fri Dec 01 21:05:20 CST 2021”这样的时间样式
这个时间会差八个小时,所以别用new Date() 获取时间,使用下面提供的getCurrentDate方式
import com.alibaba.fastjson.annotation.JSONField;
@JSONField(format="yyyy-MM-dd HH:mm:ss")
private Date actionTime;
public static Date getCurrentDate() {
LocalDateTime datetime = LocalDateTime.now(ZoneId.of("Asia/Shanghai"));
ZonedDateTime zdt = datetime.atZone(ZoneId.systemDefault());
return Date.from(zdt.toInstant());
}
谨记:索引模板配置的时间格式一个要与传入的时间格式一致,否则就会报错。
- 错误输入:
PUT my_index/_doc/1
{
"actionTime": 1543151405000
}
这样如果传入一个时间戳格式,会报错时间解析失败:
Caused by: ElasticsearchException[Elasticsearch exception [type=date_time_parse_exception, reason=date_time_parse_exception: Text '1627988093053' could not be parsed at index 0]]
at org.elasticsearch.ElasticsearchException.innerFromXContent(ElasticsearchException.java:485)
at org.elasticsearch.ElasticsearchException.fromXContent(ElasticsearchException.java:396)
at org.elasticsearch.ElasticsearchException.innerFromXContent(ElasticsearchException.java:426)
... 76 more
如果确实需要传入的是个时间戳,不设定format
参数类型就OK了:
"actionTime": {
"type": "date"
}
格式化成功了但是实际上这个字段是Text类型,单纯的用于展示没问题,用来做搜索条件或者间隔查询基本无效。
Dynamic templates说明
Dynamic mapping
这种方式下字段的映射规则基本都是ES自己决定的,它可以解决一部分场景,但有时候ES并不能很好的理解我们的业务数据,这就需要我们像上面写的自己指定属性。给个样式,混个脸熟,反正我是不这样玩。
{
"order": 0,
"version": 60001,
"index_patterns": [
"my_index*"
],
"settings": {
"index": {
"number_of_shards": "5",
"number_of_replicas": 2,
"refresh_interval": "5s"
}
},
"mappings": {
"dynamic_templates": [{
"message_field": {
"path_match": "message",
"mapping": {
"norms": false,
"type": "text"
},
"match_mapping_type": "string"
}
},
{
"string_fields": {
"mapping": {
"norms": false,
"type": "text",
"fields": {
"keyword": {
"ignore_above": 256,
"type": "keyword"
}
}
},
"match_mapping_type": "string",
"match": "*"
}
}
],
"aliases": {}
}
参考:
https://www.cnblogs.com/Neeo/articles/10869231.html
https://www.jianshu.com/p/1f67e4436c37
https://www.cnblogs.com/caoweixiong/p/11791438.html