<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Ju Factory</title>
    <link>https://yujuwon.tistory.com/</link>
    <description>Ju Factory</description>
    <language>ko</language>
    <pubDate>Mon, 17 Aug 2026 01:43:15 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>유주원</managingEditor>
    <image>
      <title>Ju Factory</title>
      <url>https://t1.daumcdn.net/cfile/tistory/237E1F3450DBD48A0A</url>
      <link>https://yujuwon.tistory.com</link>
    </image>
    <item>
      <title>[면접 대응] Flink 관련 질문 및 답변</title>
      <link>https://yujuwon.tistory.com/entry/%EB%A9%B4%EC%A0%91-%EB%8C%80%EC%9D%91-Flink</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;428&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dRlX8a/dJMcahKcrJW/z1l5Y44iKKsPdjhYoKKJZ1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dRlX8a/dJMcahKcrJW/z1l5Y44iKKsPdjhYoKKJZ1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dRlX8a/dJMcahKcrJW/z1l5Y44iKKsPdjhYoKKJZ1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdRlX8a%2FdJMcahKcrJW%2Fz1l5Y44iKKsPdjhYoKKJZ1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;844&quot; height=&quot;428&quot; data-origin-width=&quot;844&quot; data-origin-height=&quot;428&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Flink란?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 유한 및 무한 데이터에 대한 상태 연산을 할 수 있는 Stream Framework&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- JobManager와 TaskManager로 이루어져 있음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;JobManager 역할&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- job이 들어오면 각 TaskManager에게 필요한 slot을 할당 받는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 사용자가 작성한 코드를 Execution Graph 형태(source-map-sink) 로 변환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 해당 graph를 바탕으로 각 taskManager에게 작업을 할당한다. (source는 1번 TM, map은 2번 TM 등등)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- checkpoint coordinator : checkpoint를 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;TaskManager 역할&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실제 연산을 담당하는 JVM process&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- slot이라고 하는 thread 개념의 단위를 가지고 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- TaskManager에 할당된 메모리는 task slot의 개수만큼 나눠서 할당됨 (isolation)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- TaskManager 내의 cpu는 slot 단위로 격리하지 않고 공유해서 사용됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Slot sharing&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 서로 다른 단계의 task들이 하나의 slot을 공유해서 사용할 수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 예를 들어 각각이 parallelism이 2인 source -&amp;gt; map -&amp;gt; reduce 라는 pipeline이 있다고 가정하자. slot sharing이 없다면 총 6개의 slot이 필요할 것이다. 하지만 slot sharing 덕분에 하나의 slot은 source-&amp;gt;map-&amp;gt;reduce 하나의 task를 담을 수가 있고, 총 2개의 slot만 있으면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Operator chaining&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- slot 할당 전에 flink는 여러 task를 하나의 task로 묶을 수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 예를 들어 source-&amp;gt;map pipeline이 있고 이 과정에서 따로 shuffle이 필요하지 않다면 flink를 source-&amp;gt;map을 하나의 slot에서 처리할 수 있도록 task를 묶는다. 이 과정을 operator chaining이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Operator chaining Vs Slot sharing&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- slot이라고 해서 무조건 thread가 1개는 아니다. slot 내에는 많은 thread가 들어 갈 수가 있다. slot sharing은 thread를 돌리기 위한 slot을 공유하는 것이며, 그 안에는 각각의 task가 thread로 동작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- operator chaining은 여러개로 나뉘어져 있는 thread를 1개의 thread로 동작시키는 걸 말한다. 그렇기 때문에 context switching 비용을 줄일 수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 예를 들어, source(10)-&amp;gt;map(5)-&amp;gt;reduce(3) 인 pipeline이 있다고 가정하자면, 해당 pipeline은 parrallelism이 동일하지 않기 때문에 slot sharing을 할 수가 없다. slot1에서는 source(1)-&amp;gt;map(1)-&amp;gt;reduce(1) 이 각각의 task로 동작을 하게 되고, slot2에서는 source(2)-&amp;gt;map(2)-&amp;gt;reduce(2)가 각각의 thread로 동작하게 된다. 이 때의 slot은 총 10개, thread는 18개가 필요하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 여기서 만약에 source(5)-&amp;gt;map(5)-&amp;gt;reduce(5)로 pipeline이 변경된다면, operatir chaining이 적용이 되고, slot은 총 5개, thread는 5개가 필요하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;하나의 slot에 thread가 2개 동작하는 것 vs 두 개의 slot에 thread가 각자 동작하는 것&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 1개의 slot에서 2개의 thread&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 같은 JVM 내부에 있기 때문에 thread간 통신에 serialize가 필요 없다. 메모리 주소 번지만 던져주면 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - slot에 할당된 메모리를 각각의 thread가 나눠 써야 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - slot이 죽으면 두 개의 thread 모두 사용이 불가능 하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 2개의 slot에서 각자 thread&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 동일한 TaskManager에 있더라도 Flink 네트워크 스택을 거치게 된다. 데이터를 직렬화하고 로컬 네트워크 스택으로 데이터를 전송한다. 만약 다른 TM이라면 TCP/IP 네트워크 스택을 타게 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - slot에 할당된 메모리는 하나의 thread가 점유할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - slot 1개가 죽어도 다른 쪽 slot의 thread는 무사하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;데이터 역전 현상&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- flink는 slot 내에 많은 thread를 병렬로 둠으로써 나중에 들어온 데이터가 먼저 처리되는 데이터 역전 현상이 발생할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 가령 A 데이터가 들어오고 Map[1]에서 처리되고 있다고 가정하자. B 데이터가 들어 왔고 B 데이터는 Map[2]에서 처리가 되었으나 context-switching 등의 이유로 먼저 처리가 되었다고 한다면 B가 나중에 들어왔지만 먼저 처리되는 역전 현상이 발생하게 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 또한 thread에서 thread로 데이터를 직렬화하여 넘기는 과정 도중 네트워크 지연 등의 이유로 나중 데이터가 먼저 도착 할 수도 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 이를 막기 위해 KeyBy를 통한 partitioning을 고려할 수 있으며, Window와 watermark를 활용하여 해결할 수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;KeyBy Partitioning&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 같은 키를 가진 데이터는 같은 subtask로 보내도록 하는 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- JobManager에서는 처음 flink app을 올리게 되면, 해당 flink app의 parrallelism 개수를 확인 후 Keygroup을 설정하고 subtask 마다의 keygroup을 배분한다. ex) parralelism이 2인 경우, keygroup이 10이라면 slot1은 0~5, slot2는 6~10 이런식으로 할당함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 해당 할당표를 각 TaskManager에게 전달하고 각 thread는 해당 할당표를 메모리에 올리게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 실제 데이터가 유입이 되면 해당 데이터의 key를 hash 함수를 넣고 돌리고 해당 결과값을 가지고 할당표를 참조해서 해당 task의 위치를 찾아가게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 만약 parrallelism이 변경되면?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - JobManager가 다시 Keygroup을 할당하게 되고, subtask마다의 keygroup을 다시 배분한다. ex) slot1은 0~3, slot2는 4~7, slot3은 8~10&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 이 때 기존에 있던 state도 새로운 task로 함께 이동한다. (실제 object storage에 있는 데이터가 이동한다는 뜻은 아니고 기존에 task1이 읽던 걸, task3이 읽는 등 재배치가 이루어 진다는 뜻)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;state&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 과거로부터 지금까지의 데이터의 상태를 저장하는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 지금까지 들어온 주문 금액의 총 합.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 최근 10분간 발생한 에러의 개수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- state는 rocksDB에 저장을 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 매번 file에 저장하지는 않고, 우선 메모리에 저장을 하고 메모리가 차게 되면 sst(sorted string table) file 형태로 저장을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - flush 전에 서버가 꺼지면?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - 이를 막기 위해 메모리에 쓰기전에 WAL(write ahead log) 에 한 번 쓴다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - checkpoint 주기를 통해 rocksDB에 주기적으로 write한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Window&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- tumbling window&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; -&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfDdh3/dJMcahpTPPZ/iKknRVFKycY7oxkcHscPuk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfDdh3/dJMcahpTPPZ/iKknRVFKycY7oxkcHscPuk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfDdh3/dJMcahpTPPZ/iKknRVFKycY7oxkcHscPuk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfDdh3%2FdJMcahpTPPZ%2FiKknRVFKycY7oxkcHscPuk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;400&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 시간 단위로 데이터를 구분 짓는 윈도우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - processing time 모드일 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - watermark가 없다면 해당 시간이 지나게 되면 윈도우가 닫히게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - event time 모드일 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - watermark가 없다면 윈도우는 영원히 닫히지 않게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- sliding window&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; -&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/qcwSh/dJMcai99RzH/K4BYEyXlF1gBZB6O375yaK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/qcwSh/dJMcai99RzH/K4BYEyXlF1gBZB6O375yaK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/qcwSh/dJMcai99RzH/K4BYEyXlF1gBZB6O375yaK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FqcwSh%2FdJMcai99RzH%2FK4BYEyXlF1gBZB6O375yaK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;400&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 시간축으로 몇 분 간격의 누적 통계 등을 낼 때에 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - data가 겹칠 수 있기 때문에 여러 윈도우에 동일한 데이터가 중복으로 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - 예를 들어 10분 size의 1분 간격의 sliding window라고 한다면 10분 전에 들어간 데이터는 1분 간격의 10개의 window에 대해 모두 저장되게 된다. 이 때문에 하나의 subtask의 메모리 사용량이 많아질 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- session window&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; -&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/PtaJC/dJMcaaxzJiC/1NQrd2ITL4Ruk98kQDg0ZK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/PtaJC/dJMcaaxzJiC/1NQrd2ITL4Ruk98kQDg0ZK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/PtaJC/dJMcaaxzJiC/1NQrd2ITL4Ruk98kQDg0ZK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FPtaJC%2FdJMcaaxzJiC%2F1NQrd2ITL4Ruk98kQDg0ZK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;400&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 말 그대로 session 단위로 관리 되는 window이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - Session gap 시간 설정이 필요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - user의 마지막 데이터 + session gap 시간이 지나게 되면 window가 닫히게 됨.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - user 마지막 시간 + session gap 시간이 watermark 시간 보다 작다면 해당 window는 닫힌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- global window&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; -&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;400&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2V3bE/dJMcadAZhsC/UdDM8f3maIoZTYeD2BFpa0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2V3bE/dJMcadAZhsC/UdDM8f3maIoZTYeD2BFpa0/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2V3bE/dJMcadAZhsC/UdDM8f3maIoZTYeD2BFpa0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2V3bE%2FdJMcadAZhsC%2FUdDM8f3maIoZTYeD2BFpa0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;650&quot; height=&quot;400&quot; data-origin-width=&quot;650&quot; data-origin-height=&quot;400&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 모든 데이터를 하나의 거대한 윈도우에 집어 넣는 방식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 워터마크가 있어도 윈도우가 닫히지 않는다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 닫히지 않으면 어떻게 데이터를 처리할까??&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - trigger 로직을 구현해서 특정 조건이 되었을 때의 데이터 연산을 할 수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - global window란 용어 때문에 헷갈릴 수 있는데, 전체란 뜻이 아니다. global window 역시 하나의 subtask에 종속된다. global의 의미는 시간에 종속되지 않는 window를 뜻한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 사용자가 명시적으로 purge를 명령하기 전까지는 해당 window의 데이터는 삭제되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;watermark&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 네트워크 지연 등으로 데이터의 순서가 바뀌었을 때, 이런 데이터를 수용하기 위한 용도로 만들어진 옵션&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 가장 최근에 window로 유입된 시간 - 지연 시간이 watermark가 되면 해당 watermark가 계산이 되면 이제부터 이 watermark 시간 이하로는 데이터가 들어와도 처리안할꺼야라는 뜻이 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 분산 입력으로부터의 watermark 계산은 분산 입력된 가장 최신 데이터 중 가장 느린 걸 기준으로 설정한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- window size를 늘리거나, session gap을 더 늘리면 되지 않을까??&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 결과적으로는 동일함&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - session gap은 손님이 들어와서 session gap 시간 동안 자리를 비우면 테이블을 치우는 것을 의미. session gap이 길어지면 짧은 session이 여러번 반복되는 경우에서 긴 하나의 session으로 인식될 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - watermark는 손님이 들어와서 session gap 시간 동안 자리를 비웠으나 잠시 화장실을 갔었을 수도 있으니 watermark 시간 동안을 더 기다리자는 의미&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;task Manager의 메모리 구조&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- JVM (Heap) : 자바 객체가 저장되는 공간&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Framework Memory (Off-Heap) : Flink Framework 자체적으로 사용하는 메모리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Network Memory (Off-Heap) : 네트워크 버퍼, task의 thread간 데이터를 주고 받아야 할 경우 해당 네트워크 버퍼를 이용해서 이동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Managed Memory (Off-Heap) : rocksDB나 배치 연산시 메모리 캐싱.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;checkpoint barrier&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Checkpoint coordinator(JobManager)에서 source subtask 들에게 현재 몇번째 checkpoint barrier를 넣으라고 지시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- source에서는 data 사이에 checkpoint barrier를 집어 넣는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;- data1 &amp;gt; data2 &amp;gt; barrier &amp;gt; data3 &amp;gt; data4&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp;- data1과 data2 까지만 checkpoint 상태를 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 만약 입력을 다중으로 받는 subtask일 경우 한쪽에서 barrier를 받았다면, 나머지 쪽에서도 barrier를 처리하기 전까지 이미 barrier가 처리된 입력 쪽의 데이터는 버퍼에 쌓아두고 처리를 잠시 중지한다. 여기서의 버퍼는 네트워크 버퍼에 쌓인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 버퍼가 다 찾음에도 다른 쪽 입력의 barrier 처리가 안된다면 backpressure가 발생하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- barrier 처리가 완료되면, 로컬 디스크에 checkpoint file을 생성한 후 barrier를 다음 step으로 넘긴다. 그 후 외부 저장소에 async로 복사를 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이렇게 모든 subtask에서 barrier 처리가 완료되면 checkpoint coordinator는 mata data에 해당 checkpoint를 기록한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Server/centos</category>
      <category>flink</category>
      <author>유주원</author>
      <guid isPermaLink="true">https://yujuwon.tistory.com/466</guid>
      <comments>https://yujuwon.tistory.com/entry/%EB%A9%B4%EC%A0%91-%EB%8C%80%EC%9D%91-Flink#entry466comment</comments>
      <pubDate>Fri, 20 Feb 2026 14:29:19 +0900</pubDate>
    </item>
    <item>
      <title>[면접 대응] SBERT와 모바일넷 질문 답변</title>
      <link>https://yujuwon.tistory.com/entry/%EB%A9%B4%EC%A0%91-%EB%8C%80%EC%9D%91-SBERT%EC%99%80-%EB%AA%A8%EB%B0%94%EC%9D%BC%EB%84%B7-%EC%A7%88%EB%AC%B8-%EB%8B%B5%EB%B3%80</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;402&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lmwVI/dJMcafZLhwU/mQq60Cmm93lehSIXeIUf41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lmwVI/dJMcafZLhwU/mQq60Cmm93lehSIXeIUf41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lmwVI/dJMcafZLhwU/mQq60Cmm93lehSIXeIUf41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlmwVI%2FdJMcafZLhwU%2FmQq60Cmm93lehSIXeIUf41%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;470&quot; height=&quot;402&quot; data-origin-width=&quot;470&quot; data-origin-height=&quot;402&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;bert란?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기존 RNN에서 문제가 되던 vanishing gradient 문제를 해결하기 위해 고안된 모델.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 특징&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- bidirectional로 단방향 학습이 아닌 양방향 학습을 진행&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- transformer encoder : self-attention 기법을 도입함으로써 gradient가 소실되는 문제를 막았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 또한 문장의 일부 단어를 가리고, 주변 단어를 통해 가려진 단어를 맞추는 연습을 진행하고, 두 문장을 나란히 놓고 두 번째 문장이 첫 번째 문장에 올 문장인지를 맞추도록 학습을 진행함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;attention이란?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- RNN의 경우 문장이 길면 길수록 앞부분에 나오는 정보의 중요도가 감소하는 문제가 발생함. (vanishing gradient)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이를 해결하기 위해 매순간 각 토큰의 모든 중요도를 확인하자라는 의도에서 출발함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 예를 들어 encoder 입력이 '나는 학교에 간다' 이고 decoder 출력이 'i go to school'이라고 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Query: 질문, school이랑 제일 관련된 단어는 모야?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Key : 대상, 나는, 학교에, 간다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Value : 값, 나는, 학교에, 간다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 결과적으로 school에 대해 나는, 학교에, 간다를 모두 각각 내적곱을 취한 후에 결과 값으로 나온 값들에 대해 softmax를 취해서 확률 분포가 1이 되도록 만들어 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이렇게 만들어진 가중치를 value 값에 적용해서 attention 가중치를 추가해 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;self-attention이란?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- attention이 서로 다른 두 문장 사이의 관계를 표현했다면, self-attention은 하나의 문장 내의 단어와의 관계를 표현했다고 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 예를 들어 나는 학교에 간다라는 문장이 들어왔다면, Query, Key, Value는 각각 아래와 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Query : 질문, 나는이랑 가장 관련된 단어는 모야?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Key : 대상, 나는, 학교에, 간다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Value : 값, 나는, 학교에, 간다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기존 attention과의 차이라고 한다면, 자기 자신에 대한 attention을 취한다는 것 때문에 self-attention이라고 불리고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;multi-head attention이란?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 기존 attention이 전문가 1명의 입장만을 반영했다고 한다면, multi-head attention은 여러명의 전문가 입장을 반영했다고 볼 수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;sbert란?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Sentence bert. 문장이나 문단 단위의 bert의 개선판&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- bert와의 차이점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- bert는 단어 사이의 유사도를 판별하는 모델로써 학습을 진행한 반면에, sbert는 문장 전체를 하나의 벡터로 학습하는 훈련을 받음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- sbert에서는 각각의 문장을 각각의 bert 모델에 넣고 그 결과로 나온 값을 polling 해서 두 문장이 비슷한 경우는 cosine similarity를 높게 비슷하지 않은 경우에는 consine similarity를 낮게 하도록 학습을 진행함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;mobilenet&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;431&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cNd8eQ/dJMcahJ5cPK/1MWIjrVjik8p2pRF4lVEkk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cNd8eQ/dJMcahJ5cPK/1MWIjrVjik8p2pRF4lVEkk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cNd8eQ/dJMcahJ5cPK/1MWIjrVjik8p2pRF4lVEkk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcNd8eQ%2FdJMcahJ5cPK%2F1MWIjrVjik8p2pRF4lVEkk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;781&quot; height=&quot;431&quot; data-origin-width=&quot;781&quot; data-origin-height=&quot;431&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- mobilenet의 가장 큰 특징은 기존 convolution 연산을 줄였다는 것에 있다. 가령 3 channel의 input이 있고, 이를 3x3 mask로 convolution을 한다고 가정하면, 1번의 masking 연산량은 (9+9+9) x output channel(M) = 27M 가 소모 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- mobilenet에서는 이러한 연산을 줄이기 위해 channel 별로 각각 filter masking을 따로 둔다. 9 + 9 + 9 = 27의 연산이 소모되게 된다. 이를 depth-wise convolution이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- depth-wise convolution 만으로는 channel 간의 연관관계를 계산할 수 없기 때문에 여기에 point-wise convolution을 추가하여 채널 3개에 대해 1개의 결과를 내도록 1x1x3 masking을 씌운다. 이를 통해 각 채널별 convolution 과 이를 합한 convolution 이렇게 두개가 동작하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>machine learning</category>
      <category>MobileNet</category>
      <category>SBERT</category>
      <author>유주원</author>
      <guid isPermaLink="true">https://yujuwon.tistory.com/465</guid>
      <comments>https://yujuwon.tistory.com/entry/%EB%A9%B4%EC%A0%91-%EB%8C%80%EC%9D%91-SBERT%EC%99%80-%EB%AA%A8%EB%B0%94%EC%9D%BC%EB%84%B7-%EC%A7%88%EB%AC%B8-%EB%8B%B5%EB%B3%80#entry465comment</comments>
      <pubDate>Tue, 3 Feb 2026 17:55:37 +0900</pubDate>
    </item>
    <item>
      <title>[면접 대응] kafka 관련 질문 및 답변</title>
      <link>https://yujuwon.tistory.com/entry/%EB%A9%B4%EC%A0%91-%EB%8C%80%EC%9D%91-kafka-%EA%B4%80%EB%A0%A8-%EC%A7%88%EB%AC%B8-%EB%B0%8F-%EB%8B%B5%EB%B3%80</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;754&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oIsDg/dJMcadU8X73/2lEX84N22FjEp3BYjeKyX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oIsDg/dJMcadU8X73/2lEX84N22FjEp3BYjeKyX1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oIsDg/dJMcadU8X73/2lEX84N22FjEp3BYjeKyX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoIsDg%2FdJMcadU8X73%2F2lEX84N22FjEp3BYjeKyX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1456&quot; height=&quot;754&quot; data-origin-width=&quot;1456&quot; data-origin-height=&quot;754&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;kafka란??&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 분산 이벤트 스트리밍 플랫폼&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- producer : 데이터를 만들어 kafka에 보내는 생성자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- consumer : broker에서 데이터를 가져와서 소비하는 소비자&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- broker : kafka 서버&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;broker controller 선출&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- kafka 3.3 이하 버전에서는 기존 hbase나 hadoop 과 비슷한 방식으로 leader 선출이 이루어짐&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- zk znode에 에페머럴 임시 노드를 먼저 생성한 broker가 controller가 된다.&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 만약 해당 controller node가 내려가면 znode의 임시 노드가 삭제가 되고 이를 지켜보던 다른 broker 중에서 해당 노드를 생성한 node가 contoller가 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- kafka 3.3 이상 버전에서는 zookeeper를 쓰지 않고, KRaft 기반 방법을 사용함.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- Controller는 Raft 알고리즘을 통해 선출됨.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 예를 들어 5개의 broker를 순차적으로 실행시킨다고 가정하자. 1번 서버는 자기 자신에게 투표하고 candidate가 된다. 하지만 과반의 합의를 얻지 못했기 때문에 클러스터는 대기 상태가 된다. 2번 서버가 투입이 되고 2번 서버가 1번 서버에 투표를 완료해도, 아직 과반이 되지 못했기 때문에 리더는 선출이 안된다. 3번 서버가 투입이 되고 투표가 완료되면, 1번 서버에 대한 controller 선출이 결정되고 클러스터가 동작 상태가 된다. 2,3번 서버는 자동적으로 follower가 된다. 4,5 번 서버가 투입이 되지만 이미 controller가 선출 되었기 때문에 follower로 유지가 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp; 리더는 2~5번 서버에게 그 동안 발생했던 meta data를 전송한다. (전송한다기 보다는 2~5번 서버가 리더로부터 polling한 후에 meta data를 가져옴)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;투표시 반대 조건은 따로 무엇인지?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 투표를 요청하게 되면 후보자는 자신이 가지고 있는 log의 마지막 index와 term을 보내게 된다. 받는 쪽에서는 자신이 가진 log 번호보다 낮거나 term도 내 term보다 낮다면 반대표를 던진다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 이와 같이 하는 이유는 데이터의 정합성을 지키기 위함이다. 최신 데이터를 가지고 있는 broker가 리더가 됨으로써 항상 최신성의 데이터를 유지할 수 있도록 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;메타 데이터 동기화 방식&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 팔로워들이 리더에게 pull로 최신 log를 달라고 요청을 한다. 리더는 팔로워의 요청 index를 확인한 후 최종적으로 모든 follower에서 해당 index를 가져갔다면 commit index(high watermark)를 해당 index로 설정한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 이 때 팔로워 외의 다른 broker들도 리더에게 최신 meta data log를 달라고 요청한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 하지만 이때 리더에게 모든 트래픽이 몰리면 리더의 부하가 심해지기 때문에 broker는 자신에게 가장 가까운 팔로우 controller에게 데이터를 요청하고 받는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 팔로워는 fetch 한 로그를 디스크에 쓴 후 이 후 offset의 로그를 다시 리더에게 요청한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 리더는 해당 offset log와 함께 이전 offset의 log가 확정되었다는 메시지(과반수의 follower가 log를 수신했을 때) 도 함께 전달한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 팔로워는 방금 전달 받은 offset log를 디스크에 쓰고, commit이 확정된 offset의 log는 메모리에 올린다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 만약에 해당 리더가 죽어서 전달 받은 log가 commit 합의가 되지 않았다면??&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 팔로워는 새로운 리더에게 해당 offset log를 달라고 요청한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 하지만 새로운 리더는 자신이 가진 term과 요청한 offset log의 term이 다르기 때문에 해당 log를 truncate 해달라고 요청함.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 그럼 리더는 어떻게 동일한 term을 찾을까??&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 리더는 자신의 epoch cache에 저장된 로그를 기반으로 이진탐색 등의 방법으로 해당하는 term의 최종 지점까지 찾는다. 만약 해당하는 term이 follower와 다르다면 follower에게 해당 로그부터 삭제해 달라고 요청한다. 이러한 이유 때문에 follower에서 디스크에 먼저 쓴 후 commit 메시지가 오면 이후에 메모리에 반영하는 것이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;min.insync.replicas&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- high watermark가 해당 데이터가 제대로 복제되었음을 확인하는 징표라면, min.insync.replicas은 몇 명에게 복사해야 데이터를 진실로 인정할 것인가에 대한 기준표임&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- min.insync.replicas가 2로 설정이 되었다면, 최소 2개의 broker에 해당 데이터가 복제가 되야 프로듀서에게 데이터가 성공적으로 복제 되었다고 알린다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- acks=all이라는 옵션이 있는데 이 옵션은 ISR에 포함된 모든 그룹이 데이터를 복제해야 성공으로 인식하는 옵션임.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 하지만 follower 2대 중 2대가 모두 내려가서 ISR 그룹에 리더 broker만 남았을 경우 acks=all은 성공이라고 인식하게 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 그래서 min.insync.replicas 옵션을 함께 사용하는 게 중요하다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- ISR 그룹은 컨트롤러가 지정하며, 일정 시간 동안(replica.lag.time.max.ms) 복제한 이력이 없다면 해당 broker는 ISR에서 제외시킨다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- ISR에 제외됐다고 하더라도 부지런히 fetch 해서 controller의 offset을 따라 왔다면 해당 broker는 다시 ISR 그룹으로 지정이 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;topic 생성과 데이터 입력 시 동작 현상&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 컨트롤러가 topic 생성 요청을 받음.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 컨트롤러는 파티션을 어떤 broker에 할당할지를 결정함.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 컨트롤러는 자신의 meta data에 해당 기록을 남김&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- follower들이 해당 meta data를 가져가고 과반수 이상이 복제해가면 해당 메타데이터 HW를 한단계 올리고 토픽 생성을 확정한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 토픽 생성이 확정되면 해당 결과가 broker들에게도 전달(pulling)되며 각 broker에서는 해당 topic의 leader /follower라는 것을 인식한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 데이터 입력 시 producer는 아무 broker에게 meta data를 요청하고, 해당 결과를 바탕으로 리더 broker에게 데이터를 전송함.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- leader broker는 자신의 로컬에 해당 데이터를 씀&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- ISR 그룹에 속한 follower 들이 leader에게 fetch 요청을 보내서 데이터를 가져감.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 과반 이상이 데이터를 복제해가면 leader는 commit을 날리고 producer에게 success를 보낸 후, HW를 한단계 올림. 이 때부터는 해당 데이터를 consumer가 사용 가능함.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;topic reassignment&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- topic을 재배치할 때 사용한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- topic reassignment를 실행하면, 컨트롤러는 임시 broker와 새로운 broker를 합친 ISR 그룹을 생성한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 새로운 broker가 기존 leader에게 데이터를 fetch한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- leader broker는 계속해서 producer나 consumer의 업무를 처리한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 새로운 broker가 모든 데이터 복제가 완료되면, 컨트롤러는 새로운 broker에게 leader를 위임시키거나 ISR 그룹에서 기존 broker들을 삭제하고 meta log를 update한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- follower 컨트롤러가 해당 meta log를 읽고 합의를 이행하면 leader 컨트롤러는 commit을 하고, HW를 옮긴다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- assignment broker는 해당 명령이 commit 되었음을 확인하고 해당 작업을 진행한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;consumer rebalancing&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- consumer group에 새로운 맴버가 추가되거나 기존 맴버가 빠지게 되면 누구에게 어떤 partition을 맡길지 재정의를 해야 한다. 이를 위해 우선 consumer가 들고 있던 모든 partition을 내려 놓고 읽기 중지 상태가 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- consumer의 모든 group이 준비가 될 때까지 기다린 후에 다 준비가 되면 partition reassign을 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 이렇게 consumer가 partition을 내려 놓고 다시 재정의 되는 순간까지 consumer의 읽기 동작은 잠시 멈춤상태가 되고 이를 stop the world라고 한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;어떻게 stop the world를 막을 수 있을까?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- stop the world의 주기를 짧게 줄이는 방법은 consumer가 partition을 내려 놓고 다시 재정의 할때까지의 시간을 짧게 줄이는 방법이다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 코디네이터가 stop을 선언하면 각 consumer는 poll로 가져온 데이터를 모두 처리해야 consumer group에 join할 수가 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 그렇다면 poll size를 줄임으로써 consumer가 작업을 빨리 하도록 처리하면 그만큼 빨리 조인을 할 수가 있다. (max.poll.records)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- Cooperative sticky Assignor&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 새로운 consumer가 join을 하면, 기존의 consumer는 계속 자기가 들고 있던 partition을 유지한다. stop the world가 벌어지지 않는다. 코디네이터는 1번 consumer가 가진 partition을 새로 들어온 consumer에게 주자라고 계획을 세운 후, 이를 실행 한다. 그 동안 나머지 consumer들은 해당 partition을 계속 유지하고 있는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- Static Membership&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- k8s 등의 환경에서는 consumer가 재시작될 때마다 새로운 consumer로 인식되어 rebalancing되는 문제가 발생한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 이를 방지하기 위해 consumer에 id를 지정해 주게 된다. (group.instance.id)&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 해당 timeout 동안(session.timeout.ms) consumer가 중지가 됐더라도 코디네이터는 rebalancing을 하지 않는다. 해당 시간 이내에 동일한 id를 가진 consumer가 다시 접속이 되었다면, 코디네이터는 이를 같은 consumer로 인식하고 그대로 연결해 준다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;poll과 fetch의 관계&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- poll은 consumer 자체 큐에 있는 데이터를 가져오는 명령이고, fetch는 consumer의 background thread가 broker에게 요청해서 자신의 local queue에 데이터를 쌓는 것을 말한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 코디네이터에서는 consumer가 살아있는지를 poll을 통해 확인하는데 이 이유는 poll이 실제 기능을 처리하기 위한 작업이고, fetch는 단순 background로 돌아가는 기계적인 작업이기 때문에 poll을 더 중요시 본다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 코디네이터에서 consumer local queue에 있는 poll의 세부사항까지는 확인을 할 수가 없다. 단지 poll이 호출될 때마다 kafka client에서는 poll 시간을 코디네이터에게 전달하거나, heart-beat에 실어서 전송한다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;하나의 partition은 하나의 consumer만&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- kafka의 기본 원칙인 순서 보장을 위해 하나의 partition은 하나의 consumer만 보장하도록 설계되어져 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- 만약 partition1에 consumer1, consumer2가 매핑이 되었다고 가정하자. 1번 메시지가 결제, 2번 메시지가 결제 취소라고 했을 때, consumer2번에 의해 2번 메시지가 먼저 처리가 된다면, 크나큰 실수가 발생하게 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;kafka dr(disaster recovery)&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- MirrorMaker2&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 서로 다른 kafka cluster 간의 데이터를 복제하고 동기화 해주는 오픈 소스&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 기본 원리는 cluster A의 topic을 읽어서(consumer) cluster B에 write(producer) 해주는 프로세스가 동작되는 구조. active-active인 경우는 cluster A와 cluster B 각각에 해당하는 프로세스가 존재함.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;복제 무한 루프 이슈&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - active-active 환경에서의 핵심은 복제된 topic을 다시 복제하는 무한루프에 빠지지 않는 것인데 이를 위해 topic 앞에 prefix를 붙여서 복사된 topic의 경우 재복사되지 않도록 함.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - cluster A의 topic : my_topic&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - 복사된 cluster B의 topic : A.my_topic&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - MirrorMaker2에서는 이미 A라는 prefix가 붙어있으니 이를 A topic에 다시 복사할 필요가 없다라고 판단함.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - &lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;순서 보장 이슈&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - consumer에서는 양쪽 cluster의 데이터를 어떻게 읽을까? 순서보장은 어떻게??&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - A cluster에 들어간 데이터와 B cluster로 복제된 데이터 사이의 순서 보장을 보장할 수는 없다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - 그렇기 때문에 application에서 양쪽의 cluster를 모두 읽은 다음에 timestamp 등으로 순서를 맞추는 방법이 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - 그 외에 sticky partitioning 등처럼 특정 producer의 경우 특정 cluster에만 쓰도록 설정하고, consumer는 해당하는 특정 cluster에서만 읽도록 하는 방법 등이 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;A cluster를 읽다가 B cluster를 읽었을 때, data offset은 어떻게 맞출수 있는지?&lt;/span&gt;&lt;/b&gt;&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - A cluster에 있는 데이터를 B cluster에 복제할 경우 동일한 데이터라고 하더라도 offset이 달라질 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - 이를 위해 Mirror Maker2의 offset translation 기능이 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - Mirror Maker2는 consumer_offset topic에 A cluster의 특정 offset은 B cluster의 특정 offset과 같다라는 정보를 남김.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - consumer가 B cluster로 넘어가게 되면 consumer는 offset translation을 보고 B cluster의 특정 위치를 찾게 된다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - &lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;consumer가 양쪽 cluster의 동일한 topic의 동일 partition을 수집하게 하고 싶다면?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - 하나의 consumer가 양쪽의 cluster의 동일한 topic의 동일 partition을 수집하게 하는 것은 불가&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - 만약 하고 싶다면 consumer를 두 개 띄우고 각각 수집하게 한 후 공통 queue 등에서 aggregation을 해야 함.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; - consumer에서도 특정 partition을 지정하게 해서 로직을 구분해야 함.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- Stretch Cluster&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - Quorum이 5개라고 가정한다면, 2개는 A site, 2개는 B site, 1개는 witness site에 둔다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - rack awareness를 설정하여, broker를 A와 B site에 각각 배치한다. witness site의 경우 controller 전용 zone으로 사용하며 실무적으로는 broker를 두지는 않는다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - Latency 이슈가 있음. 서로 다른 zone에 데이터 복사가 이루어져야 해서 지연이 발생할 수 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;- Confluent Cluster Linking&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - Mirror Maker2와의 차이점&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - Mirror Maker2의 경우는 중간에 connector가 필요하지만 Confluence Cluster Linking의 경우는 destination broker가 source broker에게 직접 Fetch를 하는 방식&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - Active-Active 방식을 사용할 경우 topic 무한 루프에 빠질 수가 있다.&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - 이를 막기 위해 Cluster A의 경우 write만 하고, Cluster B는 read만 하도록 구현해 주는 것이 좋다.&lt;/p&gt;</description>
      <category>Server/centos</category>
      <category>DR</category>
      <category>fetch</category>
      <category>Kafka</category>
      <category>Poll</category>
      <category>stop the world</category>
      <author>유주원</author>
      <guid isPermaLink="true">https://yujuwon.tistory.com/464</guid>
      <comments>https://yujuwon.tistory.com/entry/%EB%A9%B4%EC%A0%91-%EB%8C%80%EC%9D%91-kafka-%EA%B4%80%EB%A0%A8-%EC%A7%88%EB%AC%B8-%EB%B0%8F-%EB%8B%B5%EB%B3%80#entry464comment</comments>
      <pubDate>Tue, 3 Feb 2026 17:54:47 +0900</pubDate>
    </item>
    <item>
      <title>[면접 대응] hbase 관련 질문 및 답변</title>
      <link>https://yujuwon.tistory.com/entry/%EB%A9%B4%EC%A0%91-%EB%8C%80%EC%9D%91-hbase-%EA%B4%80%EB%A0%A8-%EC%A7%88%EB%AC%B8-%EB%B0%8F-%EB%8B%B5%EB%B3%80</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;hbase 동작 방식&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;479&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ecW6dt/dJMcabiMDTA/Ty4G1Ld0DWsf2kicIdlD3k/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ecW6dt/dJMcabiMDTA/Ty4G1Ld0DWsf2kicIdlD3k/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ecW6dt/dJMcabiMDTA/Ty4G1Ld0DWsf2kicIdlD3k/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FecW6dt%2FdJMcabiMDTA%2FTy4G1Ld0DWsf2kicIdlD3k%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;638&quot; height=&quot;479&quot; data-origin-width=&quot;638&quot; data-origin-height=&quot;479&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- hdfs 위에서 동작하는 분산 NoSql 데이터베이스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Client는 zookeeper에게 현재 hbase:meta table을 들고 있는 서버 위치를 알려달라고 요청을 보냄.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- zookeeper에서는 현재 hbase:meta table을 저장하고 있는 서버 위치를 보내준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- hbase:meta table에는 table 명과 start row key, end row key등이 저장되어 있어서 해당 meta table을 보고 실제 데이터를 가지고 있는 region server를 찾을 수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;db가 아닌 zookeeper를 쓰는 이유??&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- db의 경우 디스크에 write를 하고 트랜잭션을 보장하기 때문에 속도가 느리다. 하지만 zookeeper의 경우 모든 데이터를 메모리에 올리고 있기 때문에 실시간성 데이터 업데이트에 훨씬 빠르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- zookeeper에는 세션이 끊어지면 노드가 삭제되는 임시 노드 개념이 존재한다. db의 경우는 서버가 죽은 경우 외부 프로세스가 해당 서버에 ping을 날려 죽었는지를 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- zookeeper의 경우 합의 알고리즘을 사용한다. 과반수 원리를 이용해 모든 의견을 선출하기 때문에 데이터 일관성 측면에서도 db보다 낫다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Hotspotting&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- hbase는 데이터를 row key 순서대로 저장을 한다. 하지만 이 때 특정 시간 대에 특정 범위의 row key가 몰리게 된다면, 모든 요청이 하나의 region server에 몰리게 되고 이러한 현상을 Hotspotting이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Hotspotting 해결을 위해서는 salting 기법 (무작위 단어를 붙임), hashing 기법 (hash 값으로 변경하여 저장), reversing 기법 등이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;hbase client version 2.x 로 hbase server 1.x를 호출하면 hbase meta table region server 부하가 급상승하게 되는 이유??&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- client에서 bulk load 요청을 하면 hmaster는 region server에 hbase meta table을 수정 혹은 업데이트 요청을 하게 된다. region server에서 부하가 발생했다면 이 meta table을 수정하는 과정에서 client의 버전 이슈 등으로 인한 요청 쪽의 프로토콜을 해석하지 못해서 요청이 쌓였고 결국에는 부하가 발생했다고 판단할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;put은 정상 동작 했는데??&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- put의 경우는 region server의 위치를 알면, 해당 위치에 대한 client cache가 적용되어 이 후 부터는 Master의 간섭 없이 해당 region server의 접근이 가능하다. 또한 put의 경우는 따로 hbase meta table을 업데이트 할 필요가 없는 작업이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 하지만 bulk load의 경우는 Hfile을 table meta 에 수정하거나 업데이트 해야 하는 작업이 필요하고, 이를 처리하는데 있어서 기존 규약이 아닌 새로운 프로토콜 유형의 요청을 해석한 후에 이를 적용해야 하기 때문에 Master 부하가 발생하게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;bulk load 동작 방식&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- source data를 hbase 데이터 포멧인 HFile로 변환한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - client는 zk를 통해 hbase meta table을 저장하고 있는 region server 위치를 알아낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - HFile을 생성할 때는 현재 hbase table의 region 정보를 가져와서 각 region에 들어갈 데이터들을 구분할 HFile을 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - HDFS의 임시 디렉토리에는 hbase region에 들어갈 알맞은 HFile 들이 만들어진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - HFile을 hbase가 관리하는 내부 폴더로 이동한다. (copy)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- client는 region server에게 이 파일들을 hbase 테이블에 넣어달라고 요청한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- region server에서는 HMaster에게 해당 client가 Hfile을 넣을 권한이 있는지를 확인한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- HMaster로부터의 승인이 떨어지면 region server는 각각의 region들이 저장될 경로를 hbase meta table에 기록한다. (meta update)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이 후 실제로 데이터를 serving할 region server로 가서 HFile이 갱신됐음을 알려준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- region server에서는 HDFS에 저장된 HFile을 열고, HFile 내부의 block indexing 정보를 메모리에 올린다. 이를 통해 갱신된 HFile 역시 서비스를 제공할 수 있게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- client에서 bulk load 작업이 다 끝나면, HMaster에게 작업이 종료되었음을 알린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- bulk load의 경우 동일한 key가 hfile로 또 생길 수 있으므로, major compaction을 해 주는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;major compaction, minor compaction&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 여러 이유 (bulk load, column family) 등으로 인해 작은 크기의 hfile이 여러 개 생성될 수 있으며, 이렇게 많은 수의 hfile이 생성이 되면 hbase 성능에 안 좋은 영향을 주게 된다. 이를 위해 compaction을 해주어야 한다. 우선 major compaction과 minor compaction의 차이를 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- minor compaction : region 내의 여러 hfile을 하나의 hfile로 merge 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- major compaction : minor compaction과 비슷하게 hfile을 merge하나 그 차이가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 우선 major compaction의 경우 삭제 마커가 있는 data의 경우 삭제를 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; - 그리고 minor compaction의 경우 동일 파일 내에서의 중복 row key는 하나로 합치지만, major compaction의 경우 모든 hfile에 대해 중복 row key를 하나로 합치는 작업을 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;put 동작 방식&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- client는 zk에게 hbase meta table을 가진 region server가 어디있는지 물어본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- zk에게 전달 받으면 client는 해당하는 region server에 가서 해당 데이터가 어디 있는지 다시 물어본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- region server는 해당하는 데이터를 서빙하는 region server를 알려주고, client는 해당 하는 region server로 가서 데이터를 put 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 사용자가 put을 하게 되면, 일단 데이터 region server에서는 WAL(Write Ahead Log) 로그 파일에 기입을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- WAL에 기입이 완료되면 memstore에 해당 데이터를 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- memstore가 임계치를 넘어가게 되면 메모리에 있던 데이터들을 HFile에 flush 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Hfile이 생성되면 생성된 Hfile에 대한 block indexing 정보를 만든 후 해당 정보는 다시 메모리에 올린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;get 동작 방식&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- client가 zk에게 hbase meta table을 가진 region server가 어디 있는지 물어본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- zk에게 해당 region server 위치를 전달 받으면 해당 region server를 통해 meta table을 찾고, 내가 찾고자 하는 row key가 포함된 region server를 알아낸다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 해당 region server 로 간 후 데이터를 요청한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- region server에서는 요청된 데이터에 대해 각각 memstore, block cache, hfile을 각각 조회한다. 만약 block cache에 데이터가 존재한다면, hfile을 굳이 찾을 필요는 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이렇게 세 군데서 조회한 경우 여러 row key가 나올 수 있는데, timestamp 기준으로 가장 최신의 data만 리턴한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;bloom filter&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- hfile을 보다 효율적으로 찾기 위한 확률 기반 자료구조.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 예를 들어 bloom filter를 16비트의 배열로 정의하고 hash function을 3개로 정의했다고 가정하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- row key가 tistory.com이 들어왔다고 한다면 해당 tistory.com을 우선 숫자로 변환을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 12345로 변환이 됐다고 한다면 해당 숫자를 각각의 hash function(3개)에 넣는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- hash function1은 3을 리턴하고, hash function2는 15를 리턴하고, hash function3은 14를 리턴했다면, 각각의 해당하는 비트의 값을 1로 바꿔준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 서비스에서 실제 tistory.com이 들어왔고 hash function 3개의 값이 각각 3,15,14라고 나왔다면 bloom filter의 각각에 해당하는 비트를 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 해당하는 비트가 모두 1이라면 해당 값은 있다고 간주하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- bloom filter에는 false-negative (없는데 사실은 있었던 것)인 경우는 절대 없으나, false-positive(있다고 했는데 사실은 없었던 것)의 경우는 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Hbase Column Family&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Hbase에는 독특하게 Column Family와 Column Qulifier가 존재한다. (A:id, A:name, B:grade, B:collage)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 동일한 Column Family로 묶이면 하나의 HFile에 쓰여지게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이렇게 되면 어떤 장점이 생기냐면, 행 단위 검색이 빨라지는 동시에 해당 행과 연관되어 있는 다른 행들에 대한 검색 연산도 빨라지는 효과가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 또한 Column Family 단위로 압축 기법을 다르게 줌으로써 압축 효율을 늘릴 수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 하지만 특정 Column Family의 MemStore가 가득 차서 HFile로 flush가 발생하게 되면, 같은 region 내의 작은 크기의 다른 Column Family 들도 함께 flush가 되는 단점이 생긴다. 이는 small HFile이 다수로 생성이 되며, 성능 저하의 문제가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;meta table region server가 죽으면?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- meta table을 가진 region server가 죽으면 어떤일이 발생할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- zk에서는 meta table을 가진 region server의 세션이 끊겼기 때문에 에페머럴 노드가 삭제가 된다. 해당 노드가 삭제되면 HMaster에서는 이를 감지하고, 나머지 region server 중 하나를 선택하여 meta table region server로 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 해당 region server에서는 meta table의 Hfile과 wal data를 가져와서 자신의 메모리에 올린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- HMaster는 zk에 새로운 meta table region server를 등록한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;HMaster가 죽으면?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- get, put 등의 동작은 HMaster가 직접적인 영향을 끼치지 않기 때문에 동작하는데 문제가 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 하지만 region split이나 compaction 등의 작업은 이루어지지 않는다. 한쪽에 region이 몰리게 된다면 이를 제어할 방법이 사라진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Server/centos</category>
      <category>bulk load</category>
      <category>CS</category>
      <category>Hbase</category>
      <category>면접 대비</category>
      <author>유주원</author>
      <guid isPermaLink="true">https://yujuwon.tistory.com/463</guid>
      <comments>https://yujuwon.tistory.com/entry/%EB%A9%B4%EC%A0%91-%EB%8C%80%EC%9D%91-hbase-%EA%B4%80%EB%A0%A8-%EC%A7%88%EB%AC%B8-%EB%B0%8F-%EB%8B%B5%EB%B3%80#entry463comment</comments>
      <pubDate>Tue, 3 Feb 2026 14:27:59 +0900</pubDate>
    </item>
    <item>
      <title>[면접 대응] hadoop 관련 질문 및 답변</title>
      <link>https://yujuwon.tistory.com/entry/%EB%A9%B4%EC%A0%91-%EB%8C%80%EC%9D%91-hadoop-ha-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;hadoop ha 동작 방식 설명&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hadoop에서는 namenode에서 datanode에 저장되어 있는 data에 대한 파일시스템 트리와 해당 파일, 디렉토리에 대한 메타 데이터 정보를 저장하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 datanode에 아무리 데이터가 저장되어 있다고 하더라도 namenode가 통신이 되지 않으면 해당 데이터에 대한 접근이 불가능하다. 이러한 이유로 name node가 서비스 중지되는 것을 막기 위해 name node를 이중화하여 high availability를 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hadoop ha가 어떤 방식으로 이중화가 되는지에 대해 살펴 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;628&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uP6BB/dJMcaia73jI/DbcLLUWvJ7tee06KMBn8c0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uP6BB/dJMcaia73jI/DbcLLUWvJ7tee06KMBn8c0/img.jpg&quot; data-alt=&quot;https://techvidvan.com/tutorials/hadoop-hdfs-namenode-high-availability-hadoop/&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uP6BB/dJMcaia73jI/DbcLLUWvJ7tee06KMBn8c0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuP6BB%2FdJMcaia73jI%2FDbcLLUWvJ7tee06KMBn8c0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1200&quot; height=&quot;628&quot; data-origin-width=&quot;1200&quot; data-origin-height=&quot;628&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://techvidvan.com/tutorials/hadoop-hdfs-namenode-high-availability-hadoop/&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 namenode에 대해 active node와 standby node를 설정하기 위해 name node 두 대를 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;active node에 장애가 발생하면 standby node가 active node로 변경이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;그럼 어떻게 active node가 장애가 발생했는지를 알까?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- name node의 장애는 ZKFC (ZooKeeper Failover Controller)에서 체크를 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;정상 상태일 경우&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- ZKFC는 name node와 함께 프로세스로 뜬 후에 name node를 상태를 주기적으로 풀링한다. (너 살아있니??)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- name node가 정상적으로 떠 있는 상태라면 ZKFC는 zookeeper에게 나 살아있어!! 라는 ping을 보내게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- zookeeper에서는 session timeout 기간 동안에 해당하는 ping을 전송받았다면 해당 session을 유지하고 해당 session과 연결되어 있던 ActiveStandbyElectorLock이라는 임시 노드를 유지하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- standby node에 떠 있는 ZKFC에서는 해당 노드의 Lock을 획득하려고 시도하지만 해당 노드가 살아 있기 때문에 획득은 하지 못하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;FailOver 발생 시&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- active node의 name node가 내려간 경우 ZKFC는 name node로부터 ping을 전달 받지 못하게 되고, zookeeper에서 살아 있다는 ping도 전송하지 못하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- zookeeper에서는 session timeout 동안 기다리다가 ping이 유입되지 않으면 ActiveStandbyElectorLock 임시 노드를 삭제하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- standby에 있던 ZKFC에서는 주기적으로 해당 Lock 노드를 체크하다가 해당 Lock 노드가 삭제가 되면 standby node를 active node로 변경시킨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;Split brain 방지&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- standby node가 active node로 변경이 되었다고 하더라도, 기존 active node가 죽지 않고 살아 있다면 data 무결성에 영향을 미칠 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이런 현상이 발생하면, client는 어떤 name node에 접속했느냐에 따라 완전히 다른 메타 데이터를 리턴받게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 두 active node 모두 journal node에 update 기록을 쓰게 되면, 나중에 시스템 복구 시 journal node를 통한 메타 데이터 복구 시 정확한 데이터를 복구할 수 없게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이를 막기 위해 hadoop에서는 fensing 기법을 통해 split brain을 방지 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- sshfensing : ssh로 접근해서 8020 port를 점유하고 있는 프로세스를 강제로 죽인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- custom shell : 사용자가 작성한 shell script를 실행해서 좀비 active node를 죽인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- journal node에서의 차단 : journal node에서는 새로운 active node가 선출되면 새로운 고유 epoch 번호를 active node로부터 전송 받는다. 그리고는 해당 epoch 번호보다 큰 번호의 name node 요청만 수락한다. 만일 이전 active node에서 journal node에 쓰기 요청을 하더라고 현재 쓰여진 epoch 번호보다 낮기 때문에 무시하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;active node와 standby node의 failOver는 어떤식으로 동작하게 될까?&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- standby node는 주기적으로 자신의 메모리에 있는 파일 메타데이터를&amp;nbsp; fsimage로 생성한 후 이를 active node에게 전달한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- active node는 standby node로부터 fsimage를 전달 받으면 기존 fsimage를 삭제하고 새로운 fsimage로 대체한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- standby node는 주기적으로 journal node에 editing log를 가져와서 자신의 메모리에 적용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이 시점에서 만약 active node가 내려갔다면??&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- standby node는 journal node에 아직 읽지 않은 editing log를 모두 가져와서 업데이트 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 이 후 standby node는 journal node에게 새로운 epoch 번호를 전송한다. 이 후부터는 journal node에서는 새로 전달받은 epoch 번호 보다 낮은 log가 전송된 경우는 무시하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- standby 모드를 해제하고 active mode로 변경한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;active node와 standby node간의 switching 시간 동안의 장애를 최소한으로 하려면?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- standby node의 edit log tail의 시간을 줄임으로써 switching 시간을 단축 시킬 수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- dfs.ha.tail-edits.period 기간을 단축해서 edit 로그를 가져오는 주기를 짧게 설정하거나 dfs.ha.tail-edits.in-progress를 활성화 함으로써 실시간으로 edit log를 가져오게 하면 standby node의 대기 시간을 줄일 수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;journal node를 홀수로 구성하는 이유??&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- split brain을 방지하기 위함임.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 의사 결정의 교착 상태를 방지함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 예를 들어 새로운 active node가 승격이 되어 epoch 번호를 전달했다고 가정하자. journal node 2대에서는 새로운 epoch 번호를 전달 받았지만, 나머지 2대에서는 기존 epoch 번호를 유지하고 있다고 한다면, 어떤 node가 active node인지 판단을 할 수가 없게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;hdfs block이란?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- hadoop 파일 시스템에서 데이터를 저장하는 가장 기본적인 단위. 기본적으로 128mb 단위로 크게 잡는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 크게 잡는 이유는 탐색 시간을 줄이기 위함이다. 작은 데이터 블록이 여러개가 있다면 이에 대한 위치를 찾기 위한 시간이 오래 걸리지만 큰 데이터 블록이 한 개가 있다면 해당 블록을 읽어 내려가기만 하면 되기 때문에 시간적으로 더 효과적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 그렇다면 아주 크게 block size를 잡으면 되지 않을까??&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 크게 block size를 잡게 된다면 탐색이나 name node 메모리 사용 측면에서는 유리할 수 있으나, 병렬 처리 측면에서 비효율적일 수 있다. 예를 들어 128mb 8개짜리 블록을 8대의 node에서 돌릴 수 있으나, 1gb 1개 짜리 블록은 1대의 머신에서만 돌릴 수 있기 때문이다. 또한 블록 사이즈가 크게 되면, 특정 데이터 노드에서만 traffic이 몰리는 현상이 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;&lt;b&gt;hdfs에서 replica가 3개인데 data node 3대 중 1대가 죽었을 경우?&lt;/b&gt;&lt;/span&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- data node가 죽으면 name node에서는 더이상 heart beat를 받지 못하게 되고 해당 data node가 죽었음을 인지하게 된다. 이 상태에서&amp;nbsp; name node는 죽은 data node가 가진 block들을 전수 조사하고 남은 data node들에게 replica를 3개로 맞추도록 요청을 날린다. 하지만 hdfs의 정책 상 동일한 block은 동일한 data node에 저장할 수 없도록 되어 있기 때문에 name node는 복제 상대를 찾지 못하고 대기 상태(under replicated) 가 된다. 대기 상태라고 하더라도 서비스는 정상적으로 동작이 되며, hadoop cluster만 경고 상태인 채로 서비스가 운영이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Rack awareness란?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 하둡은 기본적으로 복제본을 다음과 같이 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 첫 번째 복제본 : 자신의 로컬 노드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 두 번째 복제본 : 첫 번째 노드와 다른 rack에 있는 노드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 세 번째 복제본 : 두 번째 노드와 같은 rack이지만 다른 노드&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rack topology 파일을 적당히 수정함으로써 논리적으로 다른 rack에 있는 듯 하게 하둡 설정을 할 수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;Erasure coding이란?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 하둡 3.x에서 새롭게 도입된 기술로써, 기존의 replica 3 방식에서 데이터 저장 효율을 극대화한 방식이다. 예를 들어 replica가 3인 경우 기존에는 기존의 데이터 크기 * 3 만큼의 디스크 공간이 필요했지만, erasure coding의 경우는 기존 데이터를 6개로 쪼개고 여기에 패리티 블록 3개를 추가한 총 9개를 만듬으로써 데이터 복구가 가능하게 한다. 패리티 블록의 경우는 약간 방정식과 비슷한데, 예를 들어 1,2,3 블록이 있다고 가정한다면 이에 대한 패리티 블록은 1+2+3= 6이다. 3 블록을 유실한다고 하더라도 1+2+x = 6이 되는 수가 3이기 때문에 패리티 블록을 통해 기존 데이터 블록을 쉽게 복구할 수가 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;span style=&quot;color: #006dd7;&quot;&gt;yarn이란?&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 클러스터 리소스를 관리하고 작업을 스케쥴링하는 시스템&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 사용자가 작업을 resource manager에게 전달하면, resource manager는 하나의 노드를 선택해 application master로 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- application master는 필요한 작업에 대한 리소스를 분석한 후 resource manager에게 필요한 자원을 요청한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- resource manager가 승인하면 application master는 각 node manager에게 작업 실행을 요청한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 각 node manager에서 container를 실행시켜서 작업을 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Server/centos</category>
      <category>active node</category>
      <category>Failover</category>
      <category>Ha</category>
      <category>hadoop</category>
      <category>standby node</category>
      <category>zkfc</category>
      <category>zookeeper failover controller</category>
      <author>유주원</author>
      <guid isPermaLink="true">https://yujuwon.tistory.com/462</guid>
      <comments>https://yujuwon.tistory.com/entry/%EB%A9%B4%EC%A0%91-%EB%8C%80%EC%9D%91-hadoop-ha-%EB%8F%99%EC%9E%91-%EB%B0%A9%EC%8B%9D#entry462comment</comments>
      <pubDate>Mon, 2 Feb 2026 13:47:25 +0900</pubDate>
    </item>
    <item>
      <title>aab에서 키 해시 확인하기</title>
      <link>https://yujuwon.tistory.com/entry/aab%EC%97%90%EC%84%9C-%ED%82%A4-%ED%95%B4%EC%8B%9C-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;거의 5년만에 신규 안드로이드 앱을 출시해봤다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어 근데 출시 버튼을 누르고 apk를 업로드하려고 하면 apk가 비활성화되어 업로드를 할 수가 없다!!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-30 오전 10.27.22.png&quot; data-origin-width=&quot;2132&quot; data-origin-height=&quot;1234&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lrVdn/dJMcac9JAyb/kdgBic33fnUCKPNxwH5ikK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lrVdn/dJMcac9JAyb/kdgBic33fnUCKPNxwH5ikK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lrVdn/dJMcac9JAyb/kdgBic33fnUCKPNxwH5ikK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlrVdn%2FdJMcac9JAyb%2FkdgBic33fnUCKPNxwH5ikK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2132&quot; height=&quot;1234&quot; data-filename=&quot;스크린샷 2026-01-30 오전 10.27.22.png&quot; data-origin-width=&quot;2132&quot; data-origin-height=&quot;1234&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이거 모지??? 이젠 apk 말고 aab만 업로드가 되는건가?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 참고로 apk와 aab에 대해서 알아보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;apk는 안드로이드에서 앱을 설치하기 위한 표준 파일 형식으로 윈도우의 exe와 비슷하다고 보면 될 것 같다. apk 내에는 코드, 인증서, 리소스 파일 등이 모두 포함되어 있기 때문에 사용자는 apk 파일을 통해 자신의 폰 내에 앱을 설치할 수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;aab는 구글에서 안드로이드 앱을 배포하기 위한 최신 게시 형태이다. apk와의 차이를 보자면 apk는 모든 기기에 대한 리소스를 포함하고 있는 반면, aab는 구글플레이에서 최적화 해서 필요한 리소스만 설치하도록 최적화를 제공한다. 최종적으로 보면 aab는 앱 동작을 위한 필수적인 base apk와 각 archtecture별, 화면 밀도별, 언어별 apk가 각각 생성이 된다. 사용자가 구글 플레이에서 설치 버튼을 누르게 되면 해당 사용자의 기기에 맞는 apk가 각각 선택되어 사용자의 기기에 설치가 되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;aab build는 생각보다 간단하다. 기존의 Generate signed App Bundle or Apk 메뉴에서 apk 대신 app bundle을 선택한 후에 build를 하면 aab 파일이 생성이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 생성된 aab 파일을 구글플레이개발자 콘솔의 출시 화면에 업로드를 하면 앱 출시를 위한 업로드가 완료가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 문제가 하나 발생 했는데, 현재 개발된 app에서는 카카오 API를 사용하고 있고 카카오 API를 사용하기 위해서는 안드로이드 키 해시 값이 필요한데, 기존의 apk의 경우에는 아래와 같이 키 해시를 확인했지만, aab의 경우는 키 서명을 구글플레이스토어에서 진행하기 때문에 다른 방법이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 apk 키 해시 방법&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://yujuwon.tistory.com/entry/keystore-%ED%95%B4%EC%8B%9C-%EA%B0%92-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://yujuwon.tistory.com/entry/keystore-%ED%95%B4%EC%8B%9C-%EA%B0%92-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1769737365771&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;[ANDROID] 안드로이드 해시 값 확인하기&quot; data-og-description=&quot;카카오 API를 사용하기 위해서는 카카오 개발자 센터에서 안드로이드 키 해시를 등록해야 한다. 만약 해당 안드로이드와 다른 키 해시 값이 등록이 될 경우 API 값을 리턴 받을 수가 없다.그리고 &quot; data-og-host=&quot;yujuwon.tistory.com&quot; data-og-source-url=&quot;https://yujuwon.tistory.com/entry/keystore-%ED%95%B4%EC%8B%9C-%EA%B0%92-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0&quot; data-og-url=&quot;https://yujuwon.tistory.com/entry/keystore-%ED%95%B4%EC%8B%9C-%EA%B0%92-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/crTdod/dJMb9iaLX3t/0zvIfunkHpziR0MU1KgyLk/img.jpg?width=800&amp;amp;height=127&amp;amp;face=0_0_800_127,https://scrap.kakaocdn.net/dn/b3lVCA/dJMb9jgr02e/BIkuKlekiA9zL3osDLTRu1/img.jpg?width=800&amp;amp;height=127&amp;amp;face=0_0_800_127&quot;&gt;&lt;a href=&quot;https://yujuwon.tistory.com/entry/keystore-%ED%95%B4%EC%8B%9C-%EA%B0%92-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://yujuwon.tistory.com/entry/keystore-%ED%95%B4%EC%8B%9C-%EA%B0%92-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/crTdod/dJMb9iaLX3t/0zvIfunkHpziR0MU1KgyLk/img.jpg?width=800&amp;amp;height=127&amp;amp;face=0_0_800_127,https://scrap.kakaocdn.net/dn/b3lVCA/dJMb9jgr02e/BIkuKlekiA9zL3osDLTRu1/img.jpg?width=800&amp;amp;height=127&amp;amp;face=0_0_800_127');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;[ANDROID] 안드로이드 해시 값 확인하기&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;카카오 API를 사용하기 위해서는 카카오 개발자 센터에서 안드로이드 키 해시를 등록해야 한다. 만약 해당 안드로이드와 다른 키 해시 값이 등록이 될 경우 API 값을 리턴 받을 수가 없다.그리고&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;yujuwon.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;업로드한 aab에 대한 키 해시를 확인하기 위해서는 아래와 같은 방법이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 구글플레이 콘솔로 들어가서 테스트 및 출시 메뉴의 앱 무결성 메뉴를 클릭한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-30 오전 10.43.57.png&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;1272&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sHTK4/dJMcacaTGS0/PIYgz43nsjq8a4JyVmOcek/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sHTK4/dJMcacaTGS0/PIYgz43nsjq8a4JyVmOcek/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sHTK4/dJMcacaTGS0/PIYgz43nsjq8a4JyVmOcek/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsHTK4%2FdJMcacaTGS0%2FPIYgz43nsjq8a4JyVmOcek%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;300&quot; height=&quot;647&quot; data-filename=&quot;스크린샷 2026-01-30 오전 10.43.57.png&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;1272&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오른쪽 화면을 보면 Play 앱 서명 메뉴가 있다. 설정 버튼을 누르자.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-01-30 오전 10.44.55.png&quot; data-origin-width=&quot;2704&quot; data-origin-height=&quot;238&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yGTIO/dJMcafZJjSc/rIFI3vBKmkVnrYPnTRMyAK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yGTIO/dJMcafZJjSc/rIFI3vBKmkVnrYPnTRMyAK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yGTIO/dJMcafZJjSc/rIFI3vBKmkVnrYPnTRMyAK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyGTIO%2FdJMcafZJjSc%2FrIFI3vBKmkVnrYPnTRMyAK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2704&quot; height=&quot;238&quot; data-filename=&quot;스크린샷 2026-01-30 오전 10.44.55.png&quot; data-origin-width=&quot;2704&quot; data-origin-height=&quot;238&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱 서명 키 인증서 화면이 나타나고 SHA-1 인증서 지문이 보인다. 해당 내용을 복사한 후 아래와 같이 터미널에서 명령어를 날려주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;$&amp;gt; echo &quot;SHA-1 값&quot; | xxd -r -p | openssl base64&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;base64로 인코딩 된 결과 값이 출력이 되고 해당 값을 카카오 키 해시로 등록하면 된다.&lt;/p&gt;</description>
      <category>Programming/Android</category>
      <category>AAB</category>
      <category>안드로이드 출시</category>
      <category>키 해시</category>
      <author>유주원</author>
      <guid isPermaLink="true">https://yujuwon.tistory.com/461</guid>
      <comments>https://yujuwon.tistory.com/entry/aab%EC%97%90%EC%84%9C-%ED%82%A4-%ED%95%B4%EC%8B%9C-%ED%99%95%EC%9D%B8%ED%95%98%EA%B8%B0#entry461comment</comments>
      <pubDate>Fri, 30 Jan 2026 10:50:06 +0900</pubDate>
    </item>
    <item>
      <title>실용주의 프로그래머 - 실용주의 철학</title>
      <link>https://yujuwon.tistory.com/entry/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://search.daum.net/search?w=bookpage&amp;amp;bookId=927041&amp;amp;tab=introduction&amp;amp;DA=LB2&amp;amp;q=%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98%20%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://search.daum.net/search?w=bookpage&amp;amp;bookId=927041&amp;amp;tab=introduction&amp;amp;DA=LB2&amp;amp;q=%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98%20%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1763011694837&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;실용주의 프로그래머 &amp;ndash; Daum 검색&quot; data-og-description=&quot;Daum 검색에서 실용주의 프로그래머에 대한 최신정보를 찾아보세요.&quot; data-og-host=&quot;search.daum.net&quot; data-og-source-url=&quot;https://search.daum.net/search?w=bookpage&amp;amp;bookId=927041&amp;amp;tab=introduction&amp;amp;DA=LB2&amp;amp;q=%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98%20%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8&quot; data-og-url=&quot;https://search.daum.net/search?DA=LB2&amp;amp;bookId=927041&amp;amp;q=%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98+%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8&amp;amp;tab=introduction&amp;amp;w=bookpage&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/MCFqI/hyZNUEzmNu/6YQkwUYObLnPgN4xSHHUW0/img.png?width=1600&amp;amp;height=800&amp;amp;face=0_0_1600_800&quot;&gt;&lt;a href=&quot;https://search.daum.net/search?w=bookpage&amp;amp;bookId=927041&amp;amp;tab=introduction&amp;amp;DA=LB2&amp;amp;q=%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98%20%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://search.daum.net/search?w=bookpage&amp;amp;bookId=927041&amp;amp;tab=introduction&amp;amp;DA=LB2&amp;amp;q=%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98%20%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/MCFqI/hyZNUEzmNu/6YQkwUYObLnPgN4xSHHUW0/img.png?width=1600&amp;amp;height=800&amp;amp;face=0_0_1600_800');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;실용주의 프로그래머 &amp;ndash; Daum 검색&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Daum 검색에서 실용주의 프로그래머에 대한 최신정보를 찾아보세요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;search.daum.net&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 책은 실용주의 프로그래머가 되기 위한 여러가지 방법들을 서술해 놓고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1장에서는 실용주의 프로그래머가 되기 위해서는 실용적 철학이 있어야 함을 강조하고 있으며, 그 실용적 철학에 대한 기본 사상을 설명하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. 당신의 인생이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 나에게는 바꿀 수 있는 agency가 있다. 불평 불만만 늘어놓지 말고 직접 바꿔보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 고양이가 내 소스 코드를 삼켰어요&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 책임을 질 수 있어야 한다. 고양이가 내 소스 코드를 삼켰어요 같은 어설픈 변명 보다는 현실적인 대안을 제시하도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. 소프트웨어 엔트로피&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 엔트로피란 무질서를 나타내는 물리학 용어이다. 소프트웨어 엔트로피란 소프트웨어의 무질서를 말하며 즉 기술 부채를 이야기 한다. 어떤 경우에 기술 부채가 발생하는 걸까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 어떤 건물에 깨진 창문이 방치되어 있다면. 그 건물은 방치되어 있다는 느낌을 받을 것이다. 깨진 창문 사이로 무수한 쓰레기가 던져질 것이며, 다른 창문도 깨지게 될 것이다. 결국 해당 건물은 실제로 방치되게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;창문이 깨져 있다면 곧바로 고치도록 하자. 만약 고칠 능력이 되지 않는다면 주석으로 언급이라도 해 놓도록 함으로써 해당 코드가 방치되어 있지 않음을 알리도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;4. 돌멩이 수프와 삶은 개구리&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 군인들이 먹을 것을 찾기 위해 마을에 들렀지만, 마을 사람들은 자신들이 가진 것을 꽁꽁 숨기고 내놓지 않고 있었다. 이에 군인들은 돌멩이를 삶기 시작했다. 마을 사람들은 궁금함에 무엇을 하는지 물어봤고, 군인들은 정말 맛있는 돌멩이 스프를 끓이는 중이라고 대답하며, 당근이나 감자 이런게 있으면 더 깊은 맛이 날 수 있다고도 설명했다. 그러자 마을 사람들은 하나 둘 씩 자기가 가지고 있던 음식들을 내놓게 되었고 결국 정말 근사한 스프가 완성 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나 자신이 돌멩이 스프처럼 변화의 촉매가 되자. 크게 무리하지 않고 요구할 수 있는 걸 찾자. 그리고 중간 중간에 이걸 통해 완성될 미래를 알려준다면 더욱 동기 부여가 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 삶은 개구리처럼 되지 말자. 개구리를 냄비 안에 넣고 서서히 온도를 올리게 되면 개구리는 온도가 올라가는 것을 눈치 채지 못하고 죽어버리게 된다. 변화를 감지하고 큰 그림을 살펴 볼 수 있어야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 과연 돌멩이 스프를 만들 것인지, 개구리 스프를 만들 것인지를 잘 판단할 수 있어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;5. 적당히 괜찮은 소프트웨어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 버그 없는 완벽한 소프트웨어를 만들기란 불가능하다. 적당히 괜찮은 소프트웨어를 만들자. 여기서 적당히 괜찮은 소프트웨어란 과연 멀까? 사용자의 요구사항이 반영된 소프트웨어는 적당히 괜찮은 소프트웨어라고 할 수 있다. 소프트웨어의 품질을 요구사항으로 만들자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완벽해지기 위해 코드를 지나치게 다듬지 마라. 그림 그리기와 똑같다. 덧칠을 계속 하면 할 수록 그림을 망치듯이 개발도 똑같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;6. 지식 포트폴리오&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 지식에 대한 투자는 언제나 최고의 이윤을 낸다. 지식은 날로 변화하고 새롭게 되기 때문에 기한이 있다. 그렇게 때문에 항상 새로운 것을 배워야 한다. 과연 어떻게 배워야 할까?? 책에서는 아래와 같이 지식 포트폴리오를 쌓자고 말하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주기적인 투자 : 주기적으로 지식에 투자하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다각화 : 현재 자신이 사용하는 기술에 대해 자세히 알자. 그리고 더 여러 가지를 계속 배우자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리스크 관리 : 기술 달걀을 한 바구니에 담지 말라. (하나만 익히지 말라는 소리 같음)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;싸게 사서 비싸게 팔기 : 기술이 인기를 끌기 전에 익혀 두면 비싸게 팔 수가 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검토 및 재조정 : 기술은 매우 동적으로 변하기 때문에 시시각가 리밸런싱을 할 필요가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;7. 소통하라&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 여러 입장에서 소통하자. 코드를 작성한다는 것은 나의 생각을 다음 세대의 개발자들에게 전달하는 것과 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 소통해야 할까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 청중을 알아야 한다. 청중이 이해하고 있지 않다면 그냥 단순히 혼자 말하고 있는 것 뿐이다. 청중을 알기 위해서는 피드백을 받을 수 있어야 한다. 질문을 기다리지 말고 먼저 물어보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음으로는 내가 말하고 싶은 것을 먼저 계획하자. 그리고 이렇게 말하면 청중이 알아 들을 수 있을까? 생각하자. 그렇게 될 때까지 계속 다듬자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;때를 고를 줄 알아야 한다. 지금 이 말을 하기가 좋은 때일까를 생각하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스타일을 골라라. 말하는 스타일을 청중에 어울리도록 조정한다. 청중이 전문가인지, 신입인지 등을 고려한 후 말하는 스타일을 정하면 청중은 듣기가 한결 수월할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;멋져 보이게 하라. 마크 다운등을 활용해서 readme 등을 멋지게 만들면 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;경청하라. 말하는 도중 질문을 해서 청중이 이야기하도록 하거나 회의를 대화 형태로 바꾸면, 경청 모드를 활성화 시킬 수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서화하라. 문서를 나중에 따로 만들 생각 하지 말고 애초부터 주석 등으로 코드 안에 녹여 내자. 자료 구조, 타입 정의, 동작 방식 등을 주석다는 것은 비효율적이다. 타입을 변경할 때마다 주석도 함께 변경해야 한다. 주석으로 남길 것은 기술적인 절충점이나 결정의 이유, 폐기한 다른 대안 등을 주석으로 남기면 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>Book review</category>
      <category>실용주의 철학</category>
      <category>실용주의 프로그래머</category>
      <author>유주원</author>
      <guid isPermaLink="true">https://yujuwon.tistory.com/458</guid>
      <comments>https://yujuwon.tistory.com/entry/%EC%8B%A4%EC%9A%A9%EC%A3%BC%EC%9D%98-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8#entry458comment</comments>
      <pubDate>Thu, 13 Nov 2025 16:23:16 +0900</pubDate>
    </item>
    <item>
      <title>[ELASTICSEARCH] analyzer</title>
      <link>https://yujuwon.tistory.com/entry/ELASTICSEARCH-analyzer</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;es에서 anayzer는 아래와 같은 형태로 구성이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2010&quot; data-origin-height=&quot;804&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ykh07/btsQpXZ6nkl/IeKCzPzjX87MAh8S2eaz40/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ykh07/btsQpXZ6nkl/IeKCzPzjX87MAh8S2eaz40/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ykh07/btsQpXZ6nkl/IeKCzPzjX87MAh8S2eaz40/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fykh07%2FbtsQpXZ6nkl%2FIeKCzPzjX87MAh8S2eaz40%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;2010&quot; height=&quot;804&quot; data-origin-width=&quot;2010&quot; data-origin-height=&quot;804&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;analyzer를 구성하는데 있어서 Char Filter와 Token Filter는 없어도 무방하지만 Tokenizer는 반드시 설정이 되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Char Filter는 사용자 query가 들어오면 해당 query에서 전처리하는 역할을 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Tokenizer는 Char Filter에서 전처리된 결과를 Tokenizer 알고리즘에 맞게 Tokenizing을 하는 역할을 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 tokenizer 들이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.3024%;&quot;&gt;standard&lt;/td&gt;
&lt;td style=&quot;width: 85.6976%;&quot;&gt;es에서 제공하는 기본 tokenizer. 텍스트 기반으로 tokenizing한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.3024%;&quot;&gt;lowercase&lt;/td&gt;
&lt;td style=&quot;width: 85.6976%;&quot;&gt;모든 문자를 소문자로 변환하며, 텍스트 기반으로 tokenizing한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.3024%;&quot;&gt;ngram&lt;/td&gt;
&lt;td style=&quot;width: 85.6976%;&quot;&gt;n개의 연속된 단어 모음으로 tokenizing한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 14.3024%;&quot;&gt;uax_url_email&lt;/td&gt;
&lt;td style=&quot;width: 85.6976%;&quot;&gt;url 혹은 email을 tokenzing하는 데 특화되어 있다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마지막으로 Token Filter는 Tokenizer에 의해 Token화 된 결과들을 filter 하는 역할을 하며 주로 아래와 같은 filter가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.4884%;&quot;&gt;lowercase&lt;/td&gt;
&lt;td style=&quot;width: 86.5116%;&quot;&gt;각 token들을 모두 소문자로 변환한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.4884%;&quot;&gt;uppercase&lt;/td&gt;
&lt;td style=&quot;width: 86.5116%;&quot;&gt;각 token들을 모두 대문자로 변환한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.4884%;&quot;&gt;stemmer&lt;/td&gt;
&lt;td style=&quot;width: 86.5116%;&quot;&gt;각 token들을 모두 어원 형태로 변환한다. 영문에만 적용이 된다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 13.4884%;&quot;&gt;stop&lt;/td&gt;
&lt;td style=&quot;width: 86.5116%;&quot;&gt;많이 등장하지만 의미 없는 token들 (a, the)을 제거한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 analyzer를 호출해보자. rest api로 손쉽게 호출이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;$&amp;gt; curl -X POST &quot;localhost:9200/_analyze&quot; -H 'Content-Type : application/json' -d'&lt;br /&gt;{&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;analyzer&quot; : &quot;stop&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;text&quot; : &quot;i am a boy&quot;&lt;br /&gt;}&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;char filter와 token filter도 함께 호출해 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;$&amp;gt; curl -X POST &quot;localhost:9200/_analyze&quot; -H 'Content-Type: application/json' -d'&lt;br /&gt;{&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;char_filter&quot; : [&quot;&quot;],&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;analyzer&quot; : &quot;standard&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;filter&quot; : [&quot;lowercase&quot;, &quot;stop&quot;],&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;text&quot; : &quot;i am a boy&quot;&lt;br /&gt;}&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제에서는 char_filter는 따로 사용하지 않았고, tokenizer는 standard를 사용했으며, token filter는 lowercase filter와 stop filter를 사용했다. 순서는 char_filter -&amp;gt; standard analyzer -&amp;gt; lowercase filter -&amp;gt; stop filter 순서로 거치게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 filter의 순서가 중요한데 filter가 [&quot;lowercase&quot;, &quot;stop&quot;]으로 지정되어 있고 stop filter에 lion이라는 단어가 들어 있고, text에 &quot;I am a Lion&quot;이라고 들어 왔을 경우에, 결과 token은 i, am 이 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼 이번에는 filter가 [&quot;stop&quot;, &quot;lowercase&quot;]로 지정되어 있다면 어떻게 될까? 결과 token은 i, am, lion이 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ee2323;&quot;&gt;&lt;b&gt;이처럼 filter의 순서가 결과에 영향을 끼친다는 걸 유념하자&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;마지막으로 내가 원하는 analyzer를 직접 구성해 볼 수도 있다. 아래의 예를 참고하자. 아래의 예제는 index 생성 시에 setting 항목에 나만의 custom analyzer를 추가한 예제이다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;{&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;settings&quot; : {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;analysis&quot; : {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;filter&quot; : {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;custom_stopword&quot; : {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;type&quot; : &quot;stop&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;stopwords&quot; : [&quot;lion&quot;]&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; },&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;analyzer&quot; : {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;custom_analyzer&quot; : {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;type&quot; : &quot;custom&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;char_filter&quot; : [],&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;tokenizer&quot; : &quot;standard&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;filter&quot; : [&quot;lowercase&quot;, &quot;custom_stopword&quot;]&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;anaylzer 항목에 내가 만들 analyzer 이름을 지정하고, type을 custom으로 지정하면 custom analyzer를 구성할 수가 있다. token filter에서도 내가 만든 filter를 적용할 수가 있는데, analysis 항목의 filter에서 내가 만들 filter명을 기입하고, type과 그에 맞는 값을 넣어주면 된다. 예제에서는 stop filter에 lion이란 단어를 추가함으로써 나만의 stop filter를 구성하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 custom analyzer와 custom filter는 setting이 추가된 index에서만 사용이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;출처 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://shoppinghow.kakao.com/search/%EC%97%98%EB%9D%BC%EC%8A%A4%ED%8B%B1%20%EC%84%9C%EC%B9%98%20%EA%B0%9C%EB%B0%9C%EB%B6%80%ED%84%B0%20%EC%9A%B4%EC%98%81%EA%B9%8C%EC%A7%80/docid:V26588304018&amp;amp;srchhow:Cexpo&amp;amp;&quot;&gt;https://shoppinghow.kakao.com/search/%EC%97%98%EB%9D%BC%EC%8A%A4%ED%8B%B1%20%EC%84%9C%EC%B9%98%20%EA%B0%9C%EB%B0%9C%EB%B6%80%ED%84%B0%20%EC%9A%B4%EC%98%81%EA%B9%8C%EC%A7%80/docid:V26588304018&amp;amp;srchhow:Cexpo&amp;amp;&lt;/a&gt;&lt;/p&gt;</description>
      <category>Big Data</category>
      <category>Analyzer</category>
      <category>elasticsearch</category>
      <category>엘라스틱서치</category>
      <author>유주원</author>
      <guid isPermaLink="true">https://yujuwon.tistory.com/456</guid>
      <comments>https://yujuwon.tistory.com/entry/ELASTICSEARCH-analyzer#entry456comment</comments>
      <pubDate>Thu, 11 Sep 2025 16:29:56 +0900</pubDate>
    </item>
    <item>
      <title>[ELASTICSEARCH] Dynamic templates</title>
      <link>https://yujuwon.tistory.com/entry/ELASTICSEARCH-Dynamic-templates</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Dynamic templates란 es의 field mapping을 동적으로 mapping하는 걸 말한다. es에서 자동으로 field를 mapping 해 주는 동적 mapping과는 차이가 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 예시를 보면 이해가 될 것이다. 아래는 index 생성 시 dynamic templates를 적용한 예시이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;{&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;mappings&quot; : {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;dynamic_templates&quot; : [{&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;my_field&quot; : {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;matching_mapping_type&quot; : &quot;string&quot;,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;mapping&quot; : { &quot;type&quot; : &quot;keyword&quot; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }]&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 templates는 해당 index의 field 중 string type인 field 들에 대해 keyword로 mapping하도록 설정한 mapping table이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;es에서 자동으로 type을 설정해 주는 것과는 성격이 약간 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 내가 직적 field별 type을 정하지 않고, 이렇게 dynamic templates를 적용해야 하는 이유는 멀까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 모든 데이터를 잘 알고 있다면 상관이 없지만, 만약 모든 데이터에 대해 잘 알지 못한다면, 그래서 어떤 데이터 유형이 유입될 지 알 수 없다면 위와 같이 dynamic templates를 사용함으로써 특정 type에 대해 mapping 값을 지정해 줄 수가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;dynamic templates를 사용하기 위해서는 option 값이 중요한데 아래는 option 값의 종류를 설명하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 85px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20.2326%; height: 17px;&quot;&gt;match_mapping_type&lt;/td&gt;
&lt;td style=&quot;width: 79.7674%; height: 17px;&quot;&gt;데이터 type이 일치한 경우 mapping field에 지정한 type으로 변경한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20.2326%; height: 17px;&quot;&gt;match&lt;/td&gt;
&lt;td style=&quot;width: 79.7674%; height: 17px;&quot;&gt;field 명이 pattern과 일치할 경우 mapping field에 지정한 type으로 변경한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20.2326%; height: 17px;&quot;&gt;unmatch&lt;/td&gt;
&lt;td style=&quot;width: 79.7674%; height: 17px;&quot;&gt;field 명이 pattern과 일치할 경우 mapping field에서 제외시킨다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20.2326%; height: 17px;&quot;&gt;match_pattern&lt;/td&gt;
&lt;td style=&quot;width: 79.7674%; height: 17px;&quot;&gt;match option에 정규식 등을 사용할 때 지정한다.&lt;br /&gt;ex) match_pattern : regax&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; match : &quot;^profilt_\d+$&quot;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 17px;&quot;&gt;
&lt;td style=&quot;width: 20.2326%; height: 17px;&quot;&gt;path_match&lt;/td&gt;
&lt;td style=&quot;width: 79.7674%; height: 17px;&quot;&gt;match와 비슷하지만 nested나 object field에 대해서 적용한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 20.2326%;&quot;&gt;path_unmatch&lt;/td&gt;
&lt;td style=&quot;width: 79.7674%;&quot;&gt;unmatch와 비슷하지만 nested나 object field에 대해서 적용한다.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처 : &lt;a href=&quot;https://shoppinghow.kakao.com/search/%EC%97%98%EB%9D%BC%EC%8A%A4%ED%8B%B1%20%EC%84%9C%EC%B9%98%20%EA%B0%9C%EB%B0%9C%EB%B6%80%ED%84%B0%20%EC%9A%B4%EC%98%81%EA%B9%8C%EC%A7%80/docid:V26588304018&amp;amp;srchhow:Cexpo&amp;amp;&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://shoppinghow.kakao.com/search/%EC%97%98%EB%9D%BC%EC%8A%A4%ED%8B%B1%20%EC%84%9C%EC%B9%98%20%EA%B0%9C%EB%B0%9C%EB%B6%80%ED%84%B0%20%EC%9A%B4%EC%98%81%EA%B9%8C%EC%A7%80/docid:V26588304018&amp;amp;srchhow:Cexpo&amp;amp;&lt;/a&gt;&lt;/p&gt;</description>
      <category>Big Data</category>
      <category>dynamic templates</category>
      <category>elastic search</category>
      <category>엘라스틱서치</category>
      <author>유주원</author>
      <guid isPermaLink="true">https://yujuwon.tistory.com/455</guid>
      <comments>https://yujuwon.tistory.com/entry/ELASTICSEARCH-Dynamic-templates#entry455comment</comments>
      <pubDate>Wed, 10 Sep 2025 11:43:30 +0900</pubDate>
    </item>
    <item>
      <title>[ELASTICSEARCH] HOTDATA와 WARMDATA 구성하기</title>
      <link>https://yujuwon.tistory.com/entry/ELASTICSEARCH-HOTDATA%EC%99%80-WARMDATA-%EA%B5%AC%EC%84%B1%ED%95%98%EA%B8%B0</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;대용량의 데이터를 저장해야 하는 경우, 모든 데이터를 SSD에 저장하기에는 비용 문제가 크기 때문에, 자주 검색되는 데이터의 경우 HOTDATA에 그렇지 않은 데이터는 WARMDATA로 구분하여 저장하는 시나리오에 대해 생각해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;HOTDATA : 최근 1개월 데이터&lt;br /&gt;WARMDATA : 나머지 11개월 데이터&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HOTDATA의 경우 속도가 빠른 SSD 디스크를 사용하고, WARMDATA는 비용이 저렴한 SATA 데이터를 사용하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 최근 1개월 데이터의 경우 HOTDATA 쪽 node에 index가 설정하도록 하고, 1개월이 지난 데이터의 경우 WARMDATA 쪽 node로 shard가 이동하도록 구성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 각 node 별로 HOTDATA, WARMDATA를 구성하도록 하자. elasticsearch.yml에 아래와 같이 HOTDATA, WARMDATA를 구분해 주도록 하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;node.attr.box_type: hotdata&lt;br /&gt;# node.attr.box_type: warmdata&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 설정하고자 하는 node의 elasticsearch.yml에 위와 같이 각각 box_type을 hotdata 또는 warmdata를 설정해 주도록 한다. 설정 후에는 해당 node는 hotdata, warmdata로 구분이 가능하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근에 새롭게 생성되는 index는 hotdata로 설정된 node에 생성이 되어야 한다. 아래와 같이 index를 생성하자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;$&amp;gt; curl -X PUT &quot;localhost:9200/test?pretty&quot; -H 'Content-Type: application/json' -d'&lt;br /&gt;{&lt;br /&gt;&amp;nbsp; &amp;nbsp; 'mappings': {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; '_doc': {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;properties&quot;: { &quot;title&quot; : { &quot;type&quot; : &quot;keyword&quot; } }&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; },&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;settings&quot;: {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;index&quot;: {&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;number_of_shards&quot; : 2,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;number_of_replicas&quot; : 1,&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &quot;routing.allocation.require.box_type&quot;: &quot;hotdata&quot;&lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; }&lt;br /&gt;&amp;nbsp; &amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;'&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 test란 index로 색인되는 데이터는 HOTDATA node에 쌓이게 된다. 그렇다면 기존의 index들은 어떻게 해야 HOTDATA node로 옮길 수 있을까? 아래와 같이 옮기고자 하는 index의 routing.allocation 값을 설정해 주어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;$&amp;gt; curl -X PUT &quot;localhost:9200/old_test/_settings?pretty&quot; -H 'Content-Type: application/json' -d'&lt;br /&gt;{&lt;br /&gt;&amp;nbsp; &amp;nbsp; &quot;index.routing.allocation.require.box_type&quot;: &quot;hotdata&quot;&lt;br /&gt;}&lt;br /&gt;'&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;신규로 생성되는 index 값들은 HOTDATA node로 유입되게 설정을 완료하였다. 이제 한달이 지난 index에 대해 HOTDATA node 에서 WARMDATA node로 어떻게 이동시킬 수 있는지 살펴보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 한 달이 지난 데이터에 대해 일일이 호출함으로써 index를 allocation 시켜줄 수 있지만 해당 방법은 너무 번거롭기 때문에 curator에 등록해서 자동으로 allocation 하도록 설정 할 수가 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://yujuwon.tistory.com/entry/ELASTICSEARCH-CURATOR-%EC%97%AC%EB%9F%AC-%EA%B8%B0%EB%8A%A5-%ED%99%9C%EC%9A%A9%ED%95%98%EA%B8%B0&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;이에 대한 설명은 curator 포스팅에서 잘 설명하고 있다.&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;출처 :&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;a href=&quot;https://search.daum.net/search?w=bookpage&amp;amp;bookId=5554436&amp;amp;tab=introduction&amp;amp;DA=LB2&amp;amp;q=%EA%B8%B0%EC%B4%88%EB%B6%80%ED%84%B0%20%EB%8B%A4%EC%A7%80%EB%8A%94%20elasticsearch%20%EC%9A%B4%EC%98%81%20%EB%85%B8%ED%95%98%EC%9A%B0&quot;&gt;https://search.daum.net/search?w=bookpage&amp;amp;bookId=5554436&amp;amp;tab=introduction&amp;amp;DA=LB2&amp;amp;q=%EA%B8%B0%EC%B4%88%EB%B6%80%ED%84%B0%20%EB%8B%A4%EC%A7%80%EB%8A%94%20elasticsearch%20%EC%9A%B4%EC%98%81%20%EB%85%B8%ED%95%98%EC%9A%B0&lt;/a&gt;&lt;/p&gt;</description>
      <category>Big Data</category>
      <category>allocation</category>
      <category>box_type</category>
      <category>elasticsearch</category>
      <category>대용량 데이터</category>
      <category>엘라스틱서치</category>
      <author>유주원</author>
      <guid isPermaLink="true">https://yujuwon.tistory.com/453</guid>
      <comments>https://yujuwon.tistory.com/entry/ELASTICSEARCH-HOTDATA%EC%99%80-WARMDATA-%EA%B5%AC%EC%84%B1%ED%95%98%EA%B8%B0#entry453comment</comments>
      <pubDate>Tue, 9 Sep 2025 12:47:16 +0900</pubDate>
    </item>
  </channel>
</rss>