elasticsearch应用实例
我这里是希望通过elasticsearch的集群来处理历史订单,然后清空关系数据库中的相关内容,让后端数据规模保持在控制范围以内保持数据库性能。根据实际业务情况1个月以前的订单都不会再有改动,数据将固化下来。这种不会频繁修改的数据就能入到数据仓库中以备后期的数据挖掘,数据分析等。再以前我们的数据分析组直接使用的是oracle rac的集群来进行数据的分析和处理,随着数据量逐渐增加和一些场景的要求,实时查询等以后无法满足需求。而elastic的集群可以很快的得出结果。而其它如hbase、hadoop等学习曲线比较高,成本也很高。反而elastic的集群使用的api接口入参和出参都是json格式,而且有响应的语言接口。我们使用的是java语言开发,所以很简单的就能把json格式映射到成对象。下面简单介绍一下这几天的测试使用情况。
集群部署
大概写一下部署过程,具体内容请参考官网:https://www.elastic.co/guide/en/elasticsearch/reference/current/index.html
首先安装java jdk 添加环境变量到/etc/profile文件中。然后source命令加载变量。
我这里有三个主机用来部署集群。
export JAVA_HOME=/opt/jdk1.8.0_171
export PATH=$PATH:$JAVA_HOME/bin
这里采用yum安装方式进行安装,其它安装方式请参考官网文档。
cat /etc/yum.repos.d/elasticsearch.repo
[elasticsearch-7.x]
name=Elasticsearch repository for 7.x packages
baseurl=https://artifacts.elastic.co/packages/7.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md
yum install elasticsearch -y
大概配置elasticsearch.yml文件
cluster.name: orders
node.name: node-117
node.attr.rack: gzyun
path.data: /home/elastic/data
path.logs: /var/log/elasticsearch
bootstrap.memory_lock: true
network.host: 172.16.2.117
http.port: 9200
discovery.seed_hosts: ["172.16.2.117", "172.16.2.118","172.16.2.119"]
cluster.initial_master_nodes: ["172.16.2.117", "172.16.2.118"]
jvm.options
-Xms32g
-Xmx32g
最大堆内存建议为主机系统内存的一半,最大不超过32g。
下面是优化建议:
配置内存锁定可以防止内存数据交换到磁盘影响性能,bootstrap.memory_lock: true 。需要添加limit配置 * soft memlock unlimited * hard memlock unlimited。
vi /etc/pam.d/su 添加内容 session required pam_limits.so
centos7以上用systemd管理的需要修改:
/etc/sysconfig/elasticsearch 打开注释 MAX_LOCKED_MEMORY=unlimited
/usr/lib/systemd/system/elasticsearch.service 在[Service]添加 LimitMEMLOCK=infinity
否则无法启动成功。
使用systemctl start elasticsearch命令启动,可以查看/var/log目录下的日志了解启动情况。
数据抽取和录入
正常情况下可以使用etl从关系数据库里面直接抽取数据然后录入到elastic集群中。我这里使用logstash来录入数据。安装logstash也可以直接通过yum install logstash -y命令安装。
自定义配置文件/etc/logstash/conf.d/orders.conf
input {
file {
path => ["/root/orderdata.txt"]
# path => ["/root/test.txt"]
type => "gzorder"
codec => "json"
start_position => "beginning"
}
}
filter {
mutate {
convert => ["支付金额", "float"]
convert => ["服务费", "float"]
convert => ["退款金额", "float"]
convert => ["车票金额", "float"]
convert => ["投保金额", "float"]
add_field => ["省域","贵州省"]
}
date {
match => ["支付日期","yyyy-MM-dd HH:mm:ss"]
timezone => "+08:00"
}
date {
match => ["支付日期","yyyy-MM-dd HH:mm:ss"]
remove_field => ["支付日期"]
target => "支付时间"
timezone => "+08:00"
}
date{
match => ["对账日期","yyyy-MM-dd HH:mm:ss"]
remove_field => ["对账日期"]
target => "对账时间"
timezone => "+08:00"
}
date{
match => ["发车日期","yyyy-MM-dd HH:mm:ss"]
remove_field => ["发车日期"]
target => "发车时间"
timezone => "+08:00"
}
date{
match => ["退票日期","yyyy-MM-dd HH:mm:ss"]
remove_field => ["退票日期"]
target => "退票时间"
timezone => "+08:00"
}
date{
match => ["投保日期","yyyy-MM-dd HH:mm:ss"]
remove_field => ["投保日期"]
target => "投保时间"
timezone => "+08:00"
}
date{
match => ["生效日期","yyyy-MM-dd HH:mm:ss"]
remove_field => ["生效日期"]
target => "生效时间"
timezone => "+08:00"
}
}
output {
elasticsearch {
hosts => ["172.16.2.117:9200"]
index => "orders-data"
template_name => "orders-data"
manage_template => true
template_overwrite => true
template => "/etc/logstash/ticket-template.json" #由于一些特殊统计需求,不能使用默认的数据类型。所以需要进行数据的模版定制,否则统计结果无法达到预期。
}
#stdout { codec => rubydebug }
}
/etc/logstash/ticket-template.json文件内容。这里的一条数据就代表一张车票。
这里有几个注意点,目的站这种字符类型的数据默认是text类型。如果是这种模式统计出来的将是一个字一个字的。比如成都东站,出来的结果是成->22张,都->20张这样。要关闭全文索引特性需要使用keyword类型。另外数值方面需要采用double类型,不然统计出来有小数点的比如0.1结果可能就是0.09999这种有精度问题的数据。
{
"template": "orders-data", #留意这个名称,要和上面配置文件中的index和template_name一致
"settings": {
"index.number_of_shards": 5,#定义分片数量
"number_of_replicas": 1,#定义副本数量
"index.refresh_interval": "60s"
},
"mappings": {
"properties": {
"目的站": { "type": "keyword"},
"订单号": { "type": "keyword" },
"销售渠道": { "type": "keyword" },
"支付状态" : {"type": "keyword"},
"登录名" : {"type": "keyword"},
"支付订单号" : {"type": "keyword"},
"path" : {"type": "keyword"},
"始发车站" : {"type": "keyword"},
"省域" : {"type": "keyword"},
"host" : {"type": "keyword"},
"乘车人" : {"type": "keyword"},
"订单状态" : {"type": "keyword"},
"交易流水" : {"type": "keyword"},
"@version" : {"type": "keyword"},
"退款金额":{"type": "double"},
"车票金额":{"type": "double"},
"支付金额":{"type": "double"},
"生效时间": {
"type": "date"
},
"发车时间": {
"type": "date"
},
"退票时间": {
"type": "date"
},
"对账时间": {
"type": "date"
},
"支付时间": {
"type": "date"
},
"@timestamp": {
"type": "date"
}
}
}
}
关于数据的提取我这里是使用shell脚本直接从mysql中一次性获取数据组织成json格式输出到文本中。具体细节涉及到数据表字段建模等内容,不能透露请谅解。
配置完以后直接启动logstash就行了。为了方便管理,还可以再配置一个kibana来进行数据的验证。这就是平时在运维环境下使用的elk日志处理架构。但是elasticsearch集群是全文搜索引擎,可以应用的场景非常多。利用好特性将是在现实中的一大利器,这里大概推荐几个。如新闻资讯,电子书知识搜索,搜索引擎,微博,历史订单,日志统计等等。
应用案例
这里使用postman来对elastic的各种统计功能做简单介绍。至于如何使用程序语言接口对接请自行查阅官网文档。
后面所有的url请求地址都是url地址:http://172.16.2.117:9200/orders-data/_search 具体请根据自己的环境更换地址。
精确查询
请求类型post。sql语句 select * from table where 服务费=3
{
"query" : {
"constant_score" : {
"filter" : {
"term" : {
"服务费":3
}
}
}
}
}
范围查询和统计
请求类型post。sql语句 select sum(车票金额),sum(服务费) from table where 发车时间 > “2018-01-01”
{ "size":0,
"query":{
"constant_score":{
"filter":{
"range":{
"发车时间":{
"gt":"2018-01-01"
}
}
}
}
},
"aggs":{
"车票总金额":{
"sum":{"field":"车票金额"}
},
"服务费总金额":{
"sum":{"field":"服务费"}
}
}
}
字段统计
请求类型post。sql语句 select 始发车站,count(*) from table group by 始发车站
{
"size" : 0,
"aggs":{
"all":{
"terms":{
"field":"始发车站",
"size":200 #设置返回的数量默认10个。
}
}
}
}
多范围查询和统计
请求类型post。sql语句 select sum(车票金额),sum(服务费) from table where 服务费 > 0 and 服务费 < 1 and 发车时间 < ‘2016-09-29’
must为必须,must_not为必须不,should为应该
请求入参
{
"size": 0, #如果不是直接返回统计的聚合结果,而是返回订单详细内容。那么这个size和去掉默认是10个,可以设置更多。但推荐分页查询。
"query":{
"bool":{
"must":{
"range":{
"服务费":{
"lt":1,"gt":"0"
}
}
},
"must_not":{
"range":{
"发车时间":{
"gt":"2016-09-29"
}
}
}
}
},
"aggs":{ #这个聚合参数直接去掉可以返回查询到的匹配对象。
"车票总金额":{
"sum":{"field":"车票金额"}
},
"服务费总金额":{
"sum":{"field":"服务费"}
}
}
}
出参结果
{
"took": 13,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 24,
"relation": "eq"
},
"max_score": null,
"hits": []
},
"aggregations": {
"服务费总金额": {
"value": 0.24
},
"车票总金额": {
"value": 5.63
}
}
}
集群优化建议
下面是日常使用的一些优化情况。做个笔记!
curl -XPUT -H “Content-Type: application/json” “172.21.2.152:9200/_settings” -d '{ “number_of_replicas” : 0 }'设置副本分片数
curl -XPUT -H “Content-Type: application/json” “172.21.2.152:9200/logstash*-date +%Y.%m.%d --date '-10 days'
*/_settings” -d ‘{ “number_of_replicas” : 0 }’
curl -XPUT -H “Content-Type: application/json” “172.21.2.152:9200/_cluster/settings” -d ‘{“transient” : {“cluster.routing.allocation.enable” : “none”}}’ #滚动升级重启集群,升级完成以后需要改回all。否则新进入的索引全为red状态
curl -XPUT -H “Content-Type: application/json” “172.21.2.41:9200/_all/_settings” -d ‘{“settings” : {“index.unassigned.node_left.delayed_timeout” : “5m”}}’ #推迟分片分配
curl -XPUT -H “Content-Type: application/json” “172.21.2.152:9200/_all/_settings” -d ‘{“settings” : {“index.refresh_interval” : “30s”}}’ #不要求实时查询数据,设置延迟提高性能
curl -XPUT -u elastic -H “Content-Type: application/json” http://172.21.1.85:9200/_xpack/security/user/elastic/_password -d ‘{“password”: “bst-sa”}’ #修改elastic密码
curl -XPUT -H “Content-Type: application/json” “172.21.2.152:9200/_cluster/settings” -d ‘{“persistent” : {“discovery.zen.minimum_master_nodes” : 2}}’ #此设置应该始终被配置为 master 候选节点的法定个数(大多数个)。法定个数就是 ( master 候选节点个数 / 2) + 1。防止集群脑裂造成数据丢失。