본문 바로가기

AWS/ElasticSearch를 이용한 검색

2) ElasticSearch Alias와 index template 운영방안

반응형

앞에서 간단히 인덱스를 생성해서 해당 인덱스에 검색 데이터를 색인하는것을 테스트 해 보았다.

 

이제 좀 더 구체적으로 운영에 사용한다는 가정하에 인덱스를 생성하고 해당 검색데이터를 갱신하는 

운영 방안과 방법에 대해 고민해 본다.

 

먼저 인덱스 템플릿에 대해 알아보자. 

인덱스 템플릿은 말그대로 인덱스 생성을 위한 템플릿을 미리 설정해 놓고 해당 템플릿을 이용해 인덱스를 만드는것을 말한다. 

 

인덱스를 운영하는 방법에 대해 고민해 보자. 

인덱스의 검색 데이터를 색인하는 과장에서 해당 인덱스는 사용을 할수 없게 되거나 또는 검색데이터 수정 및 삭제를 하기보다는 

신규로 인덱스를 생성하고 해당 인덱스의  검색데이터를 새로 저장하는것이 더 빠르게 처리 되기 때문에 

보통 아래와같은 프로세스로 인덱스를 관린다.

1) 기존 index -1  유지 상태

2) 신규 index-2 생성 , 검색 데이터 색인

3) 기존 index-1 삭제 

 

위와 같은 간단한 단계를 이용해서 인덱스를 관리 하게 되는데 그럼 인덱스를 바로 볼때 index-1 을 사용하다가 갑자기 index-2로 바뀌면 

운영중이 서버에서 어떻게 인지를 하게 만들지? 라는 의문이 든다. 

그래서 alias라는 기능을 사용하게 된다.

 

index-1 ,index-2의 alias를 content-index 라고 이름을 주면 content-index 를 검색 하게되면 elasticsearh는 index-1, index-2 e둘다를 검색하게 된다.

 

이렇게 실제 elasticsearch를 보고 있는 서버에서는 content-index를 검색하고 있고 , 검색 데이터를 갱신하는 프로세스에서는 

위와 1~3 까지의 프로세스를 진행시키게 된다.

 

위와 프로세스를 구현해 보자.

 

1) 먼저 인덱스 템플릿을 만든다.

localhost:9200/_template/content-index-template
{
    "order": 0,
    "version": 1,
    "index_patterns": [
        "content-index-*"
    ],
    "settings": {
        "index": {
            "number_of_shards": "1",
            "number_of_replicas": "0",
            "analysis": {
                "analyzer": {
                    "nori": {
                        "type": "custom",
                        "tokenizer": "nori_mixed"
                    }
                },
                "tokenizer": {
                    "nori_mixed": {
                        "type": "nori_tokenizer",
                        "decompound_mode": "mixed"
                    }
                }
            }
        }
    },
    "mappings": {
        "properties": {
            "contentText": {
                "type": "text",
                "analyzer": "nori"
            },
            "title": {
                "type": "text",
                "analyzer": "nori"
            }
        }
    }
}

2) 인덱스 생성

인덱스 생성할때 좀 더 기존 index를 지우는것이 아니라 신규 인덱스와 스위칭 시키는 형식으로 되어야 한다.

( 최대한 인덱스 변경의 시간 갭을 줄이기 위함)

 

신규 인덱스 생성 -> 신규 인덱스에 검색 데이터 저장 ->  신규 인덱스의 alias 붙이것과 동시에 기존 index에서 alias 삭제

와 같은 프로세스로 진행한다.

 

@PostMapping(value="/create_content_index")
public Content create_content_index() {

    Content content = Content.builder()
            .contentId("content3")
            .title("테스트하고 있는 컨텐츠 입니다. aaa bbb ccc ddd ")
            .contentText("이것은 테스트하고 있는 컨텐츠. 서치조건1 시치조건2 우편함 , 우리나라  1111 22222 33333")
            .build();

    Content content1 = Content.builder()
            .contentId("content4")
            .title("우리나라 대한민국 대한민국만세 태극기만세 ")
            .contentText("동해물과 백두산이 마르고 닳도록 하느님이보우하사 우리나라만세")
            .build();
    List<Content> contents = Arrays.asList(content , content1);
    IndexCoordinates indexName = IndexUtil.createIndexNameWithPostFixWrapper(CONTENT_ALIAS_NAME);
    contentSearchRepository.saveAll(contents , indexName);
    changeAlias(CONTENT_ALIAS_NAME , indexName.getIndexName());
    return content1;
}

private void changeAlias(String aliasName, String indexName) {
    boolean result = false;
    IndicesAliasesRequest indicesAliasesRequest = new IndicesAliasesRequest();
    try {
        List<String> existIndexNames = contentSearchRepository.findIndexNamesByAlias(aliasName);

        IndicesAliasesRequest.AliasActions aliasAction =
                new IndicesAliasesRequest.AliasActions(IndicesAliasesRequest.AliasActions.Type.ADD)
                        .index(indexName)
                        .alias(aliasName);
        indicesAliasesRequest.addAliasAction(aliasAction);
        for(String existIndexName : existIndexNames){
            IndicesAliasesRequest.AliasActions aliasRemoveAction =
                    new IndicesAliasesRequest.AliasActions(IndicesAliasesRequest.AliasActions.Type.REMOVE)
                            .index(existIndexName)
                            .alias(aliasName);
            indicesAliasesRequest.addAliasAction(aliasRemoveAction);
        }

        AcknowledgedResponse acknowledgedResponse = contentSearchRepository.updateAlias(indicesAliasesRequest);
        result = acknowledgedResponse.isAcknowledged();

        if(result){
            for(String existIndexName : existIndexNames){
                contentSearchRepository.deleteIndex(IndexUtil.createIndexNameWrapper(existIndexName));
            }
        }

    } catch (Exception exception) {
        log.error("update alias exception", exception);
        throw new RuntimeException("update alias exception");
    }
}

changeAlias 부분이 신규 생성된 index에 alias 를 붙여주고 기존에 해당 alias로 연결된 Index를 연결 해제 하고 삭제 하는 프로세스가 구현 되어 있다.

 

@Override
public List<String> findIndexNamesByAlias(String aliasName) {
    List<String> indexNameList = new ArrayList<>();
    GetAliasesRequest getAliasesRequest = new GetAliasesRequest(aliasName);
    GetAliasesResponse getAliasesResponse = null;
    try {
        getAliasesResponse = client.indices().getAlias(getAliasesRequest, RequestOptions.DEFAULT);
    }catch(IOException ioException){
        log.error("alias search fail" , ioException);
    }
    if(getAliasesResponse != null && getAliasesResponse.getAliases() != null && getAliasesResponse.getAliases().keySet().size() > 0){
        indexNameList.addAll(getAliasesResponse.getAliases().keySet());
    }

    return indexNameList;
}

@Override
public AcknowledgedResponse updateAlias(IndicesAliasesRequest indicesAliasesRequest) throws Exception {
    return client.indices().updateAliases(indicesAliasesRequest, RequestOptions.DEFAULT);
}

@Override
public boolean deleteIndex(IndexCoordinates indexNameWrapper) {
    IndexOperations indexOperations = operations.indexOps(indexNameWrapper);
    return indexOperations.delete();
}

이렇게 인덱스 를 생성하고 실제 운영에서 어떻게 운영해야 하는지에 대해 구현해 보았다.

 

정상적으로 잘 작동되는지 확인해 보자.

localhost:8080/create_content_index

cerebro를 키고 인덱스가 잘 생성 되었는지 확인해 본다.

 

정상적으로 인덱스가 생성 되었고 설정한 인덱스 템플릿으로 잘 적용되었다.

이제 다시 인덱스를 생성 했을때 기존 인덱스가 잘 삭제되는지 확인해 보자.

 

인덱스 이름이 변경 되었고 alias로 잘 붙은것을 확인했다. 

다음에는 템플릿 생성시 설정한 한글분석기가 어떻게 동작하는지 알아 보자.