PUT /test_index/test_type/7 { "test_field": "test test" }
GET test_index/test_type/7 { "_index": "test_index", "_type": "test_type", "_id": "7", "_version": 1, "found": true, "_source": { "test_field": "test test" } }
其中一個客戶端,先更新了一下這個數據mysql
同時帶上數據的版本號,確保說,es中的數據的版本號,跟客戶端中的數據的版本號是相同的,才能修改PUT /test_index/test_type/7?version=1 { "test_field": "test client 1" }
{ "_index": "test_index", "_type": "test_type", "_id": "7", "_version": 2, "result": "updated", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "created": false }
PUT /test_index/test_type/7?version=1 { "test_field": "test client 2" }
{ "error": { "root_cause": [ { "type": "version_conflict_engine_exception", "reason": "[test_type][7]: version conflict, current version [2] is different than the one provided [1]", "index_uuid": "6m0G7yx7R1KECWWGnfH1sw", "shard": "3", "index": "test_index" } ], "type": "version_conflict_engine_exception", "reason": "[test_type][7]: version conflict, current version [2] is different than the one provided [1]", "index_uuid": "6m0G7yx7R1KECWWGnfH1sw", "shard": "3", "index": "test_index" }, "status": 409 }
GET /test_index/test_type/7 { "_index": "test_index", "_type": "test_type", "_id": "7", "_version": 2, "found": true, "_source": { "test_field": "test client 1" } } 基於最新的數據和版本號,去進行修改,修改後,帶上最新的版本號,可能這個步驟會須要反覆執行好幾回,才能成功,特別是在多線程併發更新同一條數據很頻繁的狀況下 PUT /test_index/test_type/7?version=2 { "test_field": "test client 2" } { "_index": "test_index", "_type": "test_type", "_id": "7", "_version": 3, "result": "updated", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "created": false }
es提供了一個feature,就是說,你能夠不用它提供的內部_version版本號來進行併發控制,能夠基於你本身維護的一個版本號來進行併發控制。舉個列子,加入你的數據在mysql裏也有一份,而後你的應用系統自己就維護了一個版本號,不管是什麼本身生成的,程序控制的。這個時候,你進行樂觀鎖併發控制的時候,可能並非想要用es內部的_version來進行控制,而是用你本身維護的那個version來進行控制。sql
?version=1 ?version=1&version_type=external
version_type=external,惟一的區別在於,_version,只有當你提供的version與es中的_version如出一轍的時候,才能夠進行修改,只要不同,就報錯;當version_type=external的時候,只有當你提供的version比es中的_version大的時候,才能完成修改數據庫
es,_version=1,?version=1,才能更新成功 es,_version=1,?version>1&version_type=external,才能成功,好比說?version=2&version_type=external
PUT /test_index/test_type/8 { "test_field": "test" }
{ "_index": "test_index", "_type": "test_type", "_id": "8", "_version": 1, "result": "created", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "created": true }
GET /test_index/test_type/8 { "_index": "test_index", "_type": "test_type", "_id": "8", "_version": 1, "found": true, "_source": { "test_field": "test" } }
PUT /test_index/test_type/8?version=2&version_type=external { "test_field": "test client 1" } { "_index": "test_index", "_type": "test_type", "_id": "8", "_version": 2, "result": "updated", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "created": false }
PUT /test_index/test_type/8?version=2&version_type=external { "test_field": "test client 2" } { "error": { "root_cause": [ { "type": "version_conflict_engine_exception", "reason": "[test_type][8]: version conflict, current version [2] is higher or equal to the one provided [2]", "index_uuid": "6m0G7yx7R1KECWWGnfH1sw", "shard": "1", "index": "test_index" } ], "type": "version_conflict_engine_exception", "reason": "[test_type][8]: version conflict, current version [2] is higher or equal to the one provided [2]", "index_uuid": "6m0G7yx7R1KECWWGnfH1sw", "shard": "1", "index": "test_index" }, "status": 409 }
GET /test_index/test_type/8 { "_index": "test_index", "_type": "test_type", "_id": "8", "_version": 2, "found": true, "_source": { "test_field": "test client 1" } } PUT /test_index/test_type/8?version=3&version_type=external { "test_field": "test client 2" } { "_index": "test_index", "_type": "test_type", "_id": "8", "_version": 3, "result": "updated", "_shards": { "total": 2, "successful": 1, "failed": 0 }, "created": false }
通常對應到應用程序中,每次的執行流程基本是這樣的:json
partial update是僅僅進行部分更新,無需全量替換網絡
語法:多線程
post /index/type/id/_update { "doc": { "要修改的少數幾個field便可,不須要全量的數據" } }
看起來,好像就比較方便了,每次就傳遞少數幾個發生修改的field便可,不須要將全量的document數據發送過去併發
全量更新的原理ide
其實es內部對partial update的實際執行,跟傳統的全雖替換方式,是幾乎同樣的post
partial update相較於全星替換的優勢:性能
PUT /test_index/test_type/10 { "test_field1": "test1", "test_field2": "test2" }
partial update
POST /test_index/test_type/10/_update { "doc": { "test_field2": "updated test2" } }
ES實際上是有個內置的腳本支持的,能夠基於groovy腳本實現各類各樣的複雜操做,基於groovy腳本,如何執行partial update
初始化腳本:
PUT /test_index/test_type/11 { "num": 0, "tags": [] }
POST /test_index/test_type/11/_update { "script": "ctx._source.num+=1" }
ctx._source.tags+=new_tag
POST /test_index/test_type/11/_update { "script": { "lang": "groovy", "file": "test-add-yags", "params": { "new_tag":"tag1" } } }
GET /test_index/test_type/11 { "_index": "test_index", "_type": "test_type", "_id": "11", "_version": 3, "found": true, "_source": { "num": 1, "tags": [ "tag1" ] } }
ctx.op = ctx._source.num == count ? 'delete' : 'none'
POST /test_index/test_type/11/_update { "script": { "lang": "groovy", "file": "test-delete-document", "params": { "count":1 } } }
{ "_index": "test_index", "_type": "test_type", "_id": "11", "_version": 4, "result": "deleted", "_shards": { "total": 2, "successful": 1, "failed": 0 } }
POST /test_index/test_type/11/_update { "doc": { "num": 1 } } { "error": { "root_cause": [ { "type": "document_missing_exception", "reason": "[test_type][11]: document missing", "index_uuid": "d7GOSxVnTNKYuI8x7cZfkA", "shard": "4", "index": "test_index" } ], "type": "document_missing_exception", "reason": "[test_type][11]: document missing", "index_uuid": "d7GOSxVnTNKYuI8x7cZfkA", "shard": "4", "index": "test_index" }, "status": 404 }
upsert操做
POST /test_index/test_type/11/_update { "script": "ctx._source.num+=1", "upsert": { "num": 0, "tags": [] } }
partial update內部會自動執行咱們以前所說的樂觀鎖的併發控制策
避免自動partial update fail掉的解決方案,使用 retry_on_conflict
retry策略
示例:
post /index/type/id/_update?retry_on_conflict=5&version=6