1.将所有有关联关系的数据,放在一个doc json类型数据中。冗余数据,将可能会进行搜索的条件和要搜索的数据,放在一个doc中。一般来说,对于es这种NoSQL类型的数据存储来讲,都是冗余模式
{
"deptId": "1",
"name": "研发部门",
"desc": "负责公司的所有研发项目",
"employees": [
{
"empId": "1",
"name": "张三",
"age": 28,
"gender": "男"
},
{
"empId": "2",
"name": "王兰",
"age": 25,
"gender": "女"
}
]
}
优点:性能高,不需要执行两次搜索
缺点:数据冗余,维护成本高
2.在构造数据模型的时候,将有关联关系的数据,然后分割为不同的实体
PUT /website/users/1
{
"name": "小鱼儿",
"email": "xiaoyuer@sina.com",
"birthday": "1980-01-01"
}
PUT /website/blogs/1
{
"title": "我的第一篇博客",
"content": "这是我的第一篇博客,开通啦!!!"
"userId": 1
}
优点:数据不冗余,维护方便
缺点:应用层join,如果关联数据过多,导致查询过大,性能很差
3.基于nested object实现博客与评论嵌套关系
修改mapping,将comments的类型从object设置为nested
PUT /website
{
"mappings": {
"blogs": {
"properties": {
"comments": {
"type": "nested",
"properties": {
"name": { "type": "string" },
"comment": { "type": "string" },
"age": { "type": "short" },
"stars": { "type": "short" },
"date": { "type": "date" }
}
}
}
}
}
}
基于nested object中的数据进行聚合分析:
GET /website/blogs/_search
{
"size": 0,
"aggs": {
"comments_path": {
"nested": {
"path": "comments"
},
"aggs": {
"group_by_comments_age": {
"histogram": {
"field": "comments.age",
"interval": 10
},
"aggs": {
"reverse_path": {
"reverse_nested": {},
"aggs": {
"group_by_tags": {
"terms": {
"field": "tags.keyword"
}
}
}
}
}
}
}
}
}
}
4.父子关系数据模型
相对于nested数据模型来说,优点是父doc和子doc互相之间不会影响
要点:父子关系元数据映射,用于确保查询时候的高性能,但是有一个限制,就是父子数据必须存在于一个shard中
例:PUT /company
{
"mappings": {
"rd_center": {},
"employee": {
"_parent": {
"type": "rd_center"
}
}
}
}
PUT /company/employee/1?parent=1
{
"name": "张三",
"birthday": "1970-10-24",
"hobby": "爬山"
}
例:搜索有1980年以后出生的员工的研发中心:
GET /company/rd_center/_search
{
"query": {
"has_child": {
"type": "employee",
"query": {
"range": {
"birthday": {
"gte": "1980-01-01"
}
}
}
}
}
}
例:搜索有至少2个以上员工的研发中心
GET /company/rd_center/_search
{
"query": {
"has_child": {
"type": "employee",
"min_children": 2,
"query": {
"match_all": {}
}
}
}
}
例:搜索在中国的研发中心的员工:
GET /company/employee/_search
{
"query": {
"has_parent": {
"parent_type": "rd_center",
"query": {
"term": {
"country.keyword": "中国"
}
}
}
}
}
例:统计每个国家的喜欢每种爱好的员工有多少个
GET /company/rd_center/_search
{
"size": 0,
"aggs": {
"group_by_country": {
"terms": {
"field": "country.keyword"
},
"aggs": {
"group_by_child_employee": {
"children": {
"type": "employee"
},
"aggs": {
"group_by_hobby": {
"terms": {
"field": "hobby.keyword"
}
}
}
}
}
}
}
}
5.祖孙三层数据模型
country -> rd_center -> employee
PUT /company
{
"mappings": {
"country": {},
"rd_center": {
"_parent": {
"type": "country"
}
},
"employee": {
"_parent": {
"type": "rd_center"
}
}
}
}
PUT /company/employee/1?parent=1&routing=1
routing参数必须跟grandparent相同
6.数据建模之悲观锁的并发控制,3种锁粒度
①全局锁,直接锁掉整个fs index
PUT /fs/lock/global/_create
{}
DELETE /fs/lock/global
优点:操作非常简单,非常容易使用,成本低
缺点:你直接就把整个index给上锁了,这个时候对index中所有的doc的操作,都会被block住,导致整个系统的并发能力很低
②文档锁 document level锁
③共享锁:这份数据是共享的,然后多个线程过来,都可以获取同一个数据的共享锁,然后对这个数据执行读操作(读)
排他锁:是排他的操作,只能一个线程获取排他锁,然后执行增删改操作(写)
总结:如果只是要读取数据,任意个线程都可以同时进来然后读取数据,每个线程都可以上一个共享锁。
如果有线程要过来修改数据,会尝试上排他锁,排他锁跟共享锁互斥,即,如果有人已经上了共享锁了,那么排他锁就不能上。
如果有人在读数据,就不允许别人来修改数据。反之,也是一样的。