Elasticsearch-同時競合およびソリューション
10310 ワード
1.同時衝突の詳細
電子商取引のシーンの下で、仕事の流れは以下の通りです.
マルチスレッド操作であれば、上記の3ステップフローを複数のスレッドが同時に実行する可能性があり、このとき2人が商品データを読み取りに来た場合、2つのスレッドが同時に2人にサービスし、同時に商品在庫データの修正を行う.正しい場合:スレッドAは在庫-1を99件に設定し、スレッドBは99件を読み取り、-1を98件に変更します.A,Bスレッドのいずれも100件を読み出し,A処理が完了したら99件,B処理が完了してから再び99件に変更した場合,結果はエラーとなる.
2.ソリューション
2.1悲観ロック
商品データを読み取ると同時に、この行のデータにロックをかけ、このスレッドがデータを処理した後、ロックを解除し、別のスレッドが処理を開始します.
悲観的ロック同時制御方式は、様々な場合にロックされる.ロックした後、このデータを操作できるスレッドは1つしかありません.異なるシーンの下で、上のロックは異なり、行レベルロック、表レベルロック、リードロック、ライトロックがあります.
2.2楽観ロック
楽観的なロックはロックされず、各スレッドは任意に操作できます.Esの各ドキュメントにはversionフィールドがあり、新規ドキュメントを作成すると1となり、1回の累積を修正し、スレッドA,Bが同時にデータを読み取り、version=1、Aが処理された後に在庫が99となり、esを書き込む際にesのバージョン番号と比較され、いずれも1となると書き込みに成功し、version=2、Bが処理された後も99となり、esに格納する際にesのデータのバージョン番号version=2と比較して明らかに異なり、この場合99で更新することはありません.最新のデータを読み直し、さらに1を減らして98にし、上記の操作を実行して書き込みます.
2.3 Elasticsearchの楽観ロック
Elasticsearchのバックグラウンドはいずれもマルチスレッド非同期であり,複数のリクエスト間は乱順であり,後で変更される可能性がある先着,先に変更される後着である.
Elasticsearchのマルチスレッド非同期同時修正は独自の_バージョン番号は楽観的なロックと同時制御を行います.
后で改正の先で着いて、改正が终わった后で、先に改正の后で着いて、比较してみますバージョン番号は、等しくなければそのまま捨てて、不要です.これにより、結果は正しい状態に保存されます.
コードの例:
PUT /test_index/test_type/3
{
"test_field": "test test"
}
:
{
"_index": "test_index",
"_type": "test_type",
"_id": "3",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": true
}
PUT /test_index/test_type/3
{
"test_field": "test1 test1"
}
{
"_index": "test_index",
"_type": "test_type",
"_id": "3",
"_version": 2,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": true
}
DELETE /test_index/test_type/3
:
{
"_index": "test_index",
"_type": "test_type",
"_id": "3",
"_version": 3,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": true
}
PUT /test_index/test_type/3
{
"test_field": "test1 test1"
}
{
"_index": "test_index",
"_type": "test_type",
"_id": "4",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": true
}
削除操作では、このデータのバージョン番号にも1が加算されます.
documentを削除した後、バージョン番号などの情報が残っているため、すぐに物理的に削除されたわけではないことを側面から証明することができます.まずdocumentを削除してから、このdocumentを再作成します.実はdelete versionの基礎の上に、version番号を1追加します.
2.4 esの楽観ロック同時制御例
PUT /test_index/test_type/4
{
"test_field": "test"
}
GET /test_index/test_type/4
{
"_index": "test_index",
"_type": "test_type",
"_id": "4",
"_version": 1,
"found": true,
"_source": {
"test_field": "test"
}
}
PUT test_index/test_type/4?version=1
{
"test_field": "client1 changed"
}
{
"_index": "test_index",
"_type": "test_type",
"_id": "4",
"_version": 2,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": false
}
PUT test_index/test_type/4?version=1
{
"test_field": "client2 changed"
}
,
{
"error": {
"root_cause": [
{
"type": "version_conflict_engine_exception",
"reason": "[test_type][4]: version conflict, current version [2] is different than the one provided [1]",
"index_uuid": "rsiZYqiwSCC2XdR8N2bJow",
"shard": "2",
"index": "test_index"
}
],
"type": "version_conflict_engine_exception",
"reason": "[test_type][4]: version conflict, current version [2] is different than the one provided [1]",
"index_uuid": "rsiZYqiwSCC2XdR8N2bJow",
"shard": "2",
"index": "test_index"
},
"status": 409
}
楽観ロックは同時問題GET /test_index/test_type/4
{
"_index": "test_index",
"_type": "test_type",
"_id": "4",
"_version": 2,
"found": true,
"_source": {
"test_field": "client1 changed"
}
}
が最新のデータとバージョン番号に基づいて修正を行い、修正後、最新のバージョン番号を持っていくと、このステップは何度も繰り返し実行する必要がある可能性があり、特にマルチスレッドが同じデータを同時更新することが頻繁な場合、PUT /test_index/test_type/4?version=2
{
"test_field": "client2 changed"
}
{
"_index": "test_index",
"_type": "test_type",
"_id": "4",
"_version": 3,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": false
}
2.5 external versionに基づく楽観的ロック同時制御
Esはfeatureを提供しています.つまり、内部を提供しなくてもいいということです.バージョン番号は同時制御を行い、自分でメンテナンスしたバージョン番号に基づいて同時制御を行うことができます.
?version=1&version_type=external
version_type=external、唯一の違いは、バージョン、あなたが提供したバージョンとesの中の_だけバージョンがそっくりの場合、修正することができます.違いさえあれば、間違いを報告します.version_type=externalの場合、あなたが提供したversion比esの中の_だけです.バージョンが大きい場合、修正が完了します.
es,_version=1,?version=1で、成功esを更新できます.version=1,?version>1&version_type=external、成功することができます.例えば?version=2&version_type=external
コードの例:
PUT test_index/test_type/5
{
"test_field": "external test"
}
{
"_index": "test_index",
"_type": "test_type",
"_id": "5",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": true
}
GET /test_index/test_type/5
{
"_index": "test_index",
"_type": "test_type",
"_id": "5",
"_version": 1,
"found": true,
"_source": {
"test_field": "external test"
}
}
PUT /test_index/test_type/5?version=2&version_type=external
{
"test_field": "external client1 changed"
}
{
"_index": "test_index",
"_type": "test_type",
"_id": "5",
"_version": 2,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": false
}
PUT /test_index/test_type/5?version=2&version_type=external
{
"test_field": "external client2 changed"
}
,
{
"error": {
"root_cause": [
{
"type": "version_conflict_engine_exception",
"reason": "[test_type][5]: version conflict, current version [2] is higher or equal to the one provided [2]",
"index_uuid": "rsiZYqiwSCC2XdR8N2bJow",
"shard": "1",
"index": "test_index"
}
],
"type": "version_conflict_engine_exception",
"reason": "[test_type][5]: version conflict, current version [2] is higher or equal to the one provided [2]",
"index_uuid": "rsiZYqiwSCC2XdR8N2bJow",
"shard": "1",
"index": "test_index"
},
"status": 409
}
GET /test_index/test_type/5
{
"_index": "test_index",
"_type": "test_type",
"_id": "5",
"_version": 2,
"found": true,
"_source": {
"test_field": "external client1 changed"
}
}
PUT /test_index/test_type/5?version=3&version_type=external
{
"test_field": "external client2 changed"
}
{
"_index": "test_index",
"_type": "test_type",
"_id": "5",
"_version": 3,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"created": false
}
転載先:https://juejin.im/post/5c66afb0e51d452a1c6159cc