我刚打酱油去了,不好意思

Elastic:菜鸟上手指南

你好,我是 Elastic 的刘晓国, Elastic 认证工程师,阿里云最有价值专家。如果大家想开始学习 Elastic 的话,那么这里将是你理想的学习园地。在我的博客几乎涵盖了你想学习的许多方面,从初级到高级的方方面面。你如果真的想学习 Elastic Stack 的话,那么你所需要的就是这一篇文章就够了。在这里,我来讲述一下作为一个菜鸟该如何阅读我的这些博客文章。如果你真心喜欢我的文章,请不惜点个赞!让我们一起更好地学习!如果你想按照视频学习,请观看我的文章 “Elastic:培训视频 ”,或者直接在 B 站观看。在这里的所列举的文字只是我写出来文章的一部分,但是足以让你对 Elastic Stack 有一个全面的了解。如果你想阅读所有关于 Elastic Stack 的文章,请参阅链接 Elastic。

除了我这里文章之外,Elastic 社区还定期开展一些 meetup 活动。敬请访问我们的网站 https://www.bagevent.com/org/738410
 
详细阅读,请参阅 https://elasticstack.blog.csdn ... 28604
继续阅读 »
你好,我是 Elastic 的刘晓国, Elastic 认证工程师,阿里云最有价值专家。如果大家想开始学习 Elastic 的话,那么这里将是你理想的学习园地。在我的博客几乎涵盖了你想学习的许多方面,从初级到高级的方方面面。你如果真的想学习 Elastic Stack 的话,那么你所需要的就是这一篇文章就够了。在这里,我来讲述一下作为一个菜鸟该如何阅读我的这些博客文章。如果你真心喜欢我的文章,请不惜点个赞!让我们一起更好地学习!如果你想按照视频学习,请观看我的文章 “Elastic:培训视频 ”,或者直接在 B 站观看。在这里的所列举的文字只是我写出来文章的一部分,但是足以让你对 Elastic Stack 有一个全面的了解。如果你想阅读所有关于 Elastic Stack 的文章,请参阅链接 Elastic。

除了我这里文章之外,Elastic 社区还定期开展一些 meetup 活动。敬请访问我们的网站 https://www.bagevent.com/org/738410
 
详细阅读,请参阅 https://elasticstack.blog.csdn ... 28604 收起阅读 »

Elastic 社区会议将于2月27日线上举行 + 征稿延长到 20121年1月 22日

Wow!距一月份仅两个星期,我们已经收到 100份 ElasticCC (Elastic Community Conference) 演示文稿,其中20份在 APJ(亚太/日本)中,近40份在 EMEA(欧洲/中东/非洲)和 NASA(北/南美洲)。这是一个了不起的开始,我们还有更多的空间。由于我们知道假期后开始工作的艰辛,因此我们将征稿(CfP)延长至1月22日(星期五)UTC 23:59 详细阅读,请参阅 https://elasticstack.blog.csdn ... 99156
继续阅读 »
Wow!距一月份仅两个星期,我们已经收到 100份 ElasticCC (Elastic Community Conference) 演示文稿,其中20份在 APJ(亚太/日本)中,近40份在 EMEA(欧洲/中东/非洲)和 NASA(北/南美洲)。这是一个了不起的开始,我们还有更多的空间。由于我们知道假期后开始工作的艰辛,因此我们将征稿(CfP)延长至1月22日(星期五)UTC 23:59 详细阅读,请参阅 https://elasticstack.blog.csdn ... 99156 收起阅读 »

INFINI Gateway 的使用方法和使用心得分享

本文主要分享一下大神Medcl开源的INFINI Gateway的使用方法和使用心得,当然可能由于研究得不够深入某些地方理解肤浅甚至说的根本就是错的也请Medcl大神和各位专家大佬能够多多包涵,下面详细对我使用INFINI Gateway的经验进行一下分享。
安装部署
安装部署非常简单,INFINI Gateway使用go语言进行编写,linux系统基本不需要安装任何其他依赖,直接从github上下载解压即可使用,由于需要反复重启调试,同时网关需要具备进程保护功能,我使用了supervisord进行纳管,supervisord安装部署我在此省略,有兴趣的同学直接网上搜索即可。Supervisord配置文件如下:
[program:gateway]
command = /app/logger/gateway/gateway-linux64
username = appuser
autostart=false
autorestart=true
startsecs=3
priority=1007
stdout_logfile=/app/logger/gateway/log/infini_gateway.log
注意这里由于我的测试服务器kibana、gateway和es节点都部署在一起,统一都是用supervisord进行纳管,由于网关必须等到es节点启动以后才可以启动成功,所以将gateway 的autostart设置为false。另外网关节点由于流量、资源不足等问题会有一定风险自己挂掉(测试过程中就遇到过),所以推荐将autorestart设置为true,当网关节点意外挂掉以后能够马上重启不影响应用使用。
配置文件
这块可能是我比较重点想分享的地方,因为可能由于目前项目刚刚开源,medcl大神一直忙于功能开发无暇顾及使用说明的介绍(我妄自揣测,请medcl大神不要见怪^_^),所以配置文件目前在安装介质中只有一个模板,外加模板中的一些参数注释的介绍,可能对于新手进行配置还是有比较大的难度(比如我),所以在自己摸索外加medcl大神的指导下我初步成功配置并实现了预期效果,现在分享给大家。
Path模块:
path.data: data
path.logs: log
这块没什么好说的,就是这是data和log的相对路径,一般也不要改
entry模块:
entry:
- name: es_gateway #your gateway endpoint
enable: true
router: default #configure your gateway's routing flow
network:
binding: 0.0.0.0:8000
reuse_port: true #you can start multi gateway instance, they share same port, to full utilize system's resources
tls:
enabled: false #if your es is using https, the gateway entrypoint should enable https too
这里实际上就是定义gateway的总体模块,指定了gateway网关名字,绑定的地址端口和网关是否启用的开关,这个开关就是指enable参数,我发现后面的版本有的模板文件中是不带enable这个参数的,这里其实有问题,因为测试时发现如果不设置enable为true的话默认值时false,也就是说网关不生效,为了当时排查这个问题我花了不少时间。这里划重点哈,entry模块里定义最重要的参数就是router,也就是网关gateway的路由策略。gateway的配置文件包含关系是这样的,gateway的entry指定了router,router中指定了tracing_flow和default_flow以及默认flow选择策略,tracing_flow是指定网关自己的流,也就是网关监控的流的处理逻辑,和业务查询和写入是无关的,default_flow实际上是真实网关的业务流,这个流一定是要走cache的。然后flow里面才会包含filter,filter顾名思义就是网关流的过滤筛选条件,filter里会有cache逻辑、rate限流条件、filter条件、elasticsearch属性。因为网关的监控数据要单独向es写入,需要设置一些写入属性和参数,所以需要module模块去指定模板、运行态参数和pipeline,然后再pipeline模块中指定写入es属性,包括es地址、索引名、队列名、worksize、bulksize等等,另外网关自己的一些队列参数设置在queue模块中,网关监控信息参数设置在statsd模块中。
flow模块:
- name: cache_first
filter: #comment out any filter sections, like you don't need cache or rate-limiter
- name: get_cache_1
type: get_cache
- name: rate_limit_1
type: rate_limit
parameters:
message: "Hey, You just reached our request limit!"
rules: #configure match rules against request's PATH, eg: /_cluster/health, match the first rule and return
- pattern: "/(?P<index_name>test.*?)/_search" #use regex pattern to match index, will match any /$index/_search, and limit each index with max_qps ~=100
max_qps: 1000
group: index_name
- name: elasticsearch_1
type: elasticsearch
parameters:
elasticsearch: default #elasticsearch configure reference name
max_connection: 1000 #max tcp connection to upstream, default for all nodes
max_response_size: -1 #default for all nodes
balancer: weight
- host: 192.168.3.201:9200 #the format is host:port
weight: 100
- host: 192.168.3.202:9200
weight: 100
discovery:
enabled: false
- name: set_cache_1
type: set_cache
parameters:
cache_ttl: 1000s
# max_cache_items: 100000
- name: request_logging # this flow is used for request logging, refer to `router`'s `tracing_flow`
filter:
- name: request_path_filter_1
type: request_path_filter
parameters:
must: #must match all rules to continue
prefix:
- /test
- name: request_logging_1
type: request_logging
parameters:
queue_name: request_logging
如上,flow模块就是定义了两个flow,一个是cache_first,一个是request_logging,cache_first是承接业务流,所以在filter中一定要设置get_cache和set_cache强制走缓存策略,这里注意set_cache中要有缓存失效时间cache_ttl,默认是10s,这个我根据我的业务需求直接增加到了1000s,因为我想将缓存多保留一段时间,max_cache_items我直接注释掉了,不限制缓存数量的大小,如果一定要设置这个值要注意一下这个值针对的是每一个查询语句的而不是总共的。另外在cache_first中可以设置限流策略,/(?P<index_name>test.*?)表示test开头的索引全部应用限流策略。这里还有一个关键点是指定elasticsearch地址时可以配置连接权重,这个参数对我还是蛮有用的,因为我想我的应用查询通过网关只从coor节点进入而不是datanode和masternode,做到读写节点分离同时降低大查询灾难蔓延扩散的风险。所以我会将es集群中两个coor节点配置到这里,weight尽量设置的大一点,这里要注意如果这样配置必须把discovery的enable设置为false,否则网关还是会从master或者data节点进入。
request_logging的流其实就是网关trace监控自己用的,由于后面进行了filter强匹配,所以采样sample参数我就没有配置。注意这里其实我们在cache_first中并没有配置filter规则,所以这里任何过网关的查询都会进入缓存,包括查询tracing_flow自己创建的索引。在request_logging尽量按照自己的需求去配置一些filter规则来减少监控写入的索引,如果不配置,写入的量级会非常多,后续用仪表盘进行监控时响应会非常慢。目前gateway网关支持的filter类型非常全面,包括request_path_filter根据索引名或者路径去筛选,request_header_filter根据请求头部信息去筛选索引,request_method_filter根据请求的方法类型去筛选,注意如果使用request_header_filter根据请求头部信息去筛选索引,需要在应用在请求的头里加入特殊标识,比如如果要通过request_header_filter方式把所有kibana的请求全部过滤掉,就需要在kibana配置文件中增加头部参数并自定义值:
elasticsearch.customHeaders: { "app": "kibana" }
然后在request_header_filter中加过滤条件:
- name: request_header_filter1 # filter out the requests that we are not interested, reduce tracing pressure
type: request_header_filter
parameters:
exclude: # any rule match will marked request as filtered
- app: kibana # in order to filter kibana's access log, config `elasticsearch.customHeaders: { "app": "kibana" }` to your kibana's config `/config/kibana.yml`
我目前的需求是只想监控某些固定索引前缀的请求,所以我只配置了request_path_filter并must强配置了比如test索引前缀的索引,这样已经满足我得需求并最大限度的减少了监控请求数量。
这里还有一个点,就是在filter类型中还有个特殊的类型,叫做request_logging,这个是专门针对tracing_flow设计的,其中有一个重要参数是queue_name,他会在gateway网关所在的服务器磁盘上创建一个队列用来加速写入,减少tracing_flow对网关所造成的性能影响,所以这个队列在这里创建之后会在后面的pipelines中去指定写入磁盘队列。
router模块:
router:
- name: default
tracing_flow: request_logging #a flow will execute after request finish
default_flow: cache_first
rules: #rules can't be conflicted with each other, will be improved in the future
- id: 1 # this rule means match every requests, and sent to `cache_first` flow
method:
- "*"
pattern:
- /
# priority: 1
flow:
- cache_first # after match, which processing flow will go through
router模块最重要的就是指定了tracing_flow和default_flow,这里可以定义路由规则,按照默认全部匹配就走缓存可以,同时最后在flow参数指定默认要走的flow流,也就是cache_first业务请求流。
elasticsearch模块:
elasticsearch:
- name: default
enabled: true
endpoint: http://localhost:9200 # if your elasticsearch is using https, your gateway should be listen on as https as well
version: 7.9.1 #optional, used to select es adaptor, can be done automatically after connect to es
index_prefix: gateway_
basic_auth: #used to discovery full cluster nodes, or check elasticsearch's health and versions
username: elastic
password: pass
这里需要指定es地址和端口,可以配置多个,也就是说可以将业务的es和监控索引存储的es进行分离,这里有个参数index_prefix,我的理解是这个会在kibana中去创建这个索引模式,用来后续进行仪表盘监控,但是真实测试结果是这个设置没有生效,索引模式前缀都是gateway_requests,这个可能需要后续和Medcl大神再确认一下。
modules模块:
modules:
- name: elastic
enabled: true
elasticsearch: default
init_template: true
- name: pipeline
enabled: true
runners:
- name: primary
enabled: true
max_go_routine: 1
threshold_in_ms: 0
timeout_in_ms: 5000
pipeline_id: request_logging_index
modules需要配置两个模块,一个是es模块,一个是pipeline模块,主要是为
request_logging要往es中写数做准备,es模块使用默认模板,pipeline设置一些写入需要的参数,我全部没改使用默认值。
pipelines模块:
pipelines:
- name: request_logging_index
start:
joint: json_indexing
enabled: true
parameters:
index_name: "gateway_requests"
elasticsearch: "default"
input_queue: "request_logging"
timeout: "60s"
worker_size: 1
bulk_size_in_mb: 10 #in MB
process:
因为要向es中写入trace数据,所以需要在pipeline中配置写入参数,包括json方式传输,写入的es(如果es有多个需要拆分),使用的磁盘队列queue,超时时间,写入worker数以及bulksize,这个我觉得后续可以根据监控的量级进行优化。
queue模块:
queue:
min_msg_size: 1
max_msg_size: 50000000
max_bytes_per_file: 53687091200
sync_every_records: 100000 # sync by records count
sync_timeout_in_ms: 10000 # sync by time in million seconds
read_chan_buffer: 0
这个就是那个trace数据本次磁盘队列的一些写入参数,包括消息大小,磁盘文件大小,同步消息数,read buffer等等,如果像我目前的应用场景已经严格匹配索引的量级有限可以不用改,但是我觉得默认max_bytes_per_file默认值是50G,这个我觉得单个文件有点大,生产环境可以考虑调小。
statsd模块:
enabled: false
host: 127.0.0.1
port: 8125
namespace: gateway.
这个模块目前我没用到,应该是本身监控gateway状态的模块,指定了监控端口,后续可以进行验证。
使用对比结果:
通过gateway中自带仪表盘可以对使用情况和效果进行方便的监控,通过比对可以发现一条复用的查询语句走网关缓存和不走网关缓存性能差距在100倍以上,使用网关进行查询优势巨大特别明显。

网关截图.png


未来展望:
首先感谢Medcl大神把这么好的东西开源出来,使用网关对于业务查询有百倍上的性能提升确实十分诱人。而且目前虽然INFINI Gateway刚刚开源出来,但是已经陆陆续续迭代了好几个版本,可以明显看到一些bug修复、功能增强和性能优化提高,目前最新的版本又增加了请求灰度切换、流量迁移和流量复制功能,可以实现双写和多写,这个功能对于我来说后续很可能会应用到,因为实际上是一种变相的多集群数据同步方式的实现。所以可以看出INFINI Gateway网关不仅仅定位于查询缓存网关,而是集查询和写入功能为一体的综合性大网关,期待Medcl大神后续的精彩表演,让我们拭目以待。
继续阅读 »
本文主要分享一下大神Medcl开源的INFINI Gateway的使用方法和使用心得,当然可能由于研究得不够深入某些地方理解肤浅甚至说的根本就是错的也请Medcl大神和各位专家大佬能够多多包涵,下面详细对我使用INFINI Gateway的经验进行一下分享。
安装部署
安装部署非常简单,INFINI Gateway使用go语言进行编写,linux系统基本不需要安装任何其他依赖,直接从github上下载解压即可使用,由于需要反复重启调试,同时网关需要具备进程保护功能,我使用了supervisord进行纳管,supervisord安装部署我在此省略,有兴趣的同学直接网上搜索即可。Supervisord配置文件如下:
[program:gateway]
command = /app/logger/gateway/gateway-linux64
username = appuser
autostart=false
autorestart=true
startsecs=3
priority=1007
stdout_logfile=/app/logger/gateway/log/infini_gateway.log
注意这里由于我的测试服务器kibana、gateway和es节点都部署在一起,统一都是用supervisord进行纳管,由于网关必须等到es节点启动以后才可以启动成功,所以将gateway 的autostart设置为false。另外网关节点由于流量、资源不足等问题会有一定风险自己挂掉(测试过程中就遇到过),所以推荐将autorestart设置为true,当网关节点意外挂掉以后能够马上重启不影响应用使用。
配置文件
这块可能是我比较重点想分享的地方,因为可能由于目前项目刚刚开源,medcl大神一直忙于功能开发无暇顾及使用说明的介绍(我妄自揣测,请medcl大神不要见怪^_^),所以配置文件目前在安装介质中只有一个模板,外加模板中的一些参数注释的介绍,可能对于新手进行配置还是有比较大的难度(比如我),所以在自己摸索外加medcl大神的指导下我初步成功配置并实现了预期效果,现在分享给大家。
Path模块:
path.data: data
path.logs: log
这块没什么好说的,就是这是data和log的相对路径,一般也不要改
entry模块:
entry:
- name: es_gateway #your gateway endpoint
enable: true
router: default #configure your gateway's routing flow
network:
binding: 0.0.0.0:8000
reuse_port: true #you can start multi gateway instance, they share same port, to full utilize system's resources
tls:
enabled: false #if your es is using https, the gateway entrypoint should enable https too
这里实际上就是定义gateway的总体模块,指定了gateway网关名字,绑定的地址端口和网关是否启用的开关,这个开关就是指enable参数,我发现后面的版本有的模板文件中是不带enable这个参数的,这里其实有问题,因为测试时发现如果不设置enable为true的话默认值时false,也就是说网关不生效,为了当时排查这个问题我花了不少时间。这里划重点哈,entry模块里定义最重要的参数就是router,也就是网关gateway的路由策略。gateway的配置文件包含关系是这样的,gateway的entry指定了router,router中指定了tracing_flow和default_flow以及默认flow选择策略,tracing_flow是指定网关自己的流,也就是网关监控的流的处理逻辑,和业务查询和写入是无关的,default_flow实际上是真实网关的业务流,这个流一定是要走cache的。然后flow里面才会包含filter,filter顾名思义就是网关流的过滤筛选条件,filter里会有cache逻辑、rate限流条件、filter条件、elasticsearch属性。因为网关的监控数据要单独向es写入,需要设置一些写入属性和参数,所以需要module模块去指定模板、运行态参数和pipeline,然后再pipeline模块中指定写入es属性,包括es地址、索引名、队列名、worksize、bulksize等等,另外网关自己的一些队列参数设置在queue模块中,网关监控信息参数设置在statsd模块中。
flow模块:
- name: cache_first
filter: #comment out any filter sections, like you don't need cache or rate-limiter
- name: get_cache_1
type: get_cache
- name: rate_limit_1
type: rate_limit
parameters:
message: "Hey, You just reached our request limit!"
rules: #configure match rules against request's PATH, eg: /_cluster/health, match the first rule and return
- pattern: "/(?P<index_name>test.*?)/_search" #use regex pattern to match index, will match any /$index/_search, and limit each index with max_qps ~=100
max_qps: 1000
group: index_name
- name: elasticsearch_1
type: elasticsearch
parameters:
elasticsearch: default #elasticsearch configure reference name
max_connection: 1000 #max tcp connection to upstream, default for all nodes
max_response_size: -1 #default for all nodes
balancer: weight
- host: 192.168.3.201:9200 #the format is host:port
weight: 100
- host: 192.168.3.202:9200
weight: 100
discovery:
enabled: false
- name: set_cache_1
type: set_cache
parameters:
cache_ttl: 1000s
# max_cache_items: 100000
- name: request_logging # this flow is used for request logging, refer to `router`'s `tracing_flow`
filter:
- name: request_path_filter_1
type: request_path_filter
parameters:
must: #must match all rules to continue
prefix:
- /test
- name: request_logging_1
type: request_logging
parameters:
queue_name: request_logging
如上,flow模块就是定义了两个flow,一个是cache_first,一个是request_logging,cache_first是承接业务流,所以在filter中一定要设置get_cache和set_cache强制走缓存策略,这里注意set_cache中要有缓存失效时间cache_ttl,默认是10s,这个我根据我的业务需求直接增加到了1000s,因为我想将缓存多保留一段时间,max_cache_items我直接注释掉了,不限制缓存数量的大小,如果一定要设置这个值要注意一下这个值针对的是每一个查询语句的而不是总共的。另外在cache_first中可以设置限流策略,/(?P<index_name>test.*?)表示test开头的索引全部应用限流策略。这里还有一个关键点是指定elasticsearch地址时可以配置连接权重,这个参数对我还是蛮有用的,因为我想我的应用查询通过网关只从coor节点进入而不是datanode和masternode,做到读写节点分离同时降低大查询灾难蔓延扩散的风险。所以我会将es集群中两个coor节点配置到这里,weight尽量设置的大一点,这里要注意如果这样配置必须把discovery的enable设置为false,否则网关还是会从master或者data节点进入。
request_logging的流其实就是网关trace监控自己用的,由于后面进行了filter强匹配,所以采样sample参数我就没有配置。注意这里其实我们在cache_first中并没有配置filter规则,所以这里任何过网关的查询都会进入缓存,包括查询tracing_flow自己创建的索引。在request_logging尽量按照自己的需求去配置一些filter规则来减少监控写入的索引,如果不配置,写入的量级会非常多,后续用仪表盘进行监控时响应会非常慢。目前gateway网关支持的filter类型非常全面,包括request_path_filter根据索引名或者路径去筛选,request_header_filter根据请求头部信息去筛选索引,request_method_filter根据请求的方法类型去筛选,注意如果使用request_header_filter根据请求头部信息去筛选索引,需要在应用在请求的头里加入特殊标识,比如如果要通过request_header_filter方式把所有kibana的请求全部过滤掉,就需要在kibana配置文件中增加头部参数并自定义值:
elasticsearch.customHeaders: { "app": "kibana" }
然后在request_header_filter中加过滤条件:
- name: request_header_filter1 # filter out the requests that we are not interested, reduce tracing pressure
type: request_header_filter
parameters:
exclude: # any rule match will marked request as filtered
- app: kibana # in order to filter kibana's access log, config `elasticsearch.customHeaders: { "app": "kibana" }` to your kibana's config `/config/kibana.yml`
我目前的需求是只想监控某些固定索引前缀的请求,所以我只配置了request_path_filter并must强配置了比如test索引前缀的索引,这样已经满足我得需求并最大限度的减少了监控请求数量。
这里还有一个点,就是在filter类型中还有个特殊的类型,叫做request_logging,这个是专门针对tracing_flow设计的,其中有一个重要参数是queue_name,他会在gateway网关所在的服务器磁盘上创建一个队列用来加速写入,减少tracing_flow对网关所造成的性能影响,所以这个队列在这里创建之后会在后面的pipelines中去指定写入磁盘队列。
router模块:
router:
- name: default
tracing_flow: request_logging #a flow will execute after request finish
default_flow: cache_first
rules: #rules can't be conflicted with each other, will be improved in the future
- id: 1 # this rule means match every requests, and sent to `cache_first` flow
method:
- "*"
pattern:
- /
# priority: 1
flow:
- cache_first # after match, which processing flow will go through
router模块最重要的就是指定了tracing_flow和default_flow,这里可以定义路由规则,按照默认全部匹配就走缓存可以,同时最后在flow参数指定默认要走的flow流,也就是cache_first业务请求流。
elasticsearch模块:
elasticsearch:
- name: default
enabled: true
endpoint: http://localhost:9200 # if your elasticsearch is using https, your gateway should be listen on as https as well
version: 7.9.1 #optional, used to select es adaptor, can be done automatically after connect to es
index_prefix: gateway_
basic_auth: #used to discovery full cluster nodes, or check elasticsearch's health and versions
username: elastic
password: pass
这里需要指定es地址和端口,可以配置多个,也就是说可以将业务的es和监控索引存储的es进行分离,这里有个参数index_prefix,我的理解是这个会在kibana中去创建这个索引模式,用来后续进行仪表盘监控,但是真实测试结果是这个设置没有生效,索引模式前缀都是gateway_requests,这个可能需要后续和Medcl大神再确认一下。
modules模块:
modules:
- name: elastic
enabled: true
elasticsearch: default
init_template: true
- name: pipeline
enabled: true
runners:
- name: primary
enabled: true
max_go_routine: 1
threshold_in_ms: 0
timeout_in_ms: 5000
pipeline_id: request_logging_index
modules需要配置两个模块,一个是es模块,一个是pipeline模块,主要是为
request_logging要往es中写数做准备,es模块使用默认模板,pipeline设置一些写入需要的参数,我全部没改使用默认值。
pipelines模块:
pipelines:
- name: request_logging_index
start:
joint: json_indexing
enabled: true
parameters:
index_name: "gateway_requests"
elasticsearch: "default"
input_queue: "request_logging"
timeout: "60s"
worker_size: 1
bulk_size_in_mb: 10 #in MB
process:
因为要向es中写入trace数据,所以需要在pipeline中配置写入参数,包括json方式传输,写入的es(如果es有多个需要拆分),使用的磁盘队列queue,超时时间,写入worker数以及bulksize,这个我觉得后续可以根据监控的量级进行优化。
queue模块:
queue:
min_msg_size: 1
max_msg_size: 50000000
max_bytes_per_file: 53687091200
sync_every_records: 100000 # sync by records count
sync_timeout_in_ms: 10000 # sync by time in million seconds
read_chan_buffer: 0
这个就是那个trace数据本次磁盘队列的一些写入参数,包括消息大小,磁盘文件大小,同步消息数,read buffer等等,如果像我目前的应用场景已经严格匹配索引的量级有限可以不用改,但是我觉得默认max_bytes_per_file默认值是50G,这个我觉得单个文件有点大,生产环境可以考虑调小。
statsd模块:
enabled: false
host: 127.0.0.1
port: 8125
namespace: gateway.
这个模块目前我没用到,应该是本身监控gateway状态的模块,指定了监控端口,后续可以进行验证。
使用对比结果:
通过gateway中自带仪表盘可以对使用情况和效果进行方便的监控,通过比对可以发现一条复用的查询语句走网关缓存和不走网关缓存性能差距在100倍以上,使用网关进行查询优势巨大特别明显。

网关截图.png


未来展望:
首先感谢Medcl大神把这么好的东西开源出来,使用网关对于业务查询有百倍上的性能提升确实十分诱人。而且目前虽然INFINI Gateway刚刚开源出来,但是已经陆陆续续迭代了好几个版本,可以明显看到一些bug修复、功能增强和性能优化提高,目前最新的版本又增加了请求灰度切换、流量迁移和流量复制功能,可以实现双写和多写,这个功能对于我来说后续很可能会应用到,因为实际上是一种变相的多集群数据同步方式的实现。所以可以看出INFINI Gateway网关不仅仅定位于查询缓存网关,而是集查询和写入功能为一体的综合性大网关,期待Medcl大神后续的精彩表演,让我们拭目以待。 收起阅读 »

配置 Elasticsearch 服务器 logs

当我们运行 Elasticsearch 集群时,我们可以看到一些 Elasticsearch 的日志,比如我们可以在如下的目录中找到相应的一些 server 日志:

<cluster name>.log
<cluster name>_server.json

假如我在 config/eleasticsearch.yml 文件中把 cluster.name 定义为如下的值:cluster.name: liuxg-cluster那么我们就可以在 logs/ 目录下看到如下的文件:

$ pwd
/Users/liuxg/elastic0/elasticsearch-7.10.0/logs
liuxg:logs liuxg$ ls liuxg-cluster*
liuxg-cluster.log
liuxg-cluster_audit.json
liuxg-cluster_deprecation.json
liuxg-cluster_deprecation.log
liuxg-cluster_index_indexing_slowlog.json
liuxg-cluster_index_indexing_slowlog.log
liuxg-cluster_index_search_slowlog.json
liuxg-cluster_index_search_slowlog.log
liuxg-cluster_server.json

从上面,我们可以看到所有的文件以 liuxg-cluster 开始的文件名。这些日志记录了 elasticsearch 运行的状态。依据不同的日志等级,它会记录不同的事件。
 
详细阅读,请参阅 https://elasticstack.blog.csdn ... 90556
继续阅读 »
当我们运行 Elasticsearch 集群时,我们可以看到一些 Elasticsearch 的日志,比如我们可以在如下的目录中找到相应的一些 server 日志:

<cluster name>.log
<cluster name>_server.json

假如我在 config/eleasticsearch.yml 文件中把 cluster.name 定义为如下的值:cluster.name: liuxg-cluster那么我们就可以在 logs/ 目录下看到如下的文件:

$ pwd
/Users/liuxg/elastic0/elasticsearch-7.10.0/logs
liuxg:logs liuxg$ ls liuxg-cluster*
liuxg-cluster.log
liuxg-cluster_audit.json
liuxg-cluster_deprecation.json
liuxg-cluster_deprecation.log
liuxg-cluster_index_indexing_slowlog.json
liuxg-cluster_index_indexing_slowlog.log
liuxg-cluster_index_search_slowlog.json
liuxg-cluster_index_search_slowlog.log
liuxg-cluster_server.json

从上面,我们可以看到所有的文件以 liuxg-cluster 开始的文件名。这些日志记录了 elasticsearch 运行的状态。依据不同的日志等级,它会记录不同的事件。
 
详细阅读,请参阅 https://elasticstack.blog.csdn ... 90556 收起阅读 »

Elastic:Data tiers 介绍及索引生命周期管理 - 7.10 之后版本


Data tier 也就是数据层。是一个在 7.10 版本的一个新概念。数据层是具有相同数据角色的节点的集合,这些节点通常共享相同的硬件配置文件:
  • Content tier (内容层)节点处理诸如产品目录之类的内容的索引和查询负载。
  • Hot tier (热层) 节点处理诸如日志或指标之类的时间序列数据的索引负载,并保存你最近,最常访问的数据。
  • Warm tier (温层)节点保存的时间序列数据访问频率较低,并且很少需要更新。
  • Cold tier (冷层)节点保存时间序列数据,这些数据偶尔会被访问,并且通常不会更新。

 
详细阅读,请参阅 “Elastic:Data tiers 介绍及索引生命周期管理 - 7.10 之后版本”  https://elasticstack.blog.csdn ... 50474
继续阅读 »

Data tier 也就是数据层。是一个在 7.10 版本的一个新概念。数据层是具有相同数据角色的节点的集合,这些节点通常共享相同的硬件配置文件:
  • Content tier (内容层)节点处理诸如产品目录之类的内容的索引和查询负载。
  • Hot tier (热层) 节点处理诸如日志或指标之类的时间序列数据的索引负载,并保存你最近,最常访问的数据。
  • Warm tier (温层)节点保存的时间序列数据访问频率较低,并且很少需要更新。
  • Cold tier (冷层)节点保存时间序列数据,这些数据偶尔会被访问,并且通常不会更新。

 
详细阅读,请参阅 “Elastic:Data tiers 介绍及索引生命周期管理 - 7.10 之后版本”  https://elasticstack.blog.csdn ... 50474 收起阅读 »

Elasticsearch:Node 介绍 - 7.9 之后版本

在 Elastic Stack 7.9 之后的发布中,我们可以直接在 Elasticsearch 的配置文件中配置 Node 的角色 (node roles)。这是一个新的变化。在 7.9 发布版之前,我们使用 node.master: true 这样的方式来定义一个 master 节点,但是从 7.9 开始之后,我们也可以使用另外一个方法来定义一个 master 节点。我们可以通过 node.roles 来定义一个 master 节点。但是这两种方法只可以选其一,不能两种方法同时使用。从 7.9 发布后,建议使用  node.roles 来定义 node 的角色。在今天的文章中,我来介绍 node.roles。 
 
详细阅读 https://elasticstack.blog.csdn ... 47372
继续阅读 »
在 Elastic Stack 7.9 之后的发布中,我们可以直接在 Elasticsearch 的配置文件中配置 Node 的角色 (node roles)。这是一个新的变化。在 7.9 发布版之前,我们使用 node.master: true 这样的方式来定义一个 master 节点,但是从 7.9 开始之后,我们也可以使用另外一个方法来定义一个 master 节点。我们可以通过 node.roles 来定义一个 master 节点。但是这两种方法只可以选其一,不能两种方法同时使用。从 7.9 发布后,建议使用  node.roles 来定义 node 的角色。在今天的文章中,我来介绍 node.roles。 
 
详细阅读 https://elasticstack.blog.csdn ... 47372 收起阅读 »

深入理解 Dissect ingest processor

Dissect 和 Grok 摄入处理器在数据结构化中被广泛使用。与 Grok 处理器不同,解析不使用正则表达式。 这使得 Dissect 的语法更加简单,并且在某些情况下比 Grok Processor 更快。Dissect ingest processoor 以其高效的性能优于 Grok 而在很多情形下被优先考虑。由于 dissect 的对数据的格式要求非常严格。在我们处理数据时需要格外小心。详细阅读,请参阅 “深入理解 Dissect ingest processor” https://elasticstack.blog.csdn ... 20145
继续阅读 »
Dissect 和 Grok 摄入处理器在数据结构化中被广泛使用。与 Grok 处理器不同,解析不使用正则表达式。 这使得 Dissect 的语法更加简单,并且在某些情况下比 Grok Processor 更快。Dissect ingest processoor 以其高效的性能优于 Grok 而在很多情形下被优先考虑。由于 dissect 的对数据的格式要求非常严格。在我们处理数据时需要格外小心。详细阅读,请参阅 “深入理解 Dissect ingest processor” https://elasticstack.blog.csdn ... 20145 收起阅读 »

性能爆表!INFINI Gateway 性能与压力测试结果

本文主要是分享下对 INFINI Gateway 的压测过程,使用graphite观测压力测试qps的过程。如有什么错漏的地方,还请多多包涵,不多逼逼,进入正题

硬件配置

主机 型号 CPU 内存/带宽 系统
172.31.18.148(gateway1) aws c5a.8xlarge x86 32核 64G/10G Ubuntu 20.04.1 LTS
172.31.24.102(gateway2) aws c6g.8xlarge arm 32核 64G/10G Ubuntu 20.04.1 LTS
172.31.23.133(test) aws c5a.8xlarge x86 32核 64G/10G Ubuntu 20.04.1 LTS

测试准备

系统调优(所有节点)

修改系统参数

vi /etc/sysctl.conf

net.netfilter.nf_conntrack_max = 262144
net.nf_conntrack_max = 262144
net.ipv4.ip_forward = 1
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.ip_nonlocal_bind=1
fs.file-max=10485760
net.core.rmem_max=4194304
net.core.wmem_max=4194304
net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_timestamps=1
net.core.somaxconn=32768
net.ipv4.tcp_syncookies=1
net.ipv4.tcp_max_syn_backlog=65535
net.ipv4.tcp_synack_retries=0
net.core.netdev_max_backlog=65535
net.core.rmem_max=4194304
net.core.wmem_max=4194304
#修改默认的本地端口范围
net.ipv4.ip_local_port_range='1024 65535' 
net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_timestamps=1

保存并执行 sysctl -p

修改用户单进程的最大文件数,

用户登录时生效

echo '* soft nofile 1048576' >> /etc/security/limits.conf 
echo '* hard nofile 1048576' >> /etc/security/limits.conf 

用户单进程的最大文件数 当前会话生效

ulimit -n 1048576

安装docker,graphite(gateway 安装)

sudo apt-get update
sudo apt-get install docker.io
docker run -d --name graphite --restart=always -p 80:80 -p 2003-2004:2003-2004 -p 2023-2024:2023-2024 -p 8125:8125/udp -p 8126:8126 graphiteapp/graphite-statsd

下载安装gateway

下载最新版infini-gateway(https://github.com/medcl/infini-gateway/releases),下载后无需安装即可使用

修改配置文件

解压后将gateway.yml配置文件修改成如下:

path.data: data
path.logs: log
entry:
  - name: es_gateway #your gateway endpoint
    enabled: true
   router: not_found #configure your gateway's routing flow
    network:
      binding: 0.0.0.0:8000
#      skip_occupied_port: false
      reuse_port: true #you can start multi gateway instance, they share same port, to full utilize system's resources
    tls:
      enabled: false #if your es is using https, the gateway entrypoint should enable https too
flow:
  - name: not_found #testing flow
    filter:
      - name: not_found
        type: echo
        parameters:
          str: '{"message":"not found"}'
          repeat: 1
  - name: cache_first
    filter: #comment out any filter sections, like you don't need cache or rate-limiter
      - name: get_cache_1
        type: get_cache
        parameters:
          pass_patterns: ["_cat","scroll", "scroll_id","_refresh","_cluster","_ccr","_count","_flush","_ilm","_ingest","_license","_migration","_ml","_rollup","_data_stream","_open", "_close"]
#          hash_factor:
#            header:
#              - "*"
#            path: true
#            query_args:
#              - id
#          must_cache:
#            method:
#              - GET
#            path:
#              - _search
#              - _async_search
      - name: rate_limit_1
        type: rate_limit
        parameters:
          message: "Hey, You just reached our request limit!"
          rules: #configure match rules against request's PATH, eg: /_cluster/health, match the first rule and return
            - pattern: "/(?P<index_name>medcl)/_search" #use index name, will match: /medcl/_search, with limit medcl with max_qps ~=3
              max_qps: 3 #setting max qps after match
              group: index_name #use regex group name to extract the throttle bucket name
            - pattern: "/(?P<index_name>.*?)/_search" #use regex pattern to match index, will match any /$index/_search, and limit each index with max_qps ~=100
              max_qps: 100
              group: index_name
      - name: elasticsearch_1
        type: elasticsearch
        parameters:
          elasticsearch: default  #elasticsearch configure reference name
          max_connection: 1000 #max tcp connection to upstream, default for all nodes
          max_response_size: -1 #default for all nodes
          balancer: weight
          discovery:
            enabled: true
      - name: set_cache_1
        type: set_cache
  - name: request_logging
    filter:
      - name: request_header_filter
        type: request_header_filter
        parameters:
          include:
            CACHE: true
      - name: request_logging_1
        type: request_logging
        parameters:
          queue_name: request_logging
router:
  - name: default
    tracing_flow: request_logging #a flow will execute after request finish
    default_flow: cache_first
    rules: #rules can't be conflicted with each other, will be improved in the future
      - id: 1 # this rule means match every requests, and sent to `cache_first` flow
        method:
          - "*"
        pattern:
          - /
        flow:
          - cache_first # after match, which processing flow will go through
  - name: not_found
    default_flow: not_found
elasticsearch:
- name: default
 enabled: false
  endpoint: http://localhost:9200 # if your elasticsearch is using https, your gateway should be listen on as https as well
  version: 7.6.0 #optional, used to select es adaptor, can be done automatically after connect to es
  index_prefix: gateway_
  basic_auth: #used to discovery full cluster nodes, or check elasticsearch's health and versions
    username: elastic
    password: Bdujy6GHehLFaapFI9uf
statsd:
  enabled: true
  host: 127.0.0.1
  port: 8125
  namespace: gateway.
modules:
- name: elastic
 enabled: false
  elasticsearch: default
  init_template: true
- name: pipeline
  enabled: true
  runners:
    - name: primary
      enabled: true
      max_go_routine: 1
      threshold_in_ms: 0
      timeout_in_ms: 5000
      pipeline_id: request_logging_index
pipelines:
- name: request_logging_index
  start:
    joint: json_indexing
   enabled: false
    parameters:
      index_name: "gateway_requests"
      elasticsearch: "default"
      input_queue: "request_logging"
      num_of_messages: 1
      timeout: "60s"
      worker_size: 6
      bulk_size: 5000
  process: []
queue:
  min_msg_size: 1
  max_msg_size: 500000000
  max_bytes_per_file: 50*1024*1024*1024
  sync_every_in_seconds: 30
  sync_timeout_in_seconds: 10
  read_chan_buffer: 0

开始测试

本文中使用的压测工具http-loader,见附件

为了能充分利用服务器多核资源,测试的时候直接启用多个进程

压测x86 gateway服务器

在gateway1上启动五个gateway

./gateway-amd64&
./gateway-amd64&
./gateway-amd64&
./gateway-amd64&
./gateway-amd64&

测试gateway返回内容

curl http://172.31.18.148:8000

输出

{"message":"not found"}

在gateway2,test机上同时执行(1000个并发压测10分钟)

./http-loader -c 1000 -d 600 http://172.31.18.148:8000

观测gateway1服务器指标

使用 htop 查看系统负载情况,如下图:

htop1.jpg

这里我们看到cpu基本跑满,说明gateway已经压到极限了

使用iftop查看系统网络流量情况,如下图:

iftop1.jpeg

使用netstat,ss查看tcp连接数,如下图:

sudo netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
ss -s

netstat1.jpeg

查看graphite statsd qps指标,如下图:

gf1.jpeg

压测arm gateway服务器

在gateway2上启动五个gateway

./gateway-arm64&
./gateway-arm64&
./gateway-arm64&
./gateway-arm64&
./gateway-arm64&

测试gateway返回内容

curl http://172.31.18.148:8000

输出

{"message":"not found"}

在gateway1,test机上同时执行(1000个并发压测10分钟)

./http-loader -c 1000 -d 600 http://172.31.18.148:8000

观测gateway2服务器指标

使用htop查看系统负载情况,如下图:

htop2.jpeg

使用iftop查看系统网络流量情况,如下图:

iftop2.jpeg

使用netstat,ss查看tcp连接数,如下图:

sudo netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
ss -s

netstat2.jpeg

查看graphite statsd qps指标,如下图:

gf2.jpeg

这里我们可以看到qps基本稳定在125万的qps

压测总结

单机压测x86架构机器上能达到95多万的qps, arm架构机器上能达到125万的qps,可见性能还是非常优秀的。由于文章篇幅原因,gateway单进程的压测就不贴了,有兴趣的同学可以自己下载测试下。

继续阅读 »

本文主要是分享下对 INFINI Gateway 的压测过程,使用graphite观测压力测试qps的过程。如有什么错漏的地方,还请多多包涵,不多逼逼,进入正题

硬件配置

主机 型号 CPU 内存/带宽 系统
172.31.18.148(gateway1) aws c5a.8xlarge x86 32核 64G/10G Ubuntu 20.04.1 LTS
172.31.24.102(gateway2) aws c6g.8xlarge arm 32核 64G/10G Ubuntu 20.04.1 LTS
172.31.23.133(test) aws c5a.8xlarge x86 32核 64G/10G Ubuntu 20.04.1 LTS

测试准备

系统调优(所有节点)

修改系统参数

vi /etc/sysctl.conf

net.netfilter.nf_conntrack_max = 262144
net.nf_conntrack_max = 262144
net.ipv4.ip_forward = 1
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.ip_nonlocal_bind=1
fs.file-max=10485760
net.core.rmem_max=4194304
net.core.wmem_max=4194304
net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_timestamps=1
net.core.somaxconn=32768
net.ipv4.tcp_syncookies=1
net.ipv4.tcp_max_syn_backlog=65535
net.ipv4.tcp_synack_retries=0
net.core.netdev_max_backlog=65535
net.core.rmem_max=4194304
net.core.wmem_max=4194304
#修改默认的本地端口范围
net.ipv4.ip_local_port_range='1024 65535' 
net.ipv4.tcp_tw_reuse=1
net.ipv4.tcp_timestamps=1

保存并执行 sysctl -p

修改用户单进程的最大文件数,

用户登录时生效

echo '* soft nofile 1048576' >> /etc/security/limits.conf 
echo '* hard nofile 1048576' >> /etc/security/limits.conf 

用户单进程的最大文件数 当前会话生效

ulimit -n 1048576

安装docker,graphite(gateway 安装)

sudo apt-get update
sudo apt-get install docker.io
docker run -d --name graphite --restart=always -p 80:80 -p 2003-2004:2003-2004 -p 2023-2024:2023-2024 -p 8125:8125/udp -p 8126:8126 graphiteapp/graphite-statsd

下载安装gateway

下载最新版infini-gateway(https://github.com/medcl/infini-gateway/releases),下载后无需安装即可使用

修改配置文件

解压后将gateway.yml配置文件修改成如下:

path.data: data
path.logs: log
entry:
  - name: es_gateway #your gateway endpoint
    enabled: true
   router: not_found #configure your gateway's routing flow
    network:
      binding: 0.0.0.0:8000
#      skip_occupied_port: false
      reuse_port: true #you can start multi gateway instance, they share same port, to full utilize system's resources
    tls:
      enabled: false #if your es is using https, the gateway entrypoint should enable https too
flow:
  - name: not_found #testing flow
    filter:
      - name: not_found
        type: echo
        parameters:
          str: '{"message":"not found"}'
          repeat: 1
  - name: cache_first
    filter: #comment out any filter sections, like you don't need cache or rate-limiter
      - name: get_cache_1
        type: get_cache
        parameters:
          pass_patterns: ["_cat","scroll", "scroll_id","_refresh","_cluster","_ccr","_count","_flush","_ilm","_ingest","_license","_migration","_ml","_rollup","_data_stream","_open", "_close"]
#          hash_factor:
#            header:
#              - "*"
#            path: true
#            query_args:
#              - id
#          must_cache:
#            method:
#              - GET
#            path:
#              - _search
#              - _async_search
      - name: rate_limit_1
        type: rate_limit
        parameters:
          message: "Hey, You just reached our request limit!"
          rules: #configure match rules against request's PATH, eg: /_cluster/health, match the first rule and return
            - pattern: "/(?P<index_name>medcl)/_search" #use index name, will match: /medcl/_search, with limit medcl with max_qps ~=3
              max_qps: 3 #setting max qps after match
              group: index_name #use regex group name to extract the throttle bucket name
            - pattern: "/(?P<index_name>.*?)/_search" #use regex pattern to match index, will match any /$index/_search, and limit each index with max_qps ~=100
              max_qps: 100
              group: index_name
      - name: elasticsearch_1
        type: elasticsearch
        parameters:
          elasticsearch: default  #elasticsearch configure reference name
          max_connection: 1000 #max tcp connection to upstream, default for all nodes
          max_response_size: -1 #default for all nodes
          balancer: weight
          discovery:
            enabled: true
      - name: set_cache_1
        type: set_cache
  - name: request_logging
    filter:
      - name: request_header_filter
        type: request_header_filter
        parameters:
          include:
            CACHE: true
      - name: request_logging_1
        type: request_logging
        parameters:
          queue_name: request_logging
router:
  - name: default
    tracing_flow: request_logging #a flow will execute after request finish
    default_flow: cache_first
    rules: #rules can't be conflicted with each other, will be improved in the future
      - id: 1 # this rule means match every requests, and sent to `cache_first` flow
        method:
          - "*"
        pattern:
          - /
        flow:
          - cache_first # after match, which processing flow will go through
  - name: not_found
    default_flow: not_found
elasticsearch:
- name: default
 enabled: false
  endpoint: http://localhost:9200 # if your elasticsearch is using https, your gateway should be listen on as https as well
  version: 7.6.0 #optional, used to select es adaptor, can be done automatically after connect to es
  index_prefix: gateway_
  basic_auth: #used to discovery full cluster nodes, or check elasticsearch's health and versions
    username: elastic
    password: Bdujy6GHehLFaapFI9uf
statsd:
  enabled: true
  host: 127.0.0.1
  port: 8125
  namespace: gateway.
modules:
- name: elastic
 enabled: false
  elasticsearch: default
  init_template: true
- name: pipeline
  enabled: true
  runners:
    - name: primary
      enabled: true
      max_go_routine: 1
      threshold_in_ms: 0
      timeout_in_ms: 5000
      pipeline_id: request_logging_index
pipelines:
- name: request_logging_index
  start:
    joint: json_indexing
   enabled: false
    parameters:
      index_name: "gateway_requests"
      elasticsearch: "default"
      input_queue: "request_logging"
      num_of_messages: 1
      timeout: "60s"
      worker_size: 6
      bulk_size: 5000
  process: []
queue:
  min_msg_size: 1
  max_msg_size: 500000000
  max_bytes_per_file: 50*1024*1024*1024
  sync_every_in_seconds: 30
  sync_timeout_in_seconds: 10
  read_chan_buffer: 0

开始测试

本文中使用的压测工具http-loader,见附件

为了能充分利用服务器多核资源,测试的时候直接启用多个进程

压测x86 gateway服务器

在gateway1上启动五个gateway

./gateway-amd64&
./gateway-amd64&
./gateway-amd64&
./gateway-amd64&
./gateway-amd64&

测试gateway返回内容

curl http://172.31.18.148:8000

输出

{"message":"not found"}

在gateway2,test机上同时执行(1000个并发压测10分钟)

./http-loader -c 1000 -d 600 http://172.31.18.148:8000

观测gateway1服务器指标

使用 htop 查看系统负载情况,如下图:

htop1.jpg

这里我们看到cpu基本跑满,说明gateway已经压到极限了

使用iftop查看系统网络流量情况,如下图:

iftop1.jpeg

使用netstat,ss查看tcp连接数,如下图:

sudo netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
ss -s

netstat1.jpeg

查看graphite statsd qps指标,如下图:

gf1.jpeg

压测arm gateway服务器

在gateway2上启动五个gateway

./gateway-arm64&
./gateway-arm64&
./gateway-arm64&
./gateway-arm64&
./gateway-arm64&

测试gateway返回内容

curl http://172.31.18.148:8000

输出

{"message":"not found"}

在gateway1,test机上同时执行(1000个并发压测10分钟)

./http-loader -c 1000 -d 600 http://172.31.18.148:8000

观测gateway2服务器指标

使用htop查看系统负载情况,如下图:

htop2.jpeg

使用iftop查看系统网络流量情况,如下图:

iftop2.jpeg

使用netstat,ss查看tcp连接数,如下图:

sudo netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
ss -s

netstat2.jpeg

查看graphite statsd qps指标,如下图:

gf2.jpeg

这里我们可以看到qps基本稳定在125万的qps

压测总结

单机压测x86架构机器上能达到95多万的qps, arm架构机器上能达到125万的qps,可见性能还是非常优秀的。由于文章篇幅原因,gateway单进程的压测就不贴了,有兴趣的同学可以自己下载测试下。

收起阅读 »

极限网关 INFINI Gateway 初体验

最近在elasticsearch中文社区看到medcl大神写的一篇文章《Elasticsearch 极限网关测试版本发布》,在es外层接了一个极限网关gateway,所有的请求先走网关,再到es。gateway能提供索引级别的限速限流,降低重复请求,缓存常见查询,起到查询加速的效果等等很多特性。看着很强大的样子,赶紧下载体验了一下。

下载

下载地址:https://github.com/medcl/infini-gateway/releases

找到当前最新版1.1.0_SNAPSHOT

image_(1).png


根据自己的操作系统环境选择相应的包下载,本人用的是Macbook,选择了GATEWAY-darwin64.tar.gz
#切换该路径下(路径自定)
cd /Users/shiyang/code/elastic/gateway
#下载
wget https://github.com/medcl/infin ... ar.gz
#下载完后解压
tar -zxvf GATEWAY-darwin64.tar.gz
#解压后能看到两个新文件,一个可执行二进制文件,一个yml配置文件
ls
#gateway-darwin64 gateway.yml



安装部署

在run之前需要先运行elastisearch,否则会报错,如图所示:

image_(1).png


接下来先启动es集群(如果你本地还没有部署es,建议先参考官网的es安装教程下载部署)

本机用的es版本为7.9.0,如下图表示启动es成功:

image_(2).png


接下来再启动gateway,yml配置文件可以先默认,后续可根据需要再修改。
#启动 
./gateway-darwin64

启动成功如下图所示:

image_(3).png


成功启动后,我们就可以直接访问gateway了。
curl http://0.0.0.0:8000

image_(4).png


到此,gateway就算本地部署完毕了。

是不是很简单?嗯,下载即使用,简单方便。

(接下来可以试用一下gateway的特性了。将发布在下一篇文章。)
继续阅读 »
最近在elasticsearch中文社区看到medcl大神写的一篇文章《Elasticsearch 极限网关测试版本发布》,在es外层接了一个极限网关gateway,所有的请求先走网关,再到es。gateway能提供索引级别的限速限流,降低重复请求,缓存常见查询,起到查询加速的效果等等很多特性。看着很强大的样子,赶紧下载体验了一下。

下载

下载地址:https://github.com/medcl/infini-gateway/releases

找到当前最新版1.1.0_SNAPSHOT

image_(1).png


根据自己的操作系统环境选择相应的包下载,本人用的是Macbook,选择了GATEWAY-darwin64.tar.gz
#切换该路径下(路径自定)
cd /Users/shiyang/code/elastic/gateway
#下载
wget https://github.com/medcl/infin ... ar.gz
#下载完后解压
tar -zxvf GATEWAY-darwin64.tar.gz
#解压后能看到两个新文件,一个可执行二进制文件,一个yml配置文件
ls
#gateway-darwin64 gateway.yml



安装部署

在run之前需要先运行elastisearch,否则会报错,如图所示:

image_(1).png


接下来先启动es集群(如果你本地还没有部署es,建议先参考官网的es安装教程下载部署)

本机用的es版本为7.9.0,如下图表示启动es成功:

image_(2).png


接下来再启动gateway,yml配置文件可以先默认,后续可根据需要再修改。
#启动 
./gateway-darwin64

启动成功如下图所示:

image_(3).png


成功启动后,我们就可以直接访问gateway了。
curl http://0.0.0.0:8000

image_(4).png


到此,gateway就算本地部署完毕了。

是不是很简单?嗯,下载即使用,简单方便。

(接下来可以试用一下gateway的特性了。将发布在下一篇文章。) 收起阅读 »

从零基础到能够完成微服务可观测性的专家 - Service Map 实践

对于一些大型的 IT 系统来说,微服务的个数可能达到 1000 多个或者更多。如果我们的系统变得很慢,我们想查出是哪个环节出了问题。如果没有一个很好的可观测性的工具。我们有时是一头的雾水。很幸运的是 Elastic Stack 提供了一套完整的 APM (应用性能监控)可观测性软件栈,为我们对微服务的调试提供了完美的解决方案。详细阅读请参阅文章 https://elasticstack.blog.csdn ... 50836
继续阅读 »
对于一些大型的 IT 系统来说,微服务的个数可能达到 1000 多个或者更多。如果我们的系统变得很慢,我们想查出是哪个环节出了问题。如果没有一个很好的可观测性的工具。我们有时是一头的雾水。很幸运的是 Elastic Stack 提供了一套完整的 APM (应用性能监控)可观测性软件栈,为我们对微服务的调试提供了完美的解决方案。详细阅读请参阅文章 https://elasticstack.blog.csdn ... 50836 收起阅读 »

Elasticsearch:如何处理 ingest pipeline 中的异常

我详细地介绍了如何创建并使用一个 ingest pipeline。简单地说 pipeline 是一系列处理器的定义,这些处理器将按照声明的顺序执行。 pipeline 包含两个主要字段:描述和处理器列表:
 
pipeline.png


详细阅读 https://elasticstack.blog.csdn ... 49068
继续阅读 »
我详细地介绍了如何创建并使用一个 ingest pipeline。简单地说 pipeline 是一系列处理器的定义,这些处理器将按照声明的顺序执行。 pipeline 包含两个主要字段:描述和处理器列表:
 
pipeline.png


详细阅读 https://elasticstack.blog.csdn ... 49068 收起阅读 »

Elastic 7.10 发布了可搜索快照的公测版和 Kibana Lens 的正式版

我们非常高兴地宣布 Elastic 7.10 版正式发布。这一新版本为基于 Elastic Stack(包括 Elasticsearch、Kibana、Beats 和 Logstash)构建而成的 Elastic 企业搜索、可观测性和安全性等诸多解决方案带来了一系列的新功能。同时,7.10 版也为市场带来了意义非凡的新功能,改变了客户和用户通过可搜索快照在成本、性能和数据深度三者之间进行权衡的方式。通过 Elastic 企业搜索,可以轻松连接到 Slack 和 Salesforce 沙箱。Elastic 可观测性纳入了用户体验监测和合成等新功能。Elastic 安全通过使用公测版的事件查询语言 (EQL) 关联规则和指标匹配规则,实现了复杂威胁的自动检测及优先级排序。此外,7.10 版还将 Kibana Lens 正式版收入其中,这是一款功能强大的界面拖拽工具,可用于数据的可视化和分析。

而且,我们也在不断完善 Elastic Cloud 的功能,它是部署 Elastic Stack 和我们其他解决方案的理想空间。在过去的几个月当中,我们推出了一种解决方案专属的新入门体验,并增强了对审计日志访问的合规性。当然,Elastic 7.10 目前已在 Elastic Cloud 上提供,这是唯一一个包含 7.10 版本所有新功能的托管型 Elasticsearch 产品。或者,您也可以下载 Elastic Stack 以及我们的云编排产品 Elastic Cloud Enterprise 和 Elastic Cloud for Kubernetes,进行自管型部署。

这是一个打包发行版,我们将在下文中分享一些关键的版本亮点。如需了解全面的功能介绍,请详阅各个解决方案和产品的博文。现在,让我们先来以点概面地了解一下。
 
更多阅读,请参阅 https://elasticstack.blog.csdn ... 80461
继续阅读 »
我们非常高兴地宣布 Elastic 7.10 版正式发布。这一新版本为基于 Elastic Stack(包括 Elasticsearch、Kibana、Beats 和 Logstash)构建而成的 Elastic 企业搜索、可观测性和安全性等诸多解决方案带来了一系列的新功能。同时,7.10 版也为市场带来了意义非凡的新功能,改变了客户和用户通过可搜索快照在成本、性能和数据深度三者之间进行权衡的方式。通过 Elastic 企业搜索,可以轻松连接到 Slack 和 Salesforce 沙箱。Elastic 可观测性纳入了用户体验监测和合成等新功能。Elastic 安全通过使用公测版的事件查询语言 (EQL) 关联规则和指标匹配规则,实现了复杂威胁的自动检测及优先级排序。此外,7.10 版还将 Kibana Lens 正式版收入其中,这是一款功能强大的界面拖拽工具,可用于数据的可视化和分析。

而且,我们也在不断完善 Elastic Cloud 的功能,它是部署 Elastic Stack 和我们其他解决方案的理想空间。在过去的几个月当中,我们推出了一种解决方案专属的新入门体验,并增强了对审计日志访问的合规性。当然,Elastic 7.10 目前已在 Elastic Cloud 上提供,这是唯一一个包含 7.10 版本所有新功能的托管型 Elasticsearch 产品。或者,您也可以下载 Elastic Stack 以及我们的云编排产品 Elastic Cloud Enterprise 和 Elastic Cloud for Kubernetes,进行自管型部署。

这是一个打包发行版,我们将在下文中分享一些关键的版本亮点。如需了解全面的功能介绍,请详阅各个解决方案和产品的博文。现在,让我们先来以点概面地了解一下。
 
更多阅读,请参阅 https://elasticstack.blog.csdn ... 80461 收起阅读 »

Elasticsearch:如何在聚合时选择所需要的 bucket 并进行可视化

Elasticsearch 是一个针对大数据的强大搜索引擎,它也是一个强大的数据分析引擎。采集上来的数据如果不经过分析,并生产有意义的见解,就没有任何的价值。在之前的文章中,我们已经详细讲解了有关 Elasticsearch 的聚合(aggregation)。如果你对 Elasticsearch 的聚合还是不太很清楚的话,请参阅我之前的文章:


在我们生成聚合时,我们可以通过 Bucket aggregation 来对数据进行分组,并分别计算它们的指标。但是,在实际的使用中,我们可能面对有些 bucket 并不是我们想要的 bucket,比如有些 bucket 的数据只有一个或者两个,或者是0个,那么我们并不想这些数据展示在我们的结果中。还有一种情况,比如我们只想针对每周的一个特定的天来做一个聚合。针对这些情况,我们该如何来配置我们的聚合呢?

在今天的文章中,我们将使用两种方法来对 bucket 进行选择:
  • 通过使用 min_doc_count 来控制每个 bucket 的最低显示文档数
  • 通过 bucket_selector 来挑选我们想要的 bucket 

 
更多阅读,请参阅 https://elasticstack.blog.csdn ... 56524
继续阅读 »
Elasticsearch 是一个针对大数据的强大搜索引擎,它也是一个强大的数据分析引擎。采集上来的数据如果不经过分析,并生产有意义的见解,就没有任何的价值。在之前的文章中,我们已经详细讲解了有关 Elasticsearch 的聚合(aggregation)。如果你对 Elasticsearch 的聚合还是不太很清楚的话,请参阅我之前的文章:


在我们生成聚合时,我们可以通过 Bucket aggregation 来对数据进行分组,并分别计算它们的指标。但是,在实际的使用中,我们可能面对有些 bucket 并不是我们想要的 bucket,比如有些 bucket 的数据只有一个或者两个,或者是0个,那么我们并不想这些数据展示在我们的结果中。还有一种情况,比如我们只想针对每周的一个特定的天来做一个聚合。针对这些情况,我们该如何来配置我们的聚合呢?

在今天的文章中,我们将使用两种方法来对 bucket 进行选择:
  • 通过使用 min_doc_count 来控制每个 bucket 的最低显示文档数
  • 通过 bucket_selector 来挑选我们想要的 bucket 

 
更多阅读,请参阅 https://elasticstack.blog.csdn ... 56524 收起阅读 »

Elasticsearch索引拆分方案

Elasticsearch索引拆分方案

[TOC]

一、概况

项目中,由于Elasticsearch单个索引数据量大,索引中部分数据不常用,在搜索和写入文档时,效率较低。为了减小单个索引的数据量,提升搜索和文档写入效率,将大索引根据一定的规则拆分为小的索引。拆分索引的关键点在于建立索引,文档同步,多索引搜索。

建立索引的关键问题是索引的设置以及字段的属性设置,最常见的问题是,某个字段我们希望Elasticsearch 按照我们的想法进行分词。采用自动生成索引(默认模板),索引字段的类型就会根据第一条文档的数据进行字段转换,无法实现具体某个字段使用我们想要的分词方式。另外就是无法使用自定义分词器,索引的默认分片数为5,无法根据我们制定的分片数进行分配。

为了实现我们这种自动创建索引的特殊要求,Elasticsearch也提供了索引模板API。

索引模板,就是创建索引的模板,模板中包含公共的配置(Settings)和映射(Mappings),并包含一个简单触发条件,及条件满足时使用该模板创建一个新的索引。

模板只在创建索引时应用。更改模板不会对现有索引产生影响。当使用create index API时,作为create index调用的一部分定义的设置/映射将优先于模板中定义的任何匹配设置/映射。

文档同步和搜索,我们都采用了别名的形式。索引别名,就像一个快捷方式或软连接,可以指向一个或多个索引,也可以给任何一个需要索引名的API来使用,别名不能与索引具有相同的名称。别名带给我们极大的灵活性,允许我们在运行的集群中可以无缝的从一个索引切换到另一个索引,给多个索引分组 ,给索引的一个子集创建。因为使用别名,你的应用可以在零停机的情况下从旧索引迁移到新索引。

由于文档同步,必须指定一个唯一的索引才能成功。原来单索引时,我们的索引采取了 “索引名称_v1”的形式,为方便在零停机的情况下重建索引,文档更新也新建了一个专门的索引别名。 拆分索引后,索引名称规范为“索引名称_YYMM”按月拆分(包括但不限于此种方式),就会出现多个索引,此时就不在方便新增专门的索引别名用于文档更新,反而用索引名字直接进行文档更新,就会更加的方便,直接和准确。

文档同步使用索引名称,搜索依旧使用别名的形式。多个索引,有相同的别名,索引拆分,文档分属不同的索引,但因为有相同的别名,使用别名搜索时,依然可以将数据搜索出来。

通过建立索引,文档同步,多索引搜索实现了单索引到多索引的拆分。数据还是那些数据,依然能搜索出来,索引数变多了,每个索引的数据减少,同步文档速度就可以提高。搜索也可以根据业务需求只查询部分索引,提升了查询速度,也可以查询所有数据,根据实际场景可自由变换。

二、索引拆分规则

索引拆分,可以根据创建时间拆分,如:”索引名称_yyyyMM“,”索引名称_yyyy“;也可以根据主键ID求余的方式来进行拆分,如:”索引名称_0“,”索引名称_1“。

具体的拆分规则根据业务需要进行,需要注意的是,无论根据创建时间还是根据主键ID求余来拆分,都要求根据拆分的值,是文档中不变的值,才能唯一确定一个索引,进行文档的存储,如:主键ID,创建时间;不可为变化的值,有可能变化的值,就无法唯一确定一个索引进行文档存储,如:状态,那就会出现当前在这个索引,状态改变后再另外的索引,这样每个索引都有同一条状态不同的数据,搜索时就会不准确。

本文将根据创建时间进行索引拆分。

思路:

  • 创建索引模板
  • 同步文档时,选用的索引名称以"索引名称_yyyyMM"命名,自动创建带别名的索引
  • 如果文档同步到新索引,原索引中的文档需删除

三、创建索引模板

以商品评论索引为例,将单索引拆分为多索引,根据以下规则,在同步文档时,如果无索引会字段根据模板生成:

  • 索引名称的规则“goods_comment_202010”
  • 索引别名为“goods_comment”
  • number_of_shards分片数为3
  • 配置Settings
  • 定义Mappings字段及其类型

具体模板如下所示:

{
    "order" : 0,
    "index_patterns" : [
      "goods_comment*"
    ],
    "settings" : {
      "index" : {
        "max_result_window" : "100000",
        "analysis" : {
          "filter" : {
            "by_stop_filter" : {
              "type" : "stop",
              "stopwords" : [
                " "
              ]
            },
            "pinyin_first_letter_and_full_pinyin_filter" : {
              "keep_none_chinese_in_first_letter" : "true",
              "lowercase" : "true",
              "keep_original" : "true",
              "keep_first_letter" : "true",
              "trim_whitespace" : "true",
              "type" : "pinyin",
              "keep_none_chinese" : "true",
              "limit_first_letter_length" : "16",
              "keep_full_pinyin" : "true"
            },
            "by_synonym_filter" : {
              "type" : "synonym",
              "synonyms_path" : "analysis/synonym.txt"
            },
            "full_pinyin_filter" : {
              "keep_none_chinese_in_first_letter" : "true",
              "lowercase" : "true",
              "keep_original" : "true",
              "keep_first_letter" : "false",
              "trim_whitespace" : "true",
              "type" : "pinyin",
              "keep_none_chinese" : "true",
              "limit_first_letter_length" : "16",
              "keep_full_pinyin" : "true"
            }
          },
          "char_filter" : {
            "by_char_filter" : {
              "type" : "mapping",
              "mappings" : [
                "| => |"
              ]
            }
          },
          "analyzer" : {
            "by_max_word" : {
              "filter" : [
                "by_synonym_filter",
                "lowercase"
              ],
              "char_filter" : [
                "html_strip"
              ],
              "type" : "custom",
              "tokenizer" : "ik_max_word"
            }
          },
          "tokenizer" : {
            "my_pinyin" : {
              "lowercase" : "true",
              "keep_original" : "true",
              "remove_duplicated_term" : "true",
              "keep_separate_first_letter" : "false",
              "type" : "pinyin",
              "limit_first_letter_length" : "16",
              "keep_full_pinyin" : "true"
            }
          }
        },
        "number_of_shards" : "3",
        "number_of_replicas" : "1"
      }
    },
    "mappings" : {
      "_doc" : {
        "properties" : {
          "is_img" : {
            "type" : "integer"
          },
          "gid" : {
            "type" : "integer"
          },
          "pubtime" : {
            "type" : "integer"
          }
            ....
        }
      }
    },
    "aliases" : {
      "goods_comment" : { }
    }
  }

上述模板定义,看似复杂,拆分来看,主要为如下几个部分:

{
  "order": 0,                               // 模板优先级
  "index_patterns": ["goods_comment*"],// 模板匹配的名称方式
  "settings": {...},                        // 索引设置
  "mappings": {...},                        // 索引中各字段的映射定义
  "aliases": {...}                          // 索引的别名
}

3.1 模板优先级

有时候,一个模板可能绝大部分符合新建索引的需求,但是局部需要微调,此时,如果复制旧的模板,修改该模板后,成为一个新的索引模板即可达到我们的需求,但是这操作略显重复。此时,可以采用模板叠加与覆盖来操作。模板的优先级是通过模板中的 order 字段定义的,数字越大,优先级越高。 如下为定义所有以 te 开头的索引的模板:

{
    "order": 0,
    "index_patterns": "te*",
    "settings": {
        "number_of_shards": 1
    },
    "mappings": {
        "type1": {
            "_source": {
                "enabled": false
            }
        }
    }
}

索引模板是有序合并的。如何想单独修改某一小类索引的一两处单独设置,可以在累加一层模板。

{
    "order": 1,
    "index_patterns": "tete*",
    "settings": {
        "number_of_shards": 2
    },
    "mappings": {
        "type1": {
            "_all": {
                "enabled": false
            }
        }
    }
}

上述第一个模板的 order 为0,第二个模板的 order 为1,优先级高于第一个模板,其会覆盖第一个模板中的相同项。所以对于所有以 tete 开头的索引模板效果如下:

{
    "settings": {
        "number_of_shards": 2
    },
    "mappings": {
        "type1": {
            "_source": {
                "enabled": false
            },
            "_all": {
                "enabled": false
            }
        }
    }
}

两个模板叠加了,项目的字段,优先级高的覆盖了优先级低的,如分片数。

3.2 模板匹配的名称

索引模板中的 "index_patterns" 字段定义的是该索引模板所应用的索引情况。如 "index_patterns": "tete*" 所表示的含义是,当新建索引时,所有以 tete 开头的索引都会自动匹配到该索引模板。利用该模板进行相应的设置和字段添加等。

3.3 settings 部分

索引模板中的 settings 部分一般定义的是索引的主分片、拷贝分片、刷新时间、自定义分析器等。常见的 settings 部分结构如下:


"settings": {
    "index": {
      "analysis": {...},                // 自定义的分析器
      "number_of_shards": "32",         // 主分片的个数
      "number_of_replicas": "1",        // 主分片的拷贝分片个数
      "refresh_interval": "5s"          // 刷新时间
    }
  }

建立的索引,不会立马查到,这是为什么 Elasticsearch 为 near-real-time(接近实时)的原因,需要配置刷新时间,默认的是 1s。settings 的设置中,重点是自定义分析器的设置。

  • 分析器是三个顺序执行的组件的结合。他们分别是字符过滤器、分词器、标记过滤器。字符过滤器是让字符串在被分词前变得更加整洁。一个分析器可能包含零到多个字符过滤器(character_filter)。

  • 分词器将字符串分割成单独的词(terms)或标记(tokens)。一个分析器必须包含一个分词器。

  • 分词器分词的结果的标记流会根据各自的情况,传递给特定的标记过滤器。标记过滤器可能修改、添加或删除标记。

创建的创建自定义分析器结构如下:

"settings": {
    "index": {
      "analysis": {
            "char_filter": { ... },              // 用户自定义字符过滤器
            "tokenizer":   { ... },             // 用户自定义分词器
            "filter":      { ... },             // 用户自定义标记过滤器
            "analyzer":    { ... }              // 用户自定义分析器
      },
      ...
    }
  }

3.4 索引类型的字段映射

索引模板中,映射字段所对应的常用结构是:

"mappings": {
    "_doc": {                               // 索引下的类型 _doc 应用该映射
      "dynamic_templates": [ ... ],         // 动态映射部分,用于未定义的 my_type 下字段
      "properties": { ... }                 // 自定义字段的响应映射
    }
}

"_doc" 是索引下的一个类型,Elasticsearch 7.x仅支持"_doc"作为索引类型,Elasticsearch 6.x推荐使用"_doc"为索引类型。

动态映射

动态映射 "dynamic_templates" 字段对应的是一个数组,数组中的元素是一个个字段的映射模板。每个字段的映射模板都有一个名字用户描述这个模板的用途,一个 mapping 字段由于指明这个映射如何使用,和至少一个参数(例如 match)来定义这个模板适用于哪个字段。 dynamic_templates 字段对应的字段模板结构如下:

{
    "string_fields": {                                  // 字段映射模板的名称,一般为"类型_fields"的命名方式
        "match": "*",                                   // 匹配的字段名为所有
        "match_mapping_type": "string",                 // 限制匹配的字段类型,只能是 string 类型
        "mapping": { ... }                              // 字段的处理方式
 }

自定义字段映射

针对索引类型中存在的字段,除了可以采用动态模板的方式,还可以采用定义定义的方式,常见的自定义结构如下:

"mappings": {
    "my_type": {
      "dynamic_templates": [ ... ],
      "properties": {
          "user_city": {                                // 字段名
             "analyzer": "lowercase_analyzer",          // 字段分析器
             "index": "analyzed",                       // 字段索引方式定义索引
             "type": "string",                          // 字段数据类型定义为 string
             "fields": {                                // 定义一个名为 user_city.raw 的嵌入的不分析字段
                "raw": {
                    "ignore_above": 512,
                    "index": "not_analyzed",
                    "type": "string"
                }
            }
         },
         "money":{
            "type": "double",
            "doc_values": true
         }
         ...
      }
    }
}

3.5 别名

即使你认为现在的索引设计已经是完美的了,当你的应用在生产环境使用时,还是有可能在今后有一些改变的。所以请做好准备:在应用中使用别名而不是索引。然后你就可以在任何时候重建索引。别名的开销很小,应当广泛使用。利用索引别名,可以实现零停机时间重新索引。 定义方式如下:

{
  "order": 0,                               // 模板优先级
  "index_patterns": "goods_comment*",         // 模板匹配的名称方式
  "settings": {...},                        // 索引设置
  "mappings": {...},                        // 索引中各字段的映射定义
  "aliases": {
     "goods_comment":{}
  }
}

以上只是简单的介绍了索引模板和模板内的组成部分的介绍,详情请见Elasticsearch官方文档。

有了以上的知识,我们就可以利用索引模板的API,来对模板进行创建,查询,删除操作了。

3.6 索引模板管理

创建索引模板

PUT _template/goods_comment_template
{
  "order": 0,                               // 模板优先级
  "index_patterns": "goods_comment*",  // 模板匹配的名称方式
  "settings": {...},                        // 索引设置
  "mappings": {...},                        // 索引中各字段的映射定义
  "aliases": {
     "goods_comment":{}
  }
}

查看索引模板

GET _template                // 查看所有模板
GET _template/temp*          // 查看与通配符相匹配的模板
GET _template/temp1,temp2    // 查看多个模板
GET _template/shop_template  // 查看指定模板

判断模板是否存在

HEAD _template/shop_tem

结果: a) 如果存在, 响应结果是: 200 - OK b) 如果不存在, 响应结果是: 404 - Not Found

删除索引模板

DELETE _template/shop_template    // 删除上述创建的模板

如果模板不存在, 将抛出404 错误

四、同步文档,自动创建索引

前面创建了商品评论的索引模板(goods_comment_template),同步文档时,指定索引名称为“goods_comment_202010”,如果索引不存在,便会创建名为“goods_comment_202010”的索引,同时创建好“goods_comment”别名。索引的settings和mappings都会根据模板定义的规则生成好。索引创建成功,此时该索引便能正常使用啦。

商品评论业务中,同步文档是在代码中实现,需要根据商品评论的创建时间,以“goods_comment_yyyyMM”的形式获取完整的索引名称(如:goods_comment_202010),同步文档指定goods_comment_202010,即可将数据同步到该索引。

五、别名搜索

多个商品评论索引,每个索引都有“goods_comment“别名,使用别名进行搜索,便能从这多个索引中获取数据。

同理,其他业务索引实现搜索,都要求使用别名形式。

六、可能存在的问题点

索引创建后,并不是一成不变的,随着业务的发展,新增字段也是较常见的。原来单索引,新增一个字段,只需要在mappings新增字段,重建索引,迁移数据,切换别名即可。拆分后的多索引,工作量便会成被增加。

修改索引模板,只会对后续生成的索引有作用,之前生成的索引,如需调整,需要手动或者使用脚本的形式进行重建并迁移数据。

七、附录

demo演示,也是体验索引拆分的一个实现过程。

7.1 查询索引模板列表

查看ES中的所有索引模板列表

命令:

GET _cat/templates?v

结果:

name                                  index_patterns             order       version
kibana_index_template:.kibana         [.kibana]                  0           
.monitoring-kibana                    [.monitoring-kibana-6-*]   0           6050399
.management-beats                     [.management-beats]        0           67000

7.2 创建索引模板

命令:

PUT _template/demo_template
{
  "order": 0,
  "index_patterns": [
    "demo*"
  ],
  "settings": {
    "index": {
      "number_of_shards": 2,
      "number_of_replicas": 0,
      "max_result_window": 100000
    }
  },
  "aliases": {
    "demo": {}
  }
}

结果:

{
  "acknowledged" : true
}

7.3 查看索引模板详情

命令:

GET _template/demo_template

结果:

{
  "demo_template" : {
    "order" : 0,
    "index_patterns" : [
      "demo*"
    ],
    "settings" : {
      "index" : {
        "max_result_window" : "100000",
        "number_of_shards" : "2",
        "number_of_replicas" : "0"
      }
    },
    "mappings" : { },
    "aliases" : {
      "demo" : { }
    }
  }
}

7.4 查询索引数据

命令:

GET demo_v1/_search

结果:

{
  "error" : {
    "root_cause" : [
      {
        "type" : "index_not_found_exception",
        "reason" : "no such index",
        "resource.type" : "index_or_alias",
        "resource.id" : "demo_v1",
        "index_uuid" : "_na_",
        "index" : "demo_v1"
      }
    ],
    "type" : "index_not_found_exception",
    "reason" : "no such index",
    "resource.type" : "index_or_alias",
    "resource.id" : "demo_v1",
    "index_uuid" : "_na_",
    "index" : "demo_v1"
  },
  "status" : 404
}

7.5 创建文档

在此之前demo_v1索引不存在,通过创建文档,自动生成索引,新创建的demo_v1将根据demo_template索引模板生成。

命令:

POST demo_v1/_doc
{
  "id": 1,
  "title": "这是一条数据"
}

结果:

{
  "_index" : "demo_v1",
  "_type" : "_doc",
  "_id" : "20upIHUBO6Fj2CIJUFPr",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}

查看数据

GET demo_v1/_search 用索引名称进行查询
GET demo/_search 用别名进行查询
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "demo_v1",
        "_type" : "_doc",
        "_id" : "20upIHUBO6Fj2CIJUFPr",
        "_score" : 1.0,
        "_source" : {
          "id" : 1,
          "title" : "这是一条数据"
        }
      }
    ]
  }
}

发现使用索引名称和别名都能搜索出来。但是我们并未单独创建索引别名。我们来查看一下demo_v1索引的结构。

GET demo_v1
{
  "demo_v1" : {
    "aliases" : {
      "demo" : { }
    },
    "mappings" : {
      "_doc" : {
        "properties" : {
          "id" : {
            "type" : "long"
          },
          "title" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          }
        }
      }
    },
    "settings" : {
      "index" : {
        "number_of_shards" : "2",
        "provided_name" : "demo_v1",
        "max_result_window" : "100000",
        "creation_date" : "1602570768526",
        "number_of_replicas" : "0",
        "uuid" : "WrXtDB5eRzmU-xX1vAUCrA",
        "version" : {
          "created" : "6070099"
        }
      }
    }
  }
}

我们可以看到,demo_v1 索引中的数据:

  • 分片数(number_of_shards): 2
  • 副本(number_of_replicas): 0
  • 别名(aliases):demo
  • 最大结果窗口(max_result_window):100000

这些都是我们在demo_template模板中设置的,在自动创建索引时,根据索引模板的index_patterns值,只要我们的索引名称是以“demo”为前缀,都会根据该模板生成索引。因此,无论是demo_v1,还是demo_v2,只要是以“demo”为前缀,直接创建文档,如果不存在索引,ES也会自动给我们创建以“demo_template”为模板的索引。实现索引拆分最关键的点,就在于索引模板。

同样,我们通过创建文档,来生成一个没有索引模板的索引进行对比。

查询demo

GET demo/_search

确定demo索引不存在

{
  "error" : {
    "root_cause" : [
      {
        "type" : "index_not_found_exception",
        "reason" : "no such index",
        "resource.type" : "index_or_alias",
        "resource.id" : "demo",
        "index_uuid" : "_na_",
        "index" : "demo"
      }
    ],
    "type" : "index_not_found_exception",
    "reason" : "no such index",
    "resource.type" : "index_or_alias",
    "resource.id" : "demo",
    "index_uuid" : "_na_",
    "index" : "demo"
  },
  "status" : 404
}

创建一条文档

POST demo/_doc
{
  "id": 1,
  "title": "这是一条数据"
}

创建成功

{
  "_index" : "demo",
  "_type" : "_doc",
  "_id" : "PmXEIHUBwM4PCvJbG7Xw",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}

查看数据

GET demo/_search
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "demo",
        "_type" : "_doc",
        "_id" : "PmXEIHUBwM4PCvJbG7Xw",
        "_score" : 1.0,
        "_source" : {
          "id" : 1,
          "title" : "这是一条数据"
        }
      }
    ]
  }
}

数据同步成功,索引也因此创建完成,我们来看看这个索引结构

GET demo
{
  "demo" : {
    "aliases" : { },
    "mappings" : {
      "_doc" : {
        "properties" : {
          "id" : {
            "type" : "long"
          },
          "title" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          }
        }
      }
    },
    "settings" : {
      "index" : {
        "number_of_shards" : "2",
        "provided_name" : "demo",
        "creation_date" : "1602572524390",
        "number_of_replicas" : "1",
        "uuid" : "p8kNddGzQzWOaz5xLcSWhA",
        "version" : {
          "created" : "6070099"
        }
      }
    }
  }
}

我们可以看到,demo索引中的数据:

  • 分片数(number_of_shards): 2
  • 副本(number_of_replicas): 1
  • 别名(aliases):无
  • 最大结果窗口(max_result_window):无

为了直观比较,请看下表:

有索引模板(demo_v1) 无索引模板(demo)
number_of_shards 2 2
number_of_replicas 0 1
aliases demo
max_result_window 10w 无,默认是1w

上表可知,通过索引模板的创建的索引,有利于我们更好的掌控索引的结构。

通过demo演示,我们可以进一步的理解索引拆分的一个过程及其实现原理,重点在索引模板。

八、参考

继续阅读 »

Elasticsearch索引拆分方案

[TOC]

一、概况

项目中,由于Elasticsearch单个索引数据量大,索引中部分数据不常用,在搜索和写入文档时,效率较低。为了减小单个索引的数据量,提升搜索和文档写入效率,将大索引根据一定的规则拆分为小的索引。拆分索引的关键点在于建立索引,文档同步,多索引搜索。

建立索引的关键问题是索引的设置以及字段的属性设置,最常见的问题是,某个字段我们希望Elasticsearch 按照我们的想法进行分词。采用自动生成索引(默认模板),索引字段的类型就会根据第一条文档的数据进行字段转换,无法实现具体某个字段使用我们想要的分词方式。另外就是无法使用自定义分词器,索引的默认分片数为5,无法根据我们制定的分片数进行分配。

为了实现我们这种自动创建索引的特殊要求,Elasticsearch也提供了索引模板API。

索引模板,就是创建索引的模板,模板中包含公共的配置(Settings)和映射(Mappings),并包含一个简单触发条件,及条件满足时使用该模板创建一个新的索引。

模板只在创建索引时应用。更改模板不会对现有索引产生影响。当使用create index API时,作为create index调用的一部分定义的设置/映射将优先于模板中定义的任何匹配设置/映射。

文档同步和搜索,我们都采用了别名的形式。索引别名,就像一个快捷方式或软连接,可以指向一个或多个索引,也可以给任何一个需要索引名的API来使用,别名不能与索引具有相同的名称。别名带给我们极大的灵活性,允许我们在运行的集群中可以无缝的从一个索引切换到另一个索引,给多个索引分组 ,给索引的一个子集创建。因为使用别名,你的应用可以在零停机的情况下从旧索引迁移到新索引。

由于文档同步,必须指定一个唯一的索引才能成功。原来单索引时,我们的索引采取了 “索引名称_v1”的形式,为方便在零停机的情况下重建索引,文档更新也新建了一个专门的索引别名。 拆分索引后,索引名称规范为“索引名称_YYMM”按月拆分(包括但不限于此种方式),就会出现多个索引,此时就不在方便新增专门的索引别名用于文档更新,反而用索引名字直接进行文档更新,就会更加的方便,直接和准确。

文档同步使用索引名称,搜索依旧使用别名的形式。多个索引,有相同的别名,索引拆分,文档分属不同的索引,但因为有相同的别名,使用别名搜索时,依然可以将数据搜索出来。

通过建立索引,文档同步,多索引搜索实现了单索引到多索引的拆分。数据还是那些数据,依然能搜索出来,索引数变多了,每个索引的数据减少,同步文档速度就可以提高。搜索也可以根据业务需求只查询部分索引,提升了查询速度,也可以查询所有数据,根据实际场景可自由变换。

二、索引拆分规则

索引拆分,可以根据创建时间拆分,如:”索引名称_yyyyMM“,”索引名称_yyyy“;也可以根据主键ID求余的方式来进行拆分,如:”索引名称_0“,”索引名称_1“。

具体的拆分规则根据业务需要进行,需要注意的是,无论根据创建时间还是根据主键ID求余来拆分,都要求根据拆分的值,是文档中不变的值,才能唯一确定一个索引,进行文档的存储,如:主键ID,创建时间;不可为变化的值,有可能变化的值,就无法唯一确定一个索引进行文档存储,如:状态,那就会出现当前在这个索引,状态改变后再另外的索引,这样每个索引都有同一条状态不同的数据,搜索时就会不准确。

本文将根据创建时间进行索引拆分。

思路:

  • 创建索引模板
  • 同步文档时,选用的索引名称以"索引名称_yyyyMM"命名,自动创建带别名的索引
  • 如果文档同步到新索引,原索引中的文档需删除

三、创建索引模板

以商品评论索引为例,将单索引拆分为多索引,根据以下规则,在同步文档时,如果无索引会字段根据模板生成:

  • 索引名称的规则“goods_comment_202010”
  • 索引别名为“goods_comment”
  • number_of_shards分片数为3
  • 配置Settings
  • 定义Mappings字段及其类型

具体模板如下所示:

{
    "order" : 0,
    "index_patterns" : [
      "goods_comment*"
    ],
    "settings" : {
      "index" : {
        "max_result_window" : "100000",
        "analysis" : {
          "filter" : {
            "by_stop_filter" : {
              "type" : "stop",
              "stopwords" : [
                " "
              ]
            },
            "pinyin_first_letter_and_full_pinyin_filter" : {
              "keep_none_chinese_in_first_letter" : "true",
              "lowercase" : "true",
              "keep_original" : "true",
              "keep_first_letter" : "true",
              "trim_whitespace" : "true",
              "type" : "pinyin",
              "keep_none_chinese" : "true",
              "limit_first_letter_length" : "16",
              "keep_full_pinyin" : "true"
            },
            "by_synonym_filter" : {
              "type" : "synonym",
              "synonyms_path" : "analysis/synonym.txt"
            },
            "full_pinyin_filter" : {
              "keep_none_chinese_in_first_letter" : "true",
              "lowercase" : "true",
              "keep_original" : "true",
              "keep_first_letter" : "false",
              "trim_whitespace" : "true",
              "type" : "pinyin",
              "keep_none_chinese" : "true",
              "limit_first_letter_length" : "16",
              "keep_full_pinyin" : "true"
            }
          },
          "char_filter" : {
            "by_char_filter" : {
              "type" : "mapping",
              "mappings" : [
                "| => |"
              ]
            }
          },
          "analyzer" : {
            "by_max_word" : {
              "filter" : [
                "by_synonym_filter",
                "lowercase"
              ],
              "char_filter" : [
                "html_strip"
              ],
              "type" : "custom",
              "tokenizer" : "ik_max_word"
            }
          },
          "tokenizer" : {
            "my_pinyin" : {
              "lowercase" : "true",
              "keep_original" : "true",
              "remove_duplicated_term" : "true",
              "keep_separate_first_letter" : "false",
              "type" : "pinyin",
              "limit_first_letter_length" : "16",
              "keep_full_pinyin" : "true"
            }
          }
        },
        "number_of_shards" : "3",
        "number_of_replicas" : "1"
      }
    },
    "mappings" : {
      "_doc" : {
        "properties" : {
          "is_img" : {
            "type" : "integer"
          },
          "gid" : {
            "type" : "integer"
          },
          "pubtime" : {
            "type" : "integer"
          }
            ....
        }
      }
    },
    "aliases" : {
      "goods_comment" : { }
    }
  }

上述模板定义,看似复杂,拆分来看,主要为如下几个部分:

{
  "order": 0,                               // 模板优先级
  "index_patterns": ["goods_comment*"],// 模板匹配的名称方式
  "settings": {...},                        // 索引设置
  "mappings": {...},                        // 索引中各字段的映射定义
  "aliases": {...}                          // 索引的别名
}

3.1 模板优先级

有时候,一个模板可能绝大部分符合新建索引的需求,但是局部需要微调,此时,如果复制旧的模板,修改该模板后,成为一个新的索引模板即可达到我们的需求,但是这操作略显重复。此时,可以采用模板叠加与覆盖来操作。模板的优先级是通过模板中的 order 字段定义的,数字越大,优先级越高。 如下为定义所有以 te 开头的索引的模板:

{
    "order": 0,
    "index_patterns": "te*",
    "settings": {
        "number_of_shards": 1
    },
    "mappings": {
        "type1": {
            "_source": {
                "enabled": false
            }
        }
    }
}

索引模板是有序合并的。如何想单独修改某一小类索引的一两处单独设置,可以在累加一层模板。

{
    "order": 1,
    "index_patterns": "tete*",
    "settings": {
        "number_of_shards": 2
    },
    "mappings": {
        "type1": {
            "_all": {
                "enabled": false
            }
        }
    }
}

上述第一个模板的 order 为0,第二个模板的 order 为1,优先级高于第一个模板,其会覆盖第一个模板中的相同项。所以对于所有以 tete 开头的索引模板效果如下:

{
    "settings": {
        "number_of_shards": 2
    },
    "mappings": {
        "type1": {
            "_source": {
                "enabled": false
            },
            "_all": {
                "enabled": false
            }
        }
    }
}

两个模板叠加了,项目的字段,优先级高的覆盖了优先级低的,如分片数。

3.2 模板匹配的名称

索引模板中的 "index_patterns" 字段定义的是该索引模板所应用的索引情况。如 "index_patterns": "tete*" 所表示的含义是,当新建索引时,所有以 tete 开头的索引都会自动匹配到该索引模板。利用该模板进行相应的设置和字段添加等。

3.3 settings 部分

索引模板中的 settings 部分一般定义的是索引的主分片、拷贝分片、刷新时间、自定义分析器等。常见的 settings 部分结构如下:


"settings": {
    "index": {
      "analysis": {...},                // 自定义的分析器
      "number_of_shards": "32",         // 主分片的个数
      "number_of_replicas": "1",        // 主分片的拷贝分片个数
      "refresh_interval": "5s"          // 刷新时间
    }
  }

建立的索引,不会立马查到,这是为什么 Elasticsearch 为 near-real-time(接近实时)的原因,需要配置刷新时间,默认的是 1s。settings 的设置中,重点是自定义分析器的设置。

  • 分析器是三个顺序执行的组件的结合。他们分别是字符过滤器、分词器、标记过滤器。字符过滤器是让字符串在被分词前变得更加整洁。一个分析器可能包含零到多个字符过滤器(character_filter)。

  • 分词器将字符串分割成单独的词(terms)或标记(tokens)。一个分析器必须包含一个分词器。

  • 分词器分词的结果的标记流会根据各自的情况,传递给特定的标记过滤器。标记过滤器可能修改、添加或删除标记。

创建的创建自定义分析器结构如下:

"settings": {
    "index": {
      "analysis": {
            "char_filter": { ... },              // 用户自定义字符过滤器
            "tokenizer":   { ... },             // 用户自定义分词器
            "filter":      { ... },             // 用户自定义标记过滤器
            "analyzer":    { ... }              // 用户自定义分析器
      },
      ...
    }
  }

3.4 索引类型的字段映射

索引模板中,映射字段所对应的常用结构是:

"mappings": {
    "_doc": {                               // 索引下的类型 _doc 应用该映射
      "dynamic_templates": [ ... ],         // 动态映射部分,用于未定义的 my_type 下字段
      "properties": { ... }                 // 自定义字段的响应映射
    }
}

"_doc" 是索引下的一个类型,Elasticsearch 7.x仅支持"_doc"作为索引类型,Elasticsearch 6.x推荐使用"_doc"为索引类型。

动态映射

动态映射 "dynamic_templates" 字段对应的是一个数组,数组中的元素是一个个字段的映射模板。每个字段的映射模板都有一个名字用户描述这个模板的用途,一个 mapping 字段由于指明这个映射如何使用,和至少一个参数(例如 match)来定义这个模板适用于哪个字段。 dynamic_templates 字段对应的字段模板结构如下:

{
    "string_fields": {                                  // 字段映射模板的名称,一般为"类型_fields"的命名方式
        "match": "*",                                   // 匹配的字段名为所有
        "match_mapping_type": "string",                 // 限制匹配的字段类型,只能是 string 类型
        "mapping": { ... }                              // 字段的处理方式
 }

自定义字段映射

针对索引类型中存在的字段,除了可以采用动态模板的方式,还可以采用定义定义的方式,常见的自定义结构如下:

"mappings": {
    "my_type": {
      "dynamic_templates": [ ... ],
      "properties": {
          "user_city": {                                // 字段名
             "analyzer": "lowercase_analyzer",          // 字段分析器
             "index": "analyzed",                       // 字段索引方式定义索引
             "type": "string",                          // 字段数据类型定义为 string
             "fields": {                                // 定义一个名为 user_city.raw 的嵌入的不分析字段
                "raw": {
                    "ignore_above": 512,
                    "index": "not_analyzed",
                    "type": "string"
                }
            }
         },
         "money":{
            "type": "double",
            "doc_values": true
         }
         ...
      }
    }
}

3.5 别名

即使你认为现在的索引设计已经是完美的了,当你的应用在生产环境使用时,还是有可能在今后有一些改变的。所以请做好准备:在应用中使用别名而不是索引。然后你就可以在任何时候重建索引。别名的开销很小,应当广泛使用。利用索引别名,可以实现零停机时间重新索引。 定义方式如下:

{
  "order": 0,                               // 模板优先级
  "index_patterns": "goods_comment*",         // 模板匹配的名称方式
  "settings": {...},                        // 索引设置
  "mappings": {...},                        // 索引中各字段的映射定义
  "aliases": {
     "goods_comment":{}
  }
}

以上只是简单的介绍了索引模板和模板内的组成部分的介绍,详情请见Elasticsearch官方文档。

有了以上的知识,我们就可以利用索引模板的API,来对模板进行创建,查询,删除操作了。

3.6 索引模板管理

创建索引模板

PUT _template/goods_comment_template
{
  "order": 0,                               // 模板优先级
  "index_patterns": "goods_comment*",  // 模板匹配的名称方式
  "settings": {...},                        // 索引设置
  "mappings": {...},                        // 索引中各字段的映射定义
  "aliases": {
     "goods_comment":{}
  }
}

查看索引模板

GET _template                // 查看所有模板
GET _template/temp*          // 查看与通配符相匹配的模板
GET _template/temp1,temp2    // 查看多个模板
GET _template/shop_template  // 查看指定模板

判断模板是否存在

HEAD _template/shop_tem

结果: a) 如果存在, 响应结果是: 200 - OK b) 如果不存在, 响应结果是: 404 - Not Found

删除索引模板

DELETE _template/shop_template    // 删除上述创建的模板

如果模板不存在, 将抛出404 错误

四、同步文档,自动创建索引

前面创建了商品评论的索引模板(goods_comment_template),同步文档时,指定索引名称为“goods_comment_202010”,如果索引不存在,便会创建名为“goods_comment_202010”的索引,同时创建好“goods_comment”别名。索引的settings和mappings都会根据模板定义的规则生成好。索引创建成功,此时该索引便能正常使用啦。

商品评论业务中,同步文档是在代码中实现,需要根据商品评论的创建时间,以“goods_comment_yyyyMM”的形式获取完整的索引名称(如:goods_comment_202010),同步文档指定goods_comment_202010,即可将数据同步到该索引。

五、别名搜索

多个商品评论索引,每个索引都有“goods_comment“别名,使用别名进行搜索,便能从这多个索引中获取数据。

同理,其他业务索引实现搜索,都要求使用别名形式。

六、可能存在的问题点

索引创建后,并不是一成不变的,随着业务的发展,新增字段也是较常见的。原来单索引,新增一个字段,只需要在mappings新增字段,重建索引,迁移数据,切换别名即可。拆分后的多索引,工作量便会成被增加。

修改索引模板,只会对后续生成的索引有作用,之前生成的索引,如需调整,需要手动或者使用脚本的形式进行重建并迁移数据。

七、附录

demo演示,也是体验索引拆分的一个实现过程。

7.1 查询索引模板列表

查看ES中的所有索引模板列表

命令:

GET _cat/templates?v

结果:

name                                  index_patterns             order       version
kibana_index_template:.kibana         [.kibana]                  0           
.monitoring-kibana                    [.monitoring-kibana-6-*]   0           6050399
.management-beats                     [.management-beats]        0           67000

7.2 创建索引模板

命令:

PUT _template/demo_template
{
  "order": 0,
  "index_patterns": [
    "demo*"
  ],
  "settings": {
    "index": {
      "number_of_shards": 2,
      "number_of_replicas": 0,
      "max_result_window": 100000
    }
  },
  "aliases": {
    "demo": {}
  }
}

结果:

{
  "acknowledged" : true
}

7.3 查看索引模板详情

命令:

GET _template/demo_template

结果:

{
  "demo_template" : {
    "order" : 0,
    "index_patterns" : [
      "demo*"
    ],
    "settings" : {
      "index" : {
        "max_result_window" : "100000",
        "number_of_shards" : "2",
        "number_of_replicas" : "0"
      }
    },
    "mappings" : { },
    "aliases" : {
      "demo" : { }
    }
  }
}

7.4 查询索引数据

命令:

GET demo_v1/_search

结果:

{
  "error" : {
    "root_cause" : [
      {
        "type" : "index_not_found_exception",
        "reason" : "no such index",
        "resource.type" : "index_or_alias",
        "resource.id" : "demo_v1",
        "index_uuid" : "_na_",
        "index" : "demo_v1"
      }
    ],
    "type" : "index_not_found_exception",
    "reason" : "no such index",
    "resource.type" : "index_or_alias",
    "resource.id" : "demo_v1",
    "index_uuid" : "_na_",
    "index" : "demo_v1"
  },
  "status" : 404
}

7.5 创建文档

在此之前demo_v1索引不存在,通过创建文档,自动生成索引,新创建的demo_v1将根据demo_template索引模板生成。

命令:

POST demo_v1/_doc
{
  "id": 1,
  "title": "这是一条数据"
}

结果:

{
  "_index" : "demo_v1",
  "_type" : "_doc",
  "_id" : "20upIHUBO6Fj2CIJUFPr",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}

查看数据

GET demo_v1/_search 用索引名称进行查询
GET demo/_search 用别名进行查询
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "demo_v1",
        "_type" : "_doc",
        "_id" : "20upIHUBO6Fj2CIJUFPr",
        "_score" : 1.0,
        "_source" : {
          "id" : 1,
          "title" : "这是一条数据"
        }
      }
    ]
  }
}

发现使用索引名称和别名都能搜索出来。但是我们并未单独创建索引别名。我们来查看一下demo_v1索引的结构。

GET demo_v1
{
  "demo_v1" : {
    "aliases" : {
      "demo" : { }
    },
    "mappings" : {
      "_doc" : {
        "properties" : {
          "id" : {
            "type" : "long"
          },
          "title" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          }
        }
      }
    },
    "settings" : {
      "index" : {
        "number_of_shards" : "2",
        "provided_name" : "demo_v1",
        "max_result_window" : "100000",
        "creation_date" : "1602570768526",
        "number_of_replicas" : "0",
        "uuid" : "WrXtDB5eRzmU-xX1vAUCrA",
        "version" : {
          "created" : "6070099"
        }
      }
    }
  }
}

我们可以看到,demo_v1 索引中的数据:

  • 分片数(number_of_shards): 2
  • 副本(number_of_replicas): 0
  • 别名(aliases):demo
  • 最大结果窗口(max_result_window):100000

这些都是我们在demo_template模板中设置的,在自动创建索引时,根据索引模板的index_patterns值,只要我们的索引名称是以“demo”为前缀,都会根据该模板生成索引。因此,无论是demo_v1,还是demo_v2,只要是以“demo”为前缀,直接创建文档,如果不存在索引,ES也会自动给我们创建以“demo_template”为模板的索引。实现索引拆分最关键的点,就在于索引模板。

同样,我们通过创建文档,来生成一个没有索引模板的索引进行对比。

查询demo

GET demo/_search

确定demo索引不存在

{
  "error" : {
    "root_cause" : [
      {
        "type" : "index_not_found_exception",
        "reason" : "no such index",
        "resource.type" : "index_or_alias",
        "resource.id" : "demo",
        "index_uuid" : "_na_",
        "index" : "demo"
      }
    ],
    "type" : "index_not_found_exception",
    "reason" : "no such index",
    "resource.type" : "index_or_alias",
    "resource.id" : "demo",
    "index_uuid" : "_na_",
    "index" : "demo"
  },
  "status" : 404
}

创建一条文档

POST demo/_doc
{
  "id": 1,
  "title": "这是一条数据"
}

创建成功

{
  "_index" : "demo",
  "_type" : "_doc",
  "_id" : "PmXEIHUBwM4PCvJbG7Xw",
  "_version" : 1,
  "result" : "created",
  "_shards" : {
    "total" : 2,
    "successful" : 1,
    "failed" : 0
  },
  "_seq_no" : 0,
  "_primary_term" : 1
}

查看数据

GET demo/_search
{
  "took" : 1,
  "timed_out" : false,
  "_shards" : {
    "total" : 2,
    "successful" : 2,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "demo",
        "_type" : "_doc",
        "_id" : "PmXEIHUBwM4PCvJbG7Xw",
        "_score" : 1.0,
        "_source" : {
          "id" : 1,
          "title" : "这是一条数据"
        }
      }
    ]
  }
}

数据同步成功,索引也因此创建完成,我们来看看这个索引结构

GET demo
{
  "demo" : {
    "aliases" : { },
    "mappings" : {
      "_doc" : {
        "properties" : {
          "id" : {
            "type" : "long"
          },
          "title" : {
            "type" : "text",
            "fields" : {
              "keyword" : {
                "type" : "keyword",
                "ignore_above" : 256
              }
            }
          }
        }
      }
    },
    "settings" : {
      "index" : {
        "number_of_shards" : "2",
        "provided_name" : "demo",
        "creation_date" : "1602572524390",
        "number_of_replicas" : "1",
        "uuid" : "p8kNddGzQzWOaz5xLcSWhA",
        "version" : {
          "created" : "6070099"
        }
      }
    }
  }
}

我们可以看到,demo索引中的数据:

  • 分片数(number_of_shards): 2
  • 副本(number_of_replicas): 1
  • 别名(aliases):无
  • 最大结果窗口(max_result_window):无

为了直观比较,请看下表:

有索引模板(demo_v1) 无索引模板(demo)
number_of_shards 2 2
number_of_replicas 0 1
aliases demo
max_result_window 10w 无,默认是1w

上表可知,通过索引模板的创建的索引,有利于我们更好的掌控索引的结构。

通过demo演示,我们可以进一步的理解索引拆分的一个过程及其实现原理,重点在索引模板。

八、参考

收起阅读 »

训练营报名中 | 5天突破Elasticsearch全观测日志分析能力

报名时间:截止 2020年9月13日 23:59分 报名链接:https://developer.aliyun.com/learning/trainingcamp/es/1?spm=a2c6h.17708739.J_1655470630.1.20d5624dXGy59Z

训练营介绍

Elastic.png

奖品介绍

礼品-Elastic版.png

云栖大会 | Elasticsearch 场景化应用专场,将于2020年9月18日 下午13:00开始,如果你不想错过,马上订阅吧 https://yunqi.aliyun.com/2020/session55?dtrid=6bW7hKei95

更多Elasticsearch 场景化应用,请下载白皮书【Elasticsearch 八大经典应用】 下载链接:https://files.alicdn.com/tpsservice/d95297082f35355f91528a2020afd926.pdf

继续阅读 »

报名时间:截止 2020年9月13日 23:59分 报名链接:https://developer.aliyun.com/learning/trainingcamp/es/1?spm=a2c6h.17708739.J_1655470630.1.20d5624dXGy59Z

训练营介绍

Elastic.png

奖品介绍

礼品-Elastic版.png

云栖大会 | Elasticsearch 场景化应用专场,将于2020年9月18日 下午13:00开始,如果你不想错过,马上订阅吧 https://yunqi.aliyun.com/2020/session55?dtrid=6bW7hKei95

更多Elasticsearch 场景化应用,请下载白皮书【Elasticsearch 八大经典应用】 下载链接:https://files.alicdn.com/tpsservice/d95297082f35355f91528a2020afd926.pdf

收起阅读 »