[英]ElasticSearch : Concurrent updates to index while _reindex for the same index in progress
我們一直使用此鏈接作為參考,以在零停機時間的情況下適應索引中字段映射的任何變化。
問題:考慮上述鏈接中的相同示例,當我們使用_reindex API 將數據從 my_index_v1 重新索引到 my_index_v2 時。ElasticSearch 是否保證 my_index_v1 中發生的任何並發更新肯定會到達 my_index_v2?
例如,在 api 重新索引到 my_index_v2 之前或之后,文檔可能會在 my_index_v1 中更新。
最終,我們只需要確保雖然我們不希望因進行任何映射更改而停機(因此 _reindex 使用別名和 ES 的其他很酷的東西),我們還希望確保在此期間不會遺漏任何添加/更新巨大的重新索引正在進行中,因為我們正在談論重新索引 >50GB 的數據。
謝謝,
桑迪普
解決此問題的一種方法是使用兩個別名而不是一個別名。 一種用於查詢(我們稱之為read_alias ),一種用於索引( write_alias )。 我們可以編寫我們的代碼,以便所有索引都通過write_alias 進行,所有查詢都通過read_alias 進行。 讓我們考慮三個時間段:
重建前
read_alias : 指向current_index
所有查詢都返回當前數據。
所有修改都進入current_index 。
重建期間
read_alias : 指向current_index
由於搜索代碼使用read_alias ,因此所有查詢都會繼續獲取重建之前存在的數據。
所有行,包括修改的行,都被索引到new_index 中,因為重建循環和數據庫觸發器都使用write_alias 。
重建后
read_alias : 指向new_index
所有查詢都返回新數據,包括重建期間所做的修改。
所有修改都進入new_index 。
如果我們在重建過程中(即,當別名指向不同的索引時)將 DB 觸發器代碼索引修改后的行設置為兩個索引,則甚至應該可以在重建時從查詢中獲取修改后的數據。
使用自定義代碼而不是依賴_reindex API 從源數據重建索引通常更好,因為這樣我們可以添加可能未存儲在舊索引中的新字段。
這篇文章有更多細節。
reindex api 不會考慮進程啟動后所做的更改。您可以做的一件事是完成重新索引進程后。您可以再次使用version_type:external啟動進程。 這將導致只有從源索引到目標索引的文檔具有不同的版本並且不存在
這是例子
POST _reindex
{
"source": {
"index": "twitter"
},
"dest": {
"index": "new_twitter",
"version_type": "external"
}
}
將 version_type 設置為 external 將導致 Elasticsearch 保留源中的版本,創建任何丟失的文檔,並更新目標索引中版本比源索引中版本舊的任何文檔:
看起來它是基於源索引的快照來做的。
這對我來說意味着他們無法合理地尊重在流程中間發生的源更改。 您可以避免搜索端的停機時間,但我認為您需要在此過程中暫停索引端的更新。
您可以做的是跟蹤文檔上次修改時間的索引。 然后,一旦完成索引並切換別名,就可以查詢舊索引以了解中間更改的內容。 將這些更改傳播到新索引,您將獲得最終的一致性。
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Base64;
import java.util.List;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
*
* @author user
*/
public class JWebToken {
private static final String SECRET_KEY = "FREE_MASON"; //@TODO Add Signature here
private static final char[] HEX_ARRAY = "0123456789ABCDEF".toCharArray();
private static final String ISSUER = "mason.metamug.net";
private static final String JWT_HEADER = "{\"alg\":\"HS256\",\"typ\":\"JWT\"}";
private JSONObject payload = new JSONObject();
private String signature;
private String encodedHeader;
private JWebToken() {
encodedHeader = encode(new JSONObject(JWT_HEADER));
}
public JWebToken(JSONObject payload) {
this(payload.getString("sub"), payload.getJSONArray("aud"), payload.getLong("exp"));
}
public JWebToken(String sub, JSONArray aud, long expires) {
this();
payload.put("sub", sub);
payload.put("aud", aud);
payload.put("exp", expires);
payload.put("iat", LocalDateTime.now().toEpochSecond(ZoneOffset.UTC));
payload.put("iss", ISSUER);
payload.put("jti", UUID.randomUUID().toString()); //how do we use this?
signature = hmacSha256(encodedHeader + "." + encode(payload), SECRET_KEY);
}
/**
* For verification
*
* @param token
* @throws java.security.NoSuchAlgorithmException
*/
public JWebToken(String token) throws NoSuchAlgorithmException {
this();
String[] parts = token.split("\\.");
if (parts.length != 3) {
throw new IllegalArgumentException("Invalid Token format");
}
if (encodedHeader.equals(parts[0])) {
encodedHeader = parts[0];
} else {
throw new NoSuchAlgorithmException("JWT Header is Incorrect: " + parts[0]);
}
payload = new JSONObject(decode(parts[1]));
if (payload.isEmpty()) {
throw new JSONException("Payload is Empty: ");
}
if (!payload.has("exp")) {
throw new JSONException("Payload doesn't contain expiry " + payload);
}
signature = parts[2];
}
@Override
public String toString() {
return encodedHeader + "." + encode(payload) + "." + signature;
}
public boolean isValid() {
return payload.getLong("exp") > (LocalDateTime.now().toEpochSecond(ZoneOffset.UTC)) //token not expired
&& signature.equals(hmacSha256(encodedHeader + "." + encode(payload), SECRET_KEY)); //signature matched
}
public String getSubject() {
return payload.getString("sub");
}
public List<String> getAudience() {
JSONArray arr = payload.getJSONArray("aud");
List<String> list = new ArrayList<>();
for (int i = 0; i < arr.length(); i++) {
list.add(arr.getString(i));
}
return list;
}
private static String encode(JSONObject obj) {
return encode(obj.toString().getBytes(StandardCharsets.UTF_8));
}
private static String encode(byte[] bytes) {
return Base64.getUrlEncoder().withoutPadding().encodeToString(bytes);
}
private static String decode(String encodedString) {
return new String(Base64.getUrlDecoder().decode(encodedString));
}
/**
* Sign with HMAC SHA256 (HS256)
*
* @param data
* @return
* @throws Exception
*/
private String hmacSha256(String data, String secret) {
try {
//MessageDigest digest = MessageDigest.getInstance("SHA-256");
byte[] hash = secret.getBytes(StandardCharsets.UTF_8);//digest.digest(secret.getBytes(StandardCharsets.UTF_8));
Mac sha256Hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKey = new SecretKeySpec(hash, "HmacSHA256");
sha256Hmac.init(secretKey);
byte[] signedBytes = sha256Hmac.doFinal(data.getBytes(StandardCharsets.UTF_8));
return encode(signedBytes);
} catch (NoSuchAlgorithmException | InvalidKeyException ex) {
Logger.getLogger(JWebToken.class.getName()).log(Level.SEVERE, ex.getMessage(), ex);
return null;
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.