1. 程式人生 > >elasticsearch 基礎 —— Delete By Query API

elasticsearch 基礎 —— Delete By Query API

Delete By Query API

_delete_by_query 的簡單用法,就是在查詢匹配到的每個文件上執行刪除。例如:

POST twitter/_delete_by_query
{
  "query": { ①
    "match": {
      "message": "some message"
    }
  }
}

①:查詢必須是有效的鍵值對,query是鍵,這和Search API是同樣的方式。在search apiq引數和上面效果是一樣的。

返回如下內容:

{
  "took" : 147,
  "timed_out": false,
  "deleted": 119,
  "batches": 1,
  "version_conflicts": 0,
  "noops": 0,
  "retries": {
    "bulk": 0,
    "search": 0
  },
  "throttled_millis": 0,
  "requests_per_second": -1.0,
  "throttled_until_millis": 0,
  "total": 119,
  "failures" : [ ]
}

 _delete_by_query在索引啟動時獲取索引的快照,並使用internal版本控制刪除它找到的內容。這意味著如果文件在拍攝快照的時間和處理刪除請求之間發生更改,則會出現版本衝突。當版本匹配時,文件將被刪除。

由於internal版本控制不支援將值0作為有效版本號,因此無法使用版本等於0的文件刪除, _delete_by_query並且將使請求失敗。

_delete_by_query執行期間,順序執行多個搜尋請求以便找到要刪除的所有匹配文件。每次找到一批文件時,都會執行相應的批量請求以刪除所有這些文件。如果搜尋或批量請求被拒絕,則_delete_by_query依賴於預設策略來重試被拒絕的請求(最多10次,指數後退)。達到最大重試次數限制會導致_delete_by_query

 中止,並failures在響應中返回所有失敗。已執行的刪除仍然有效。換句話說,該過程不會回滾,只會中止。當第一個失敗導致中止時,失敗的批量請求返回的所有失敗都將返回到failures元件; 因此,可能存在相當多的失敗實體。

如果您想計算版本衝突而不是讓它們中止,那麼請conflicts=proceed在URL或"conflicts": "proceed"請求正文中設定。

下面僅僅只是刪除索引twitter中型別tweet的所有資料:

POST twitter/tweet/_delete_by_query?conflicts=proceed
{
  "query": {
    "match_all": {}
  }
}

一次刪除多個索引中的多個型別中的資料,也是可以的。例如:

POST twitter,blog/tweet,post/_delete_by_query
{
  "query": {
    "match_all": {}
  }
}

如果你提供了routing,接著這個路由會被複制給scroll query,根據匹配到的路由值,來決定哪個分片來處理:

POST twitter/_delete_by_query?routing=1
{
  "query": {
    "range" : {
        "age" : {
           "gte" : 10
        }
    }
  }
}

預設情況下,_delete_by_query自上而下批量1000條資料,你也可以在URL中使用引數scroll_size

POST twitter/_delete_by_query?scroll_size=5000
{
  "query": {
    "term": {
      "user": "kimchy"
    }
  }
}

URL引數

除了標準的引數,如pretty,刪除通過查詢API也支援refreshwait_for_completionwait_for_active_shardstimeout 和scroll

傳送refresh請求將在請求完成後重新整理查詢刪除中涉及的所有分片。這與Delete API的refresh 引數不同,後者僅導致接收刪除請求的分片被重新整理。

如果請求包含,wait_for_completion=false則Elasticsearch將執行一些預檢檢查,啟動請求,然後返回task 可與Tasks API 一起使用以取消或獲取任務狀態的請求。Elasticsearch還將建立此任務的記錄作為文件.tasks/task/${taskId}。這是你的保留或刪除你認為合適。完成後,刪除它,以便Elasticsearch可以回收它使用的空間。

wait_for_active_shards控制在繼續請求之前必須啟用碎片的副本數量。詳情請見此處 。timeout控制每個寫入請求等待不可用分片變為可用的時間。兩者都完全適用於 Bulk API中的工作方式。由於_delete_by_query採用滾動搜尋,你還可以指定scroll引數來控制多長時間保持“搜尋上下文”活著,例如?scroll=10m,預設情況下它是5分鐘。

requests_per_second可以被設定為任何正十進位制數(1.46, 1000等)和節流速率_delete_by_query通過填充每個批次由一等待時間發出的刪除操作的批次。可以通過設定requests_per_second為禁用限制-1

通過在批處理之間等待來完成限制,以便在_delete_by_query內部使用的滾動 可以被賦予考慮填充的超時。填充時間是批量大小除以requests_per_second寫入所花費的時間之間的差異。預設情況下,批處理大小為1000,因此如果requests_per_second設定為500

target_time = 1000 / 500 per second = 2 seconds
wait_time = target_time - write_time = 2 seconds - .5 seconds = 1.5 seconds

由於批處理是作為單個_bulk請求發出的,因此大批量資料將導致Elasticsearch建立許多請求,然後等待一段時間再開始下一組。這是“突發”而不是“平滑”。預設是-1

Response body

{
  "took" : 147,
  "timed_out": false,
  "total": 119,
  "deleted": 119,
  "batches": 1,
  "version_conflicts": 0,
  "noops": 0,
  "retries": {
    "bulk": 0,
    "search": 0
  },
  "throttled_millis": 0,
  "requests_per_second": -1.0,
  "throttled_until_millis": 0,
  "failures" : [ ]
}

took

整個操作從開始到結束的毫秒數。

timed_out

true如果在通過查詢執行刪除期間執行的任何請求超時 ,則將此標誌設定為。

total

已成功處理的文件數。

deleted

已成功刪除的文件數。

batches

通過查詢刪除拉回的滾動響應數。

version_conflicts

按查詢刪除的版本衝突數。

noops

對於按查詢刪除,此欄位始終等於零。它只存在,以便通過查詢刪除,按查詢更新和reindex API返回具有相同結構的響應。

retries

通過查詢刪除嘗試的重試次數。bulk是重試的批量操作search的數量,是重試的搜尋操作的數量。

throttled_millis

請求睡眠符合的毫秒數requests_per_second

requests_per_second

在通過查詢刪除期間有效執行的每秒請求數。

throttled_until_millis

在按查詢響應刪除時,此欄位應始終等於零。它只在使用Task API時有意義,它指示下一次(自紀元以來的毫秒數),為了符合,將再次執行受限制的請求requests_per_second

failures

如果在此過程中存在任何不可恢復的錯誤,則會出現故障陣列。如果這是非空的,那麼請求因為那些失敗而中止。逐個查詢是使用批處理實現的,任何故障都會導致整個程序中止,但當前批處理中的所有故障都會被收集到陣列中。您可以使用該conflicts選項來防止reindex在版本衝突中中止。

Works with the Task API

你可以使用Task API來獲取任何一個正在執行的delete-by-query請求的狀態。

GET _tasks?detailed=true&actions=*/delete/byquery

返回如下內容:

{
  "nodes" : {
    "r1A2WoRbTwKZ516z6NEs5A" : {
      "name" : "r1A2WoR",
      "transport_address" : "127.0.0.1:9300",
      "host" : "127.0.0.1",
      "ip" : "127.0.0.1:9300",
      "attributes" : {
        "testattr" : "test",
        "portsfile" : "true"
      },
      "tasks" : {
        "r1A2WoRbTwKZ516z6NEs5A:36619" : {
          "node" : "r1A2WoRbTwKZ516z6NEs5A",
          "id" : 36619,
          "type" : "transport",
          "action" : "indices:data/write/delete/byquery",
          "status" : {    ①
            "total" : 6154,
            "updated" : 0,
            "created" : 0,
            "deleted" : 3500,
            "batches" : 36,
            "version_conflicts" : 0,
            "noops" : 0,
            "retries": 0,
            "throttled_millis": 0
          },
          "description" : ""
        }
      }
    }
  }
}

①這個物件包含實際的狀態。響應體是json格式,其中total欄位是非常重要的。total表示期望執行reindex操作的數量。你可以通過加入的updatedcreateddeleted欄位來預估進度。但它們之和等於total欄位時,請求將結束。

使用task id可以直接查詢此task

GET /_tasks/taskId:1

這個api的優點是它整合了wait_for_completion=false來透明的返回已完成任務的狀態。如果此任務完成並且設定為wait_for_completion=false,那麼其將返回results或者error欄位。這個特性的代價就是當設定wait_for_completion=false時,會在.tasks/task/${taskId}中建立一個文件。當然你也可以刪除這個文件。

Works with the Cancel Task API

任何一個Delete By Query都可以使用Task Cancel API來取消掉:

POST _tasks/task_id:1/_cancel

可以使用上面的task api來找到task_id;  取消應該儘快發生,但是也可能需要幾秒鐘,上面的task 狀態 api將會進行列出task直到它被喚醒並取消自己。

Rethrottling

requests_per_second的值可以在使用_rethrottle引數的正在執行的delete by queryapi上進行更改:

POST _delete_by_query/task_id:1/_rethrottle?requests_per_second=-1

使用上面的tasks API來查詢task_id

就像在_delete_by_query中設定一樣,requests_per_second可以設定-1來禁止這種限制或者任何一個10進位制數字,像1.7或者12來限制到這種級別。加速查詢的Rethrottling會立即生效,但是緩慢查詢的Rethrottling將會在完成當前批處理後生效。這是為了防止scroll timeouts

Manually slicing

Delete-by-query支援Sliced Scroll,其可以使你相對容易的手動並行化程序:

POST twitter/_delete_by_query
{
  "slice": {
    "id": 0,
    "max": 2
  },
  "query": {
    "range": {
      "likes": {
        "lt": 10
      }
    }
  }
}
POST twitter/_delete_by_query
{
  "slice": {
    "id": 1,
    "max": 2
  },
  "query": {
    "range": {
      "likes": {
        "lt": 10
      }
    }
  }
}

你可以通過以下方式進行驗證:

GET _refresh
POST twitter/_search?size=0&filter_path=hits.total
{
  "query": {
    "range": {
      "likes": {
        "lt": 10
      }
    }
  }
}

像下面這樣只有一個total是合理的:

{
  "hits": {
    "total": 0
  }
}

Automatic slicing

你也可以使用Sliced Scrolldelete-by-query api自動並行化,以在_uid上切片:

POST twitter/_delete_by_query?refresh&slices=5
{
  "query": {
    "range": {
      "likes": {
        "lt": 10
      }
    }
  }
}

你可以通過以下來驗證:

POST twitter/_search?size=0&filter_path=hits.total
{
  "query": {
    "range": {
      "likes": {
        "lt": 10
      }
    }
  }
}

像下面的total是一個合理的結果:

{
  "hits": {
    "total": 0
  }
}

新增slices_delete_by_query將會自動執行上面部分中使用手動處理的部分,建立子請求這意味著有些怪事:

  1. 你可以在Tasks APIs中看到這些請求。這些子請求是使用了slices請求任務的子任務。
  2. 為此請求(使用了slices)獲取任務狀態僅僅包含已完成切片的狀態。
  3. 這些子請求都是獨立定址的,例如:取消和rethrottling.
  4. Rethrottling the request with slices will rethrottle the unfinished sub-request proportionally.
  5. 取消slices請求將會取消每個子請求。
  6. 由於slices的性質,每個子請求並不會得到完全均勻的文件結果。所有的文件都將要處理,但是有些slices(切片)會大些,有些會小些。希望大的slices(切片)有更均勻的分配。
  7. slices請求中像requests_per_secondsize引數,按比例分配給每個子請求。結合上面的關於分配的不均勻性,你應該得出結論:在包含slices_delete_by_query請求中使用size引數可能不會得到正確大小的文件結果。
  8. 每個子請求都會獲得一個略微不同的源索引快照,儘管這些請求都是大致相同的時間。

Picking the number of slices

這裡我們有些關於slices數量的建議(如果是手動並行的話,那麼在slice api就是max引數):

  1. 不要使用大數字。比如500,將會建立相當大規模的CPU震盪。  這裡說明下震盪(thrashing)的意思: cpu大部分時間都在進行換頁,而真正工作時間卻很短的現象稱之為thrashing (震盪)
  2. 從查詢效能角度來看,在源索引中使用多個分片是更高效的。
  3. 從查詢效能角度來看,在源索引中使用和分片相同的數量是更高效的。
  4. 索引效能應該在可利用slices之間進行線性擴充套件。
  5. 索引(插入)或查詢效能是否占主導地位取決於諸多因素,比如:重新索引文件和叢集進行重新索引。