Elasticsearch-82-应用层关联查询与通过冗余数据关联对比

关系型数据建模

数据建模

以一个博客网站作为案例背景,模拟用户发表各种博客,然后针对用户和博客之间的关系进行数据建模,同时针对建模好的数据执行各种搜索 + 聚合的操作

先以类似关系型数据库中的模型来构建数据,还是将有关联的数据分割为不同的实体

添加数据

1
2
3
4
5
6
7
8
9
10
11
12
13
PUT /website/users/1 
{
"name": "小鱼儿",
"email": "xiaoyuer@sina.com",
"birthday": "1980-01-01"
}

PUT /website/blogs/1
{
"title": "我的第一篇博客",
"content": "这是我的第一篇博客,开通啦!!!",
"userId": 1
}

一个用户对应一个博客,一对多的关系

查询

假设我们现在要查询小鱼儿发表的所有博客, 就应该先拿到小鱼儿的用户id,然后去博客表中去根据user_id去搜索,请求如下

先去搜索用户

1
2
3
4
5
6
7
8
9
10
GET /website/users/_search
{
"query": {
"term": {
"name.keyword": {
"value": "小鱼儿"
}
}
}
}

返回值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"took": 35,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 0.2876821,
"hits": [
{
"_index": "website",
"_type": "users",
"_id": "1",
"_score": 0.2876821,
"_source": {
"name": "小鱼儿",
"email": "xiaoyuer@sina.com",
"birthday": "1980-01-01"
}
}
]
}
}

拿到用户id之后,去博客表根据userID去搜索

1
2
3
4
5
6
7
8
9
10
11
12
13
14
GET /website/blogs/_search
{
"query": {
"constant_score": {
"filter": {
"terms": {
"userId": [
1
]
}
}
}
}
}

返回值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
{
"took": 2,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 1,
"hits": [
{
"_index": "website",
"_type": "blogs",
"_id": "1",
"_score": 1,
"_source": {
"title": "我的第一篇博客",
"content": "这是我的第一篇博客,开通啦!!!",
"userId": 1
}
}
]
}
}

这样就把小鱼儿所有的博客都搜出来了,这种搜索就属于是应用层的join关联,在应用层先查出一份数据,然后再查出一份数据,进行关联

这里思考一个问题.假设要查询的是1万个人的博客,那么第一次查询的时候会拿到1万个user_id,然后第二次查询要在terms中放入1万个user_id,这样的话性能是很差的

优缺点

优点: 数据不冗余,维护方便
缺点: 应用层的join,如果关联的数据过多,导致查询过大,性能差

document型数据建模

数据建模

下面使用冗余数据,采用document数据模型,进行数据建模,实现用户和博客的关联

添加数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PUT /website/users/1
{
"name": "小鱼儿",
"email": "xiaoyuer@sina.com",
"birthday": "1980-01-01"
}

PUT /website/blogs/1
{
"title": "小鱼儿的第一篇博客",
"content": "大家好,我是小鱼儿。。。",
"userInfo": {
"userId": 1,
"username": "小鱼儿"
}
}

冗余数据,就是说,将可能进行搜索的条件和要搜索的数据放在一个doc中

基于冗余用户数据搜索博客

请求:

1
2
3
4
5
6
7
8
9
10
GET /website/blogs/_search
{
"query": {
"term": {
"userInfo.username.keyword": {
"value": "小鱼儿"
}
}
}
}

返回值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
{
"took": 3,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 0.2876821,
"hits": [
{
"_index": "website",
"_type": "blogs",
"_id": "1",
"_score": 0.2876821,
"_source": {
"title": "小鱼儿的第一篇博客",
"content": "大家好,我是小鱼儿。。。",
"userInfo": {
"userId": 1,
"username": "小鱼儿"
}
}
}
]
}
}

这样的话就不需要再走应用层的join,先搜索一个数据,再去搜另一份数据,直接走一个有冗余的type即可,指定要搜索的条件,即可搜索出自己想要的数据

优缺点

优点: 性能高,不需要执行多次搜索
缺点: 数据冗余,维护成本高.

比如说一个username变了,要同时更新user type和blog type.

一般来说,NoSql类型的数据存储,都是冗余模式,所以一旦出现冗余数据的修改,必须记得将所有关联的数据全都更新