Elasticsearch高手进阶篇(66)
数据建模实战_基于共享锁和排他锁实现悲观锁并发控制
共享锁和排他锁的说明
- 共享锁:
- 这份数据是共享的,然后多个线程过来,都可以获取同一个数据的共享锁,然后对这个数据执行读操作
- 排他锁:
- 是排他的操作,只能一个线程获取排他锁,然后执行增删改操作
读写锁的分离
-
如果只是要读取数据的话,那么任意个线程都可以同时进来然后读取数据,每个线程都可以上一个共享锁
-
但是这个时候,如果有线程要过来修改数据,那么会尝试上排他锁,排他锁会跟共享锁互斥,也就是说,如果有人已经上了共享锁了,那么排他锁就不能上,就得等
- 如果有人在读数据,就不允许别人来修改数据
- 如果有人在修改数据,就是加了排他锁
-
那么其他线程过来要修改数据,也会尝试加排他锁,此时会失败,锁冲突,必须等待,同时只能有一个线程修改数据
-
如果有人过来同时要读取数据,那么会尝试加共享锁,此时会失败,因为共享锁和排他锁是冲突的
如果有在修改数据,就不允许别人来修改数据,也不允许别人来读取数据
共享锁和排他锁的实验
有人在读数据,其他人也能过来读数据
构建脚本数据judge-lock-2.groovy:
if (ctx.source.lock_type == 'exclusive') { assert false }; ctx. source.lock_count++
POST /waws_fs/lock/1/_update
{
"upsert": {
"lock_type": "shared",
"lock_count": 1
},
"script": {
"lang": "groovy",
"file": "judge-lock-2"
}
}
POST /waws_fs/lock/1/_update
{
"upsert": {
"lock_type": "shared",
"lock_count": 1
},
"script": {
"lang": "groovy",
"file": "judge-lock-2"
}
}
- 获取锁信息
GET /waws_fs/lock/1
{
"_index": "waws_fs",
"_type": "lock",
"_id": "1",
"_version": 6,
"found": true,
"_source": {
"lock_type": "shared",
"lock_count": 2
}
}
就给大家模拟了,有人上了共享锁,你还是要上共享锁,直接上就行了,没问题,只是lock_count加1
已经有人上了共享锁,然后有人要上排他锁
PUT /waws_fs/lock/1/_create
{ "lock_type": "exclusive" }
{
"error": {
"root_cause": [
{
"type": "version_conflict_engine_exception",
"reason": "[lock][1]: version conflict, document already exists (current version [6])",
"index_uuid": "V98PfLHrRjKrnIkQU5IbPA",
"shard": "3",
"index": "waws_fs"
}
],
"type": "version_conflict_engine_exception",
"reason": "[lock][1]: version conflict, document already exists (current version [6])",
"index_uuid": "V98PfLHrRjKrnIkQU5IbPA",
"shard": "3",
"index": "waws_fs"
},
"status": 409
}
排他锁用的不是upsert语法,create语法,要求lock必须不能存在,直接自己是第一个上锁的人,上的是排他锁
如果已经有人上了共享锁,明显/waws_fs/lock/1是存在的,create语法去上排他锁,肯定会报错
对共享锁进行解锁
PUT /waws_fs/lock/_bulk
{ "delete": { "_id": 1}}
连续解锁3次,此时共享锁就彻底没了
每次解锁一个共享锁,就对lock_count先减1,如果减了1之后,是0,那么说明所有的共享锁都解锁完了,此时就就将/fs/lock/1删除,就彻底解锁所有的共享锁
上排他锁,再上排他锁
PUT /waws_fs/lock/1/_create
{ "lock_type": "exclusive" }
其他线程
PUT /waws_fs/lock/1/_create
{ "lock_type": "exclusive" }
{
"error": {
"root_cause": [
{
"type": "version_conflict_engine_exception",
"reason": "[lock][1]: version conflict, document already exists (current version [1])",
"index_uuid": "V98PfLHrRjKrnIkQU5IbPA",
"shard": "3",
"index": "waws_fs"
}
],
"type": "version_conflict_engine_exception",
"reason": "[lock][1]: version conflict, document already exists (current version [1])",
"index_uuid": "V98PfLHrRjKrnIkQU5IbPA",
"shard": "3",
"index": "waws_fs"
},
"status": 409
}
上排他锁,上共享锁
POST /waws_fs/lock/1/_update
{
"upsert": {
"lock_type": "shared",
"lock_count": 1
},
"script": {
"lang": "groovy",
"file": "judge-lock-2"
}
}
{
"error": {
"root_cause": [
{
"type": "remote_transport_exception",
"reason": "[w85Pu0f][127.0.0.1:9300][indices:data/write/update[s]]"
}
],
"type": "illegal_argument_exception",
"reason": "failed to execute script",
"caused_by": {
"type": "script_exception",
"reason": "error evaluating judge-lock-2",
"caused_by": {
"type": "power_assertion_error",
"reason": "assert false\n"
},
"script_stack": [],
"script": "",
"lang": "groovy"
}
},
"status": 400
}
解锁排他锁
DELETE /waws_fs/lock/1
Elasticsearch高手进阶篇(67)
数据建模实战_基于nested object实现博客与评论嵌套关系
nested object
冗余数据方式的来建模,其实用的就是object类型,我们这里又要引入一种新的object类型,nested object类型
博客,评论,做的这种数据模型
PUT /waws_webblog/blogs/6
{
"title": "花无缺发表的一篇帖子",
"content": "我是花无缺,大家要不要考虑一下投资房产和买股票的事情啊。。。",
"tags": [ "投资", "理财" ],
"comments": [
{
"name": "小鱼儿",
"comment": "什么股票啊?推荐一下呗",
"age": 28,
"stars": 4,
"date": "2016-09-01"
},
{
"name": "黄药师",
"comment": "我喜欢投资房产,风,险大收益也大",
"age": 31,
"stars": 5,
"date": "2016-10-22"
}
]
}
被年龄是28岁的黄药师评论过的博客,搜索
GET /waws_webblog/blogs/_search
{
"query": {
"bool": {
"must": [
{ "match": { "comments.name": "黄药师" }},
{ "match": { "comments.age": 28 }}
]
}
}
}
{
"took": 6,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 1.8022683,
"hits": [
{
"_index": "waws_webblog",
"_type": "blogs",
"_id": "6",
"_score": 1.8022683,
"_source": {
"title": "花无缺发表的一篇帖子",
"content": "我是花无缺,大家要不要考虑一下投资房产和买股票的事情啊。。。",
"tags": [
"投资",
"理财"
],
"comments": [
{
"name": "小鱼儿",
"comment": "什么股票啊?推荐一下呗",
"age": 28,
"stars": 4,
"date": "2016-09-01"
},
{
"name": "黄药师",
"comment": "我喜欢投资房产,风,险大收益也大",
"age": 31,
"stars": 5,
"date": "2016-10-22"
}
]
}
}
]
}
}
结果是好像不太对啊
object类型数据结构的底层存储
{
"title":[ "花无缺", "发表", "一篇", "帖子" ],
"content":[ "我", "是", "花无缺", "大家", "要不要", "考虑", "一下", "投资", "房产", "买", "股票", "事情" ],
"tags":[ "投资", "理财" ],
"comments.name":[ "小鱼儿", "黄药师" ],
"comments.comment":[ "什么", "股票", "推荐", "我", "喜欢", "投资", "房产", "风险", "收益", "大" ],
"comments.age":[ 28, 31 ],
"comments.stars":[ 4, 5 ],
"comments.date":[ 2016-09-01, 2016-10-22 ]
}
object类型底层数据结构,会将一个json数组中的数据,进行扁平化
所以,直接命中了这个document,name=黄药师,age=28,正好符合
引入nested object类型,来解决object类型底层数据结构导致的问题
修改mapping,将comments的类型从object设置为nested
# 先删除所有数据
DELETE /waws_webblog
PUT /waws_webblog
{
"mappings": {
"blogs": {
"properties": {
"comments": {
"type": "nested",
"properties": {
"name": {"type": "string"},
"comment": {"type": "string"},
"age": {"type": "short"},
"stars": {"type": "short"},
"date": {"type": "date"}
}
}
}
}
}
}
{
"comments.name":[ "小鱼儿" ],
"comments.comment":[ "什么", "股票", "推荐" ],
"comments.age":[ 28 ],
"comments.stars":[ 4 ],
"comments.date":[ 2014-09-01 ]
}
{
"comments.name":[ "黄药师" ],
"comments.comment":[ "我", "喜欢", "投资", "房产", "风险", "收益", "大" ],
"comments.age":[ 31 ],
"comments.stars":[ 5 ],
"comments.date":[ 2014-10-22 ]
}
{
"title":[ "花无缺", "发表", "一篇", "帖子" ],
"body":[ "我", "是", "花无缺", "大家", "要不要", "考虑", "一下", "投资", "房产", "买", "股票", "事情" ],
"tags":[ "投资", "理财" ]
}
再次搜索,成功了
GET /waws_webblog/blogs/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"title": "花无缺"
}
},
{
"nested": {
"path": "comments",
"query": {
"bool": {
"must": [
{
"match": {
"comments.name": "黄药师"
}
},
{
"match": {
"comments.age": 28
}
}
]
}
}
}
}
]
}
}
}
{
"took": 13,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 0,
"max_score": null,
"hits": []
}
}
- score_mode:max,min,avg,none,默认是avg
- 如果搜索命中了多个nested document,如何讲个多个nested document的分数合并为一个分数