写这几篇的文章的初衷是我想对mysql中大数据量报表查询的问题做一个探索和改善。
在上一篇Canal Adapter写不到ES问题排查 我们把数据写入到了ES中,然后我们就需要用API操作数据了,下面记录下遇到的问题和一些常用的操作。
先说一个查看、操作ES信息好用的一个浏览器插件
elasticvue非常方便好用强烈推荐,用了之后我抛弃了kibana。
我碰到的一些常见问题
ES占用内存过大
直接在本地 Elasticsearch 文件的 config > jvm.options 文件中
## JVM 配置
################################################################
## 重要说明:JVM 堆大小
################################################################
…
#Xms 表示总堆空间的初始大小
#Xmx 表示总堆空间的最大大小
-Xms4g
-Xmx4g
ES写不进去数据
首先看下索引的健康信息、以及分片了。
查看健康状态会返回一些信息 是否有YELLO问题
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 | =问题 |
| terms | in问题 |
| wildcard | like问题 |
| range | >,>=,<,<=范围问题 |
| sort | like问题 |
{
"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.还是宽表直接填数据