게시글이 10만개가 넘으니 게시글 검색이 느려지는게 체감이 점점 오더니 결국엔 엘라스틱서치를 자체적으로 적용(개발)하게 되었습니다.

 

엘라스틱서치를 이번 기회에 처음 만지게 되었는데, NoSQL을 이렇게 본격적으로 만져본 적은 처음이기도 하고 해서 생각보다 많이 낮설었습니다.

 

 

처음엔 형태소 분리에서 헤메다가 계속 삽질을 하다보니 해결하였으나... 여러모로 넘여야 할 산들이 많더군요 ㅋㅋㅋ

elasticsearch가 NoSQL이다보니 기존 RDBMS의 서브쿼리, join같은 기능을 사용하지 못하는 점이 젤 힘들었습니다.

 

특히 제일 문제가 되었던 점은

- 댓글, 확장변수 검색 상태에서 게시글 정렬시 voted_count, blamed_count, readed_count, comment_count 등.. 기준으로 정렬

문제였습니다.

 

기존 mysql에선 그냥 join한 다음에 group by 하면 쉽게 해결될 문제지만 엘라스틱 서치에선 이런 기능을 지원하지 않으니 기존 데이터를 비정규화 작업을 한 다음에 elasticsearch 작업에 넣는 작업이....정말 만만찮은 작업이더라구요.

정상적인 방식에선 적어도 2~3개의 인덱스(mysql에선 테이블. mysql의 인덱스와는 다른 개념입니다.)를 참조해야 하나, elasticsearch의 구조적인 특징때문에 한개의 인덱스만 참조가능하다보니 한개의 인덱스로 게시글 목록을 가져오는 작업이 어려웠습니다.

제목+내용, 제목, 내용 검색같은 경우 어려운 문제는 아니지만

'댓글, 확장변수로 검색할 경우 게시글 정렬순서는 어떻게 가져오는가??' 가 어렵긴 하더라구요.

 

 

구글검색을 열심히 하니 댓글은 nested라고 해서 게시글 데이터에 포함하는 방법을 사용하신 분이 있는데 실제로 적용해보니 성능상의 문제로 패스..(제가 잘못 시도해서 성능이 안좋았을수도 있지만요 ㅎㅎ)

그래서 comment index에다가 게시글의 "title", "voted_count", "blamed_count", "readed_count", "comment_count", "regdate" 등과 같은 데이터들을 같이 저장하면 어떨까? 라는 생각이 들었습니다.

 

문제는 게시글의 정보가 바뀔때마다 comment의 데이터도 함께 변경해줘야 한다는 문제점이 있었습니다.

게시글에 댓글이 1000개가 있는 상태에서 게시글의 제목이 바뀌면 게시글(1개) + 기존 댓글에 비정규화된 데이터(1000개)를 수정해야 한다는 문제가 발생합니다.(nested방식을 사용했을때도 내부적으로는 이렇게 돌아가지만요)

 

기본적으로 elasticsearch와 같은 nosql은 update, delete 작업이 느려서 자주 업데이트 시키고 싶지는 않았습니다.

특히 조회수가 업데이트되는 일은 매우 빈번하게 발생되고, 이런일이 발생할때마다 elasticsearch에 있는 데이터들을 업데이트 하기엔 매우 비효율적인것 같더라구요.

굳이 업데이트를 해야한다면 별도의 집계서버를 놔두고 이벤트 소싱(https://mjspring.medium.com/%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EC%86%8C%EC%8B%B1-event-sourcing-%EA%B0%9C%EB%85%90-50029f50f78c)을 사용하여 주기적으로 elasticsearch의 값들을 갱신해줄수는 있지만 '굳이..??' 라는 생각에 패스.

 

위의 문제를 종합해보면

- 비정규화를 사용해야한다 (댓글 데이터에 게시글 데이터도 함께 저장)

- 게시글의 데이터가 업데이트 될때마다 관련있는 비정규화된 데이터들도 함께 업데이트를 해야한다

- 하지만 elasticsearch의 비정규화된 데이터를 매번 업데이트하는것은 매우 비효율적이다

- 그렇다고 elasticsearch서버의 직접적인 잦은 업데이트를 방지하기 위해 별도의 집계서버를 쓰기는 싫다

 

 

어떻게 이 난제를 해결할 것인가.. 참 고민이 많이 되더라구요.

결국엔 댓글 데이터엔 게시글의 "list_order", "user_id", "regdate", "member_srl"만 함께 저장하기로 했습니다.

제목 없음.png

 

실제로 "regdate", "list_order"순 정렬을 많이 사용하는걸 생각한다면

"readed_count", "voted_count", "blamed_count", "comment_count", "last_update", "update_order"를 사용할 일이 없을것이니까요.

무엇보다도 상기 언급된 컬럼들이 업데이트가 제일 빈번하게 일어나기도 하여서 과감하게 뺐습니다.

댓글 뿐만 아니라 확장변수의 index도 동일하구요.

 

이렇게 수십번의 인덱스 생성, 삭제와 매핑작업을 진행하다보니 문득 드는 생각이

elasticsearch의 빠른 속도를 유지함과 동시에 xe의 모든 기능을 사용한다는 것은 어려운 것 같습니다. ㅠㅠ

이론적으로 불가능한건 아니지만 노력에 비해 별로 쓰지도 않는 기능을 구현하기는..ㅎㅎ(물론 저만 쓰지 않는 기능일 수도 있습니다..)

 

하여간 이렇게 비정규화 작업이라는 큰 산을 넘으니 이번엔 타임라인 모듈 연동이라는 또 다른 산이 보이는군요 ㅋㅋㅋ

물론 이후엔 최적화라는 더 큰 산이 있겠지만요..

 

이렇게 작업을 하고 테스트로 게시글 검색을 수행하니 확실히 mysql의 like검색에 비해 훨씬 빠르네요. ㅎㅎ

노력한 보람이 나오는것 같습니다. (감격)

 

 

나중에 개발이 끝난다면 후기한번 작성하겠습니다!

  • profile
    기진곰님이 이미 기업용 버전으로 만드신걸로 아는데 ㅎㅎ 판매하실 계획이신가요?
  • profile ?
    그냥 혼자 쓸려고 만든거라 판매는 안할려구요 ㅎㅎ
  • profile

    윗분이 공짜홍보(?)를 해주셨듯이 저도 만들어서 몇 군데 판매해 보았는데... 일반적으로 활용도가 낮은 몇몇 검색/정렬조건은 포기해야 합니다. ㅠㅠ

    한때 한글 형태소분석기를 확장기능으로 만들어 배포하시던 분들도 있었는데, 엘라스틱의 무척 빠른 업데이트 주기와 허구헌날 바뀌는 API를 따라가기 힘든 것 같더군요. 게다가 은어와 비속어가 난무하는 인터넷 커뮤니티에서는 형태소 분석을 거친 검색결과보다는 문자 그대로의 검색결과를 오히려 더 선호하는 경우가 많습니다. 기껏 형태소 분석기 적용해 드렸더니 빼달라고 하시더군요. ㅡ.ㅡ 그래서 요즘은 불필요한 태그만 제거하고 ngram을 쓰고 있습니다.

    게다가 엘라스틱은 램을 많이 잡아먹기 때문에 운영에 상당히 부담이 됩니다. 게시물 DB는 100기가도 안 되는데 엘라스틱에 할당된 램이 200기가가 넘는 사이트도 있습니다. 램을 적게 주면 곧바로 버벅거립니다.

    MySQL의 full-text search 기능도 나날이 발전하고 있으니, 엘라스틱 이런 거 따로 붙여서 쓰지 않고 그냥 DB 자체의 검색 기능으로 충분한 날이 어서 오면 좋겠습니다. 사실 MySQL이 많이 뒤처진 것이고, PostgreSQL은 꽤 오래 전부터 쓸만한 ngram 검색을 지원했지요.

  • profile ?

    형태소 설정을 잘못하니 한글 검색은 잘 되나 "mp3', "7z", "쿠키런" 과 같은 합성어가 제대로 검색이 안되는 문제가 생기더군요.
    저같은 경우엔 nori_tokenizer에다 shingle 필터 조합으로 사용하니 문제가 어느 정도 해결되었습니다.

    확실히 램을 무지 먹는 것 같습니다. 까딱 구조를 잘못 짠 상태에서(nested방식으로 게시글 인덱스에 댓글 데이터도 집어넣었을때) 게시글 10만개 정도를 import하니 금세 램을 16기가씩 먹어버리니 원..
    물론 이후에 다른 구조를 사용하니 램 소모량이 줄어들긴 했는데, 줄어들더라도 mysql에비해 램 소모량이 훨씬 많긴해서 확실히 운영에 부담이 갈만한 수치더라구요.