ElasticSearch遇到的问题和一些常用查询

463 阅读5分钟

写这几篇的文章的初衷是我想对mysql中大数据量报表查询的问题做一个探索和改善。

在上一篇Canal Adapter写不到ES问题排查 我们把数据写入到了ES中,然后我们就需要用API操作数据了,下面记录下遇到的问题和一些常用的操作。

先说一个查看、操作ES信息好用的一个浏览器插件

elasticvue非常方便好用强烈推荐,用了之后我抛弃了kibana。

elasticvue.jpg

我碰到的一些常见问题

ES占用内存过大

直接在本地 Elasticsearch 文件的 config > jvm.options 文件中

## JVM 配置
################################################################
## 重要说明:JVM 堆大小
#################################################################Xms 表示总堆空间的初始大小
#Xmx 表示总堆空间的最大大小
-Xms4g
-Xmx4g

ES写不进去数据

首先看下索引的健康信息、以及分片了。

elasticvue-健康.jpg

查看健康状态会返回一些信息 是否有YELLO问题 elasticvue-健康2.jpg

GET _cluster/allocation/explain

"explanation": "a copy of this shard is already allocated to this node [[alice][0], node[SVNMP5vjR8a1exT0eibU2A], [P], s[STARTED], a[id=jDpqs-weRsqSmR0U6wheVQ], failed_attempts[0]]"

报错a copy of this shard is already allocated to this node是节点yellow的详细原因, 由于这个复制分片已经分配到此节点(因为是单节点分片和副本不能在同一个节点上,但是当前是单机的,可能之前用的集群), 那么解决方案就是把复制分片设置为0【number_of_replicas: 0】就可以了。

PUT /alice/_settings
{
  "number_of_replicas": 0
}

返回数据10000的问题

网上有游标取数的解决办法,这里只是简单的用API修改限制。

# 调大查询窗口大小,比如100w
PUT demo_scroll/_settings
{
  "index.max_result_window": "1000000"
}

hit数据量返回10000的问题

GET index/_search
{
	"track_total_hits": true #主要是这里
	"query":{
		"match":{
			"massage":"elasticsearch"
			}
		}
}

text数据索引的问题

如果不指定分词的话中文会像英文一样被一个一个切割,like查询的时候中文只能一个词的时候才能匹配,两个就不要可以。例如:ElasticSearch遇到的问题和java入门,like '' 可以匹配结果。但是 like '遇到' 就不可以了。

IK提供了两个分词算法:ik_smart和ik_max_word,其中ik smart为最少切分,ik_max_word为最细粒度划分!

text类型,es会分词导致多个汉字不能正常正常查询
keyword不分词可以正常查询
wildcard和keyword类似
text类型附加keyword类型,相当于支持两种类型,默认text,但是可以通过关键字加.keyword变成keyword类型

text解法,或者指定其他两种类型
"REMARK":{
        "type":"text",
        "analyzer": "ik_smart"
      }

查询数据

REST的API进行查询

因为我们的问题是想解决报表的查询问题,这些查询基本都是精确进行的,所以我们可以就不要考虑查询评分的问题。ES的查询种函数种类很多,因为这里对标的是数据库常用操作,所以只探索能够用的就可以了

常用的查询:in、=、!=、like、范围查询、分页

关键字简要说明
from-size分页
bool多个条件组合
must条件都满足
must_not条件不满足
term=问题
termsin问题
wildcardlike问题
range>,>=,<,<=范围问题
sortlike问题
{
  "from": 0,
  "size": 100,
  "query": {
    "bool": {
      "must": [
        {
          "term": {
            "BANKACC": "11050165360000000981"
          }
        },
        {
          "terms": {
            "OPPACCNO": ["41001506010050207552","6217000010088735058"]
          }
        },
        {
          "ids": {
          "values": ["66CC26D3771D30717176DF8E3A0F7A2A","D49D9403C0B2AB34297B9B372C0E0401"]
           }
        },
        {
         "wildcard": {
           "BANKACC": "*981*"
         }
        },
        {
          "range": {
            "TRANSTIME": {
              "gte": "2024-05-01 00:00:00",
              "lte": "2024-11-11 23:59:59"
            }
          }
        }
      ]
    }
  },
  "sort": [
    {
      "TRANSTIME": {
        "order": "desc"
      }
    }
  ]
}

由于我本职用java这里就写一些java的API(ES8版本的)

基本涵盖常用的sql了

直接用JSON进行数据查询
Reader queryJson = new StringReader("{请求的JSON数据}");
SearchRequest aggRequest = SearchRequest.of(b -> b
                .withJson(queryJson));
SearchResponse<BisAccDtlVO> aggResponse = esClient.search(aggRequest, BisAccDtlVO.class);
单个查询(query)拼凑
TermQuery termQuery=TermQuery.of(t->t.field("OPPACCNO").value("41001592110050203998"));

ArrayList<String> list = new ArrayList<>();
list.add("41001592110050203998");
list.add("029100310000629900000");
// 创建 "skuIds" 条件tems查询
TermsQuery termsQuery = TermsQuery.of(t -> t
                .field("OPPACCNO")
                .terms(t2 -> t2
                        .value(list.stream().map(FieldValue::of).collect(Collectors.toList())))
);

SearchResponse<BisAccDtlVO> search2 = esClient.search(s -> s
        .index("index_bis_acc_dtl")
        .query(q -> q
                .bool(b -> b
                        .must(m->m.term(termQuery))
                        .must(m->m.terms(termsQuery))
                        .must(m->m.wildcard(wildcardQuery))
                        .must(m->m.wildcard(wildcardQuery2))
                        .must(m->m.range(rangeQuery))
                ))
        .from(1)
        .size(100)
        .sort(so->so.field(f->f.field("TRANSTIME").order(SortOrder.Desc)))
        .trackTotalHits(TrackHits.of(th -> th.enabled(true))),//返回数据总数
        BisAccDtlVO.class);
直接lambada(还没用好)
SearchResponse<BisAccDtlVO> search1 = esClient.search(s -> s
                .index("index_bis_acc_dtl")
                .query(q -> q
                        .bool(b -> b
                                .must(m->m
                                        .term(t -> t
                                                .field("BANKACC").value("41001506010050207552")
                                                )
                                     )
                                .must(m->m
                                        .terms(t -> t
                                                .field("OPPACCNO")
                                                .terms(t2 -> t2.value(list.stream().map(FieldValue::of).collect(Collectors.toList())))
                                        )
                                )
                                .must(m->m
                                        .wildcard(w -> w.
                                                   field("OPPACCNO").wildcard("*3998*")))

                        ))
                .query(q -> q.range(r -> r
                        .field("TRANSTIME")
                        .gte(JsonData.of("2024-05-01 00:00:00"))
                        .lte(JsonData.of("2024-11-11 23:59:59"))
                ))
                .searchType(SearchType.QueryThenFetch)
        .from(1)
        .size(100)
        .sort(so->so.field(f->f.field("TRANSTIME").order(SortOrder.Desc)))
        , BisAccDtlVO.class);

最后问题

  • 需要考虑的问题是避免复杂的ES操作,又要满足数据库中join的操作
  • 1.采用回表补数据
  • 2.还是宽表直接填数据