<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>아그거뭐였지?</title>
    <link>https://wave35.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Tue, 2 Jun 2026 08:56:30 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>wave35</managingEditor>
    <image>
      <title>아그거뭐였지?</title>
      <url>https://tistory1.daumcdn.net/tistory/6182847/attach/185e0976e14249f68e92260e2b9d216d</url>
      <link>https://wave35.tistory.com</link>
    </image>
    <item>
      <title>mongoDB - Time-Series Collection 와 Aggregation Pipeline</title>
      <link>https://wave35.tistory.com/222</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;&amp;nbsp;&lt;br /&gt;[ Time-Series Collection ]&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 개요&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MongoDB 5.0부터 도입된 시계열 전용 컬렉션 타입입니다. &lt;br /&gt;&amp;ldquo;같은 시계열(센서&amp;middot;채널 등)에 속하는 여러 측정값을&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lsquo;버킷(bucket)&amp;rsquo;이라는 큰 문서에 압축&amp;middot;열 지향 포맷으로 묶어서 보관해&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰기&amp;middot;저장&amp;middot;조회 효율을 극대화한 컬렉션&amp;rdquo; 입니다.&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;timeField : 시간 값(ISODate)&lt;/li&gt;
&lt;li&gt;metaField : 동일 시계열을 구분하는 태그(ID&amp;middot;채널&amp;middot;센서 등, 선택)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;추가 옵션&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;granularity : 데이터 입력 주기 힌트(&quot;seconds&quot;, &quot;minutes&quot;, &quot;hours&quot;)&lt;/li&gt;
&lt;li&gt;TTL&amp;middot;샤딩도 그대로 사용 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 간단한 구조 살펴보기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 시나리오 : YouTube 라이브 채널에서 1초마다 &amp;ldquo;동시시청자(viewers)&amp;rdquo; 수치를 수집&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 컬렉션 생성&lt;/h4&gt;
&lt;pre id=&quot;code_1753882880578&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.createCollection(&quot;channelViewers&quot;, {
  timeseries: {
    timeField : &quot;ts&quot;,          // 1초 주기의 타임스탬프
    metaField : &quot;channelId&quot;,   // 채널 ID별로 시계열 구분
    granularity: &quot;seconds&quot;
  },
  expireAfterSeconds: 60*60*24*30   // 30일 보존(선택)
})&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2) 데이터 삽입&lt;/h4&gt;
&lt;pre id=&quot;code_1753882892781&quot; class=&quot;csharp&quot; data-ke-language=&quot;csharp&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.channelViewers.insertMany([
  { channelId: &quot;LIVE_abc&quot;, ts: ISODate(&quot;2024-07-30T10:15:04Z&quot;), viewers: 731 },
  { channelId: &quot;LIVE_abc&quot;, ts: ISODate(&quot;2024-07-30T10:15:05Z&quot;), viewers: 742 },
  { channelId: &quot;LIVE_xyz&quot;, ts: ISODate(&quot;2024-07-30T10:15:05Z&quot;), viewers: 189 }
])&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동일 channelId(metaField)에 해당하는 레코드들은 내부적으로 같은 버킷에 차곡차곡 기록됩니다.&lt;/li&gt;
&lt;li&gt;_id 인덱스를 만들지 않으므로 쓰기 오버헤드가 작습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3) 기본 조회 (특정 채널 1분치 찾기)&lt;/h4&gt;
&lt;pre id=&quot;code_1753882944933&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.channelViewers.find({
  channelId: &quot;LIVE_abc&quot;,
  ts: { $gte: ISODate(&quot;2024-07-30T10:15:00Z&quot;),
        $lt : ISODate(&quot;2024-07-30T10:16:00Z&quot;) }
})&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MongoDB는 버킷 메타데이터를 먼저 찾아 필요한 버킷만 스캔합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 집계( Aggregation Pipeline ) 예제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;[목표] 유튜브 채널별 1분 평균 시청자수를 구해보자.&lt;/p&gt;
&lt;pre id=&quot;code_1753882992040&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.channelViewers.aggregate([
  // ① 5분 범위로 한정(옵션)
  { $match: {
      ts: { $gte: ISODate(&quot;2024-07-30T10:15:00Z&quot;),
            $lt : ISODate(&quot;2024-07-30T10:20:00Z&quot;) }
  }},

  // ② 1분 단위로 버킷 키 만들기
  { $group: {
      _id: {
        channelId: &quot;$channelId&quot;,
        minute: { $dateTrunc: { date: &quot;$ts&quot;, unit: &quot;minute&quot; } }
      },
      avgViewers: { $avg: &quot;$viewers&quot; }
  }},

  { $sort: { &quot;_id.channelId&quot;: 1, &quot;_id.minute&quot;: 1 } }
])&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;버킷(열 지향) 저장 덕분에 $match 와 $group 단계에서 버킷 단위 블록 프로세싱이 적용되어 CPU&amp;middot;디스크 I/O가 매우 적음.&lt;/li&gt;
&lt;li&gt;channelId 로 샤딩하면 채널별 스케일-아웃도 간단.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. TTL&amp;middot;아카이빙: 오래된 데이터 자동 삭제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞서 설정한 expireAfterSeconds 옵션 = TTL 인덱스 역할&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 30일이 지난 버킷은 백그라운드에서 자동 삭제되어 스토리지를 깔끔하게 유지.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. Materialized View(요약 컬렉션)로 선집계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자주 쓰는 1분 요약을 미리 저장하면 실시간 대시보드가 훨씬 빨라집니다.&lt;/p&gt;
&lt;pre id=&quot;code_1753883072819&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.channelViewers.aggregate([
  { $match: { ts: { $gte: /*당일 1분 전*/ } } },
  { $group: {
      _id: {
        channelId: &quot;$channelId&quot;,
        minute: { $dateTrunc: { date: &quot;$ts&quot;, unit: &quot;minute&quot; } }
      },
      avgViewers: { $avg: &quot;$viewers&quot; }
  }},
  { $merge: {
      into: &quot;channelViewers_1min&quot;,
      on:   [&quot;_id.channelId&quot;, &quot;_id.minute&quot;],
      whenMatched: &quot;replace&quot;,
      whenNotMatched: &quot;insert&quot;
  }}
])&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이 파이프라인을 1분마다 크론/Trigger로 실행 &amp;rarr; channelViewers_1min 컬렉션에 upsert&lt;/li&gt;
&lt;li&gt;대시보드는 요약 컬렉션만 조회하면 되므로 응답이 몇 ms 수준&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. 요약&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. createCollection() 시 timeseries 옵션만 지정하면 시계열 특화 저장 구조가 자동 적용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 동일 시계열(채널&amp;middot;센서 등) 데이터가 한 버킷-문서에 모여 쓰기&amp;middot;저장&amp;middot;조회가 모두 빨라짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. Aggregation Pipeline, 윈도 함수, $merge 를 이용해&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;초 &amp;rarr; 분 &amp;rarr; 시 단위 집계&lt;/li&gt;
&lt;li&gt;누적합&amp;middot;랭킹&amp;middot;롤링 평균&lt;/li&gt;
&lt;li&gt;Materialized View 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;등을&amp;nbsp;별도&amp;nbsp;데이터베이스&amp;nbsp;없이&amp;nbsp;몽고&amp;nbsp;내부에서&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ Aggregation Pipeline ]&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MongoDB 서버 내부에서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;데이터를 흐름(Stream)처럼 흘려보내며, 여러 단계(stage)로 변환&amp;middot;필터&amp;middot;집계&amp;rdquo;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하도록 설계된 데이터 가공 엔진입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL의 SELECT &amp;hellip; GROUP BY &amp;hellip; HAVING &amp;hellip; 을 MongoDB 한곳에서 구현한다고 보면 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 핵심 개념&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파이프라인은 배열 형태로, 각 요소가 하나의 stage - [ { $stage1: {...} }, { $stage2: {...} }, &amp;hellip; ]&lt;/li&gt;
&lt;li&gt;앞 단계의 출력이 바로 다음 단계의 입력이 되어 스트림처럼 흐름을 이룹니다.&lt;/li&gt;
&lt;li&gt;모든 stage는 서버 메모리에서 실행되므로 애플리케이션이 데이터를 꺼내서 따로 가공할 필요가 없습니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 대표 Stage와 용도&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Stage | 설명 (SQL에 비유)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;$match | WHERE 절 : 조건 필터, 인덱스 사용&lt;/li&gt;
&lt;li&gt;$project | SELECT 절 : 필드 선택&amp;middot;변환&lt;/li&gt;
&lt;li&gt;$group | GROUP BY : 키별 집계(sum, avg &amp;hellip;)&lt;/li&gt;
&lt;li&gt;$sort | ORDER BY&lt;/li&gt;
&lt;li&gt;$unwind | 배열을 행으로 펼치기&lt;/li&gt;
&lt;li&gt;$bucket/$bucketAuto | 구간 히스토그램&lt;/li&gt;
&lt;li&gt;$lookup | JOIN (다른 컬렉션 결합)&lt;/li&gt;
&lt;li&gt;$setWindowFields | 윈도 함수(누적합, 이동평균 등)&lt;/li&gt;
&lt;li&gt;$merge | 결과를 컬렉션에 upsert &amp;rArr; Materialized View 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 예제 시나리오&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가상의&amp;nbsp;컬렉션&amp;nbsp;userActions&lt;/p&gt;
&lt;pre id=&quot;code_1753883385900&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  channelId : &quot;LIVE_abc&quot;,
  ts        : ISODate(&quot;2024-07-30T14:23:05Z&quot;),
  userId    : &quot;U_123&quot;,
  action    : &quot;donate&quot;,          // like | subscribe | donate
  amount    : NumberDecimal(&quot;5&quot;)
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목표:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 채널별&amp;middot;1시간 단위 기부 합계 / 건수&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2) &amp;ldquo;기부액 누적합&amp;rdquo; 그래프용 데이터&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3)&amp;nbsp;위&amp;nbsp;결과를&amp;nbsp;요약&amp;nbsp;컬렉션에&amp;nbsp;upsert&lt;/p&gt;
&lt;pre id=&quot;code_1753883566594&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;db.userActions.aggregate([

  /* ① 관심 기간&amp;middot;액션만 선별 ------------ */
  { $match: {
      action: &quot;donate&quot;,                                   // 필터
      ts: { $gte: ISODate(&quot;2024-07-30T00:00:00Z&quot;) }       // 인덱스 사용
  }},

  /* ② 키 생성: 채널 + 1시간 ------------ */
  { $group: {                                             // GROUP BY
      _id: {
        channelId: &quot;$channelId&quot;,
        hour: { $dateTrunc: { date: &quot;$ts&quot;, unit: &quot;hour&quot; } }
      },
      totalDonation : { $sum: &quot;$amount&quot; },                // SUM(amount)
      donationCnt   : { $sum: 1 }                         // COUNT(*)
  }},

  /* ③ 정렬 ---------------------------- */
  { $sort: { &quot;_id.channelId&quot;: 1, &quot;_id.hour&quot;: 1 } },

  /* ④ 결과를 요약 컬렉션에 upsert ------ */
  { $merge: {
      into: &quot;donationHourlySummary&quot;,                      // 대상 테이블
      on:   [&quot;_id.channelId&quot;, &quot;_id.hour&quot;],                // PK
      whenMatched: &quot;replace&quot;,
      whenNotMatched: &quot;insert&quot;
  }}

])&lt;/code&gt;&lt;/pre&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>Storage/MongoDB</category>
      <category>mongodb</category>
      <author>wave35</author>
      <guid isPermaLink="true">https://wave35.tistory.com/222</guid>
      <comments>https://wave35.tistory.com/222#entry222comment</comments>
      <pubDate>Wed, 30 Jul 2025 22:54:28 +0900</pubDate>
    </item>
    <item>
      <title>Kafka Connect - 7장 Rest API 활용하여 관리</title>
      <link>https://wave35.tistory.com/220</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 REST API는 8083 포트에서 사용할 수 있으며 보안되지 않습니다. ( 인증 기능 추가 가능 )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST API는 모든 요청 본문이 콘텐츠 유형 application/json 을 사용할 것으로 예상하며&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;커넥터 생성 및 삭제하기&lt;/li&gt;
&lt;li&gt;커넥터 구성&lt;/li&gt;
&lt;li&gt;커넥터의 수명 주기 제어&lt;/li&gt;
&lt;li&gt;커넥터 오프셋 나열&lt;/li&gt;
&lt;li&gt;문제 디버깅&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;커넥터 생성 및 삭제&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클러스터의 버전을 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1751288967101&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl localhost:8083
{
  &quot;version&quot;: &quot;3.5.0&quot;,
  &quot;commit&quot;: &quot;c97b88d5db4de28d&quot;,
  &quot;kafka_cluster_id&quot;: &quot;PSCn87RpRoqhfjAs9KYtuw&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;사용할 수 있는 커넥터 플러그인 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 여기에는 Kafka Connect 클러스터에 설치된 소스 및 싱크 커넥터 플러그인만 나열됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1751289002505&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl localhost:8083/connector-plugins
[{
    &quot;class&quot;: &quot;org.apache.kafka.connect.mirror.MirrorCheckpointConnector&quot;,
    &quot;type&quot;: &quot;source&quot;,
    &quot;version&quot;: &quot;3.5.0&quot;
}, {
    &quot;class&quot;: &quot;org.apache.kafka.connect.mirror.MirrorHeartbeatConnector&quot;,
    &quot;type&quot;: &quot;source&quot;,
    &quot;version&quot;: &quot;3.5.0&quot;
}, {
    &quot;class&quot;: &quot;org.apache.kafka.connect.mirror.MirrorSourceConnector&quot;,
    &quot;type&quot;: &quot;source&quot;,
    &quot;version&quot;: &quot;3.5.0&quot;
}]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실행 중인 커넥터 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1751289128483&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl localhost:8083/connectors
[]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;커넥터 생성&lt;/h3&gt;
&lt;pre id=&quot;code_1751289190082&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 예: PUT 요청:
$ curl -X PUT -H &quot;Content-Type: application/json&quot; \
  -d &quot;@sink-config.json&quot; \
  localhost:8083/connectors/file-sink/config 
{
  &quot;name&quot;:&quot;file-sink&quot;,
  &quot;config&quot;:{
    &quot;connector.class&quot;:&quot;org.apache.kafka.connect.file.FileStreamSinkConnector&quot;,
    &quot;tasks.max&quot;:&quot;1&quot;,
    &quot;topics&quot;:&quot;topic-to-export&quot;,
    &quot;file&quot;:&quot;/tmp/sink.out&quot;,
    &quot;value.converter&quot;:&quot;org.apache.kafka.connect.storage.StringConverter&quot;,
    &quot;name&quot;:&quot;file-sink&quot;
  },
  &quot;tasks&quot;:[],&quot;type&quot;:&quot;sink&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sink-config.json 파일&lt;/p&gt;
&lt;pre id=&quot;code_1751289206808&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;connector.class&quot;:&quot;org.apache.kafka.connect.file.FileStreamSinkConnector&quot;,
  &quot;tasks.max&quot;: &quot;1&quot;,
  &quot;topics&quot;: &quot;topic-to-export&quot;,
  &quot;file&quot;: &quot;/tmp/sink.out&quot;,
  &quot;value.converter&quot;: &quot;org.apache.kafka.connect.storage.StringConverter&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성 확인&lt;/p&gt;
&lt;pre id=&quot;code_1751289260562&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl localhost:8083/connectors
[&quot;file-sink&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;쿼리 매개변수를 통해 자세히 확인&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;GET /connectors?expand=info 을 호출하면 구성 및 모든 작업 등이 나열&lt;/li&gt;
&lt;li&gt;GET /connectors?expand=status 은 커넥터의 상태 및 관련 작업을 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1751289344672&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl &quot;localhost:8083/connectors?expand=status&amp;amp;expand=info&quot;
{
  &quot;file-sink&quot;: {
    &quot;status&quot;: {
      &quot;name&quot;: &quot;file-sink&quot;,
      &quot;connector&quot;: {
        &quot;state&quot;: &quot;RUNNING&quot;,
        &quot;worker_id&quot;: &quot;192.168.1.110:8083&quot;
      },
      &quot;tasks&quot;: [
        {
          &quot;id&quot;: 0,
          &quot;state&quot;: &quot;RUNNING&quot;,
          &quot;worker_id&quot;: &quot;192.168.1.110:8083&quot;
        }
      ],
      &quot;type&quot;: &quot;sink&quot;
    },
    &quot;info&quot;: {
      &quot;name&quot;: &quot;file-sink&quot;,
      &quot;config&quot;: {
        &quot;connector.class&quot;: 
          &quot;org.apache.kafka.connect.file.FileStreamSinkConnector&quot;,
        &quot;file&quot;: &quot;/tmp/sink.out&quot;,
        &quot;tasks.max&quot;: &quot;1&quot;,
        &quot;topics&quot;: &quot;topic-to-export&quot;,
        &quot;name&quot;: &quot;file-sink&quot;,
        &quot;value.converter&quot;: &quot;org.apache.kafka.connect.storage.StringConverter&quot;
      },
      &quot;tasks&quot;: [
        {
          &quot;connector&quot;: &quot;file-sink&quot;,
          &quot;task&quot;: 0
        }
      ],
      &quot;type&quot;: &quot;sink&quot;
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt;특정 커넥터 상태 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1751289392113&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET /connectors/&amp;lt;CONNECTOR&amp;gt;/status&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;특정 커넥터 태스크 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1751289415838&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET /connectors/&amp;lt;CONNECTOR&amp;gt;/tasks&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;커넥터와 연동되는 토픽 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka&amp;nbsp;Connect&amp;nbsp;파이프라인의&amp;nbsp;관리자는&amp;nbsp;특정&amp;nbsp;커넥터가&amp;nbsp;어떤&amp;nbsp;토픽과&amp;nbsp;상호&amp;nbsp;작용했는지&amp;nbsp;파악하는&amp;nbsp;것이&amp;nbsp;유용할&amp;nbsp;수&amp;nbsp;있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 돕기 위해 Kafka Connect는 커넥터가 상호 작용한 모든 토픽을 검색할 수 있는 메커니즘을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 방금 만든 file-sink 커넥터의 경우, 이 커넥터가 상호작용한 단일 토픽을 볼 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1751289524114&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl localhost:8083/connectors/file-sink/topics
{
  &quot;file-sink&quot;: {
    &quot;topics&quot;: [
      &quot;topic-to-export&quot;
    ]
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;커넥터와 토픽 연동 해제&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka Connect가 이전 상호 작용을 '잊어버리도록' 할 수 있습니다:&lt;/p&gt;
&lt;pre id=&quot;code_1751289615745&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl localhost:8083/connectors/file-sink/topics
{
  &quot;file-sink&quot;: {
    &quot;topics&quot;: [
      &quot;topic-to-export&quot;
    ]
  }
}&lt;/code&gt;&lt;/pre&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;pre id=&quot;code_1751289636869&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl localhost:8083/connectors/file-sink/topics
{
  &quot;file-sink&quot;: {
    &quot;topics&quot;: []
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;커넥터 삭제&lt;/h3&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;pre id=&quot;code_1751289663014&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl -X DELETE localhost:8083/connectors/file-sink&lt;/code&gt;&lt;/pre&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;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;커넥터 및 작업 구성&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;플러그인 설정 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST API는 특정 플러그인의 구성 설정을 나열하고 검증하는 데 도움이 되는 옵션을 제공합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 다음과 같이 FileStreamSink​Con⁠nector 에 대한 구성 설정 목록을 얻을 수 있습니다:&lt;/p&gt;
&lt;pre id=&quot;code_1751290226269&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl localhost:8083/connector-plugins/org.apache.kafka.connect.file.FileStreamS
inkConnector/config
[
  {
    &quot;name&quot;: &quot;file&quot;, 1
    &quot;type&quot;: &quot;STRING&quot;, 2
    &quot;required&quot;: false, 3
    &quot;default_value&quot;: null, 4
    &quot;importance&quot;: &quot;HIGH&quot;, 5
    &quot;documentation&quot;: &quot;Destination filename. If not specified, the standard output 
 will be used&quot;, 6
    &quot;group&quot;: null, 7
    &quot;width&quot;: &quot;NONE&quot;, 8
    &quot;display_name&quot;: &quot;file&quot;, 9
    &quot;dependents&quot;: [], 10
    &quot;order&quot;: -1 11
  }
]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 - 이 구성 설정의 이름입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2 - 구성 값의 예상 유형, BOOLEAN, STRING, INT, SHORT, LONG, DOUBLE, LIST, CLASS, PASSWORD 중 하나입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3 - 이 구성 값이 필요한지 여부를 나타냅니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4 - 기본값, required 이 true 인 경우 기본값은 null 입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5 - 구성 설정의 중요도 수준입니다. HIGH , MEDIUM, LOW 중 하나입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6 - 구성 설정에 대한 정보입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7 - 이&amp;nbsp;구성&amp;nbsp;설정이&amp;nbsp;어느&amp;nbsp;group&amp;nbsp;에&amp;nbsp;속하는지를&amp;nbsp;나타냅니다.&amp;nbsp;플러그인은&amp;nbsp;자체&amp;nbsp;설정에&amp;nbsp;대해&amp;nbsp;자체&amp;nbsp;그룹을&amp;nbsp;도입할&amp;nbsp;수&amp;nbsp;있습니다.&lt;br /&gt;8 - 구성&amp;nbsp;설정의&amp;nbsp;width.&amp;nbsp;NONE&amp;nbsp;,&amp;nbsp;SHORT,&amp;nbsp;MEDIUM,&amp;nbsp;LONG&amp;nbsp;중&amp;nbsp;하나.&lt;br /&gt;9 - 구성&amp;nbsp;설정의&amp;nbsp;표시&amp;nbsp;이름(이름과&amp;nbsp;일치할&amp;nbsp;수&amp;nbsp;있음)입니다.&lt;br /&gt;10 - 이&amp;nbsp;설정에&amp;nbsp;의존하는&amp;nbsp;다른&amp;nbsp;구성&amp;nbsp;설정&amp;nbsp;목록입니다.&lt;br /&gt;11 - 구성 값의 정수 순서 번호(설정하지 않은 경우 -1 )입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;플러그인 설정 유효성 검사&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용할 구성을 결정한 후에는 PUT 요청을 보내 유효성을 검사할 수 있습니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;구성에서 커넥터 이름이 누락된 경우 FileStreamSinkConnector 의 예를 살펴보겠습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1751290360423&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl -X PUT -H &quot;Content-Type: application/json&quot; \
  -d '{&quot;connector.class&quot;: &quot;org.apache.kafka.connect.file.FileStreamSinkConnector&quot;
, &quot;tasks.max&quot;: &quot;1&quot;, &quot;topics&quot;: &quot;sink-topic&quot;}' \
  localhost:8083/connector-plugins/org.apache.kafka.connect.file.FileStreamSinkCo
nnector/config/validate 
{
  &quot;name&quot;: &quot;org.apache.kafka.connect.file.FileStreamSinkConnector&quot;, 1
  &quot;error_count&quot;: 1, 2
  &quot;groups&quot;: [  3
    &quot;Common&quot;,
    &quot;Transforms&quot;,
    &quot;Predicates&quot;,
    &quot;Error Handling&quot;
  ],
  &quot;configs&quot;: [
    {
      &quot;definition&quot;: {  4
        &quot;name&quot;: &quot;name&quot;,
        &quot;type&quot;: &quot;STRING&quot;,
        &quot;required&quot;: true,
        &quot;default_value&quot;: null,
        &quot;importance&quot;: &quot;HIGH&quot;,
        &quot;documentation&quot;: &quot;Globally unique name to use...&quot;,
        &quot;group&quot;: &quot;Common&quot;,
        &quot;width&quot;: &quot;MEDIUM&quot;,
        &quot;display_name&quot;: &quot;Connector name&quot;,
        &quot;dependents&quot;: [],
        &quot;order&quot;: 1
      },
      &quot;value&quot;: {
        &quot;name&quot;: &quot;name&quot;, 5
        &quot;value&quot;: null,  6
        &quot;recommended_values&quot;: [],  7
        &quot;errors&quot;: [  8
          &quot;Missing required configuration \&quot;name\&quot; which has no default value.&quot;
        ],
        &quot;visible&quot;: true  9
      }
    },
 ...
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 - 플러그인을 제공하는 클래스의 이름입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2 - 제공된 구성의 유효성을 검사하는 동안 발견된 오류의 수입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3 - 구성 설정에 있는 그룹이 반환됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4 - 이 구성 설정의 정의입니다. 엔드포인트의 출력과 일치합니다. /connector-plugins/&amp;lt;CONNECTOR_PLUGIN&amp;gt;/config 엔드포인트의 출력과 일치합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5 - 이 구성 설정의 이름입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;6 - 구성 설정에 제공된 값(제공되지 않은 경우 null )입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7 - 제공된 다른 구성 값을 고려할 때 구성 설정에 유효한 값입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;8 - 오류가 없는 경우 빈 배열, 또는 이 값이 구성 설정에 허용되지 않는 이유에 대한 오류 메시지 배열입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;9 - 이 구성 값을 나열할지 여부를 나타냅니다.&lt;br /&gt;&lt;br /&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;h2 data-ke-size=&quot;size26&quot;&gt;커넥터의 수명 주기 제어&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실패 커넥터 재실행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특정 커넥터에서 실패한 모든 작업을 다시 시작할 수 있습니다&lt;/p&gt;
&lt;pre id=&quot;code_1751290558029&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl -X POST &quot;localhost:8083/connectors/file-source/restart?includeTasks=true&amp;amp;onlyFailed=true&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;커넥터 일시중지(PAUSED) 와 중지(STOPPED)&lt;/h3&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;외부 시스템에 과부하가 걸리는 경우, config 업데이트를 적용할 때 그 시간 동안 트래픽을 원하지 않는 경우 등에 사용할 수 있습니다.&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;PAUSED 와 STOPPED 의 차이점은&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;STOPPED 상태에서는 모든 작업이 종료되어 리소스를 사용하지 않는다는 것입니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, STOPPED 상태보다 PAUSED 상태에서 커넥터가 다시 시작되는 데 걸리는 시간이 더 짧습니다.&lt;br /&gt;&lt;br /&gt;실행 중인 커넥터를 일시 중지&lt;/p&gt;
&lt;pre id=&quot;code_1751290717422&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl -X PUT localhost:8083/connectors/file-sink/pause&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 상태 엔드포인트에 커넥터와 모든 작업이 PAUSED 상태로 표시됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1751290727864&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl localhost:8083/connectors/file-sink/status
{
  &quot;name&quot;: &quot;file-sink&quot;,
  &quot;connector&quot;: {
    &quot;state&quot;: &quot;PAUSED&quot;,
    &quot;worker_id&quot;: &quot;192.168.1.110:8083&quot;
  },
  &quot;tasks&quot;: [
    {
      &quot;id&quot;: 0,
      &quot;state&quot;: &quot;PAUSED&quot;,
      &quot;worker_id&quot;: &quot;192.168.1.110:8083&quot;
    }
  ],
  &quot;type&quot;: &quot;sink&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;커넥터를 다시 시작하려면 비슷한 명령을 사용하되 pause 대신 resume 를 사용합니다.&lt;/p&gt;
&lt;pre id=&quot;code_1751290741318&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl -X PUT localhost:8083/connectors/file-sink/resume&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커넥터 및 작업의 상태가 RUNNING 상태로 돌아갑니다.&lt;br /&gt;&lt;br /&gt;커넥터를 완전히 중지&lt;/p&gt;
&lt;pre id=&quot;code_1751290771668&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl -X PUT localhost:8083/connectors/file-sink/stop&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 상태 엔드포인트에 커넥터가 STOPPED 상태로 표시됩니다.&lt;/p&gt;
&lt;pre id=&quot;code_1751290784728&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl localhost:8083/connectors/file-sink/status
{
  &quot;name&quot;: &quot;file-sink&quot;,
  &quot;connector&quot;: {
    &quot;state&quot;: &quot;STOPPED&quot;,
    &quot;worker_id&quot;: &quot;192.168.1.110:8083&quot;
  },
  &quot;tasks&quot;: [],
  &quot;type&quot;: &quot;sink&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 중지되면 PUT /connectors/&amp;lt;CONNECTOR&amp;gt;/resume 엔드포인트를 사용하여 RUNNING 상태로 돌아갑니다.&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;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;커넥터 오프셋 나열&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;특정 커넥터에 대한 오프셋을 나열 ( 소스 커넥터 )&lt;/h3&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;pre id=&quot;code_1751290933817&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl localhost:8083/connectors/file-sink/offsets
{
  &quot;offsets&quot;: [
    {
      &quot;partition&quot;: {
        &quot;kafka_partition&quot;: 0,  1
        &quot;kafka_topic&quot;: &quot;topic-to-export&quot;  2
      },
      &quot;offset&quot;: {
        &quot;kafka_offset&quot;: 3  3
      }
    }
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1 - 이&amp;nbsp;예제에서는&amp;nbsp;커넥터에&amp;nbsp;단일&amp;nbsp;파티션인&amp;nbsp;0&amp;nbsp;이&amp;nbsp;있습니다.&lt;br /&gt;2 - 커넥터가&amp;nbsp;topic-to-export&amp;nbsp;토픽의&amp;nbsp;레코드를&amp;nbsp;소비하고&amp;nbsp;있습니다.&lt;br /&gt;3 - 커넥터가 오프셋까지 소모되었습니다 3.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size23&quot;&gt;특정 커넥터에 대한 오프셋을 나열 ( 싱크 커넥터 )&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소스 커넥터의 출력은 약간 다르며 특정 커넥터에 따라 다릅니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 FileStreamSource 커넥터를 실행하면 다음과 같은 내용이 표시될 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1751291011655&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl localhost:8083/connectors/file-source/offsets
{
  &quot;offsets&quot;: [
    {
      &quot;partition&quot;: {
        &quot;filename&quot;: &quot;/tmp/source.txt&quot;
      },
      &quot;offset&quot;: {
        &quot;position&quot;: 41
      }
    }
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;offsets, partition, offset 키는 모든 소스 커넥터에 공통이지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;partition 및 offset JSON 객체 내부의 내용은 개별 커넥터에 의해 결정됩니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;FileStreamSource 커넥터는 파티션을 {&quot;filename&quot;: &quot;/path/to/file&quot;} 으로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오프셋을 {&quot;position&quot;: &amp;lt;BYTES&amp;gt;} 으로 저장하며, 여기서 &amp;lt;BYTES&amp;gt; 은 파일에서 읽은 바이트 수입니다.&lt;br /&gt;&lt;br /&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;h2 data-ke-size=&quot;size26&quot;&gt;문제 디버깅&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka Connect의 문제를 조사하고 디버깅할 때는 런타임 또는 커넥터의 로그를 사용하는 것이 중요합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka Connect는 관리자가 런타임에 logger 수준을 보고 업데이트할 수 있도록&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/admin 아래에 몇 개의 엔드포인트를 노출합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;현재 logger 레벨 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1751291248741&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl localhost:8083/admin/loggers
{
  &quot;org.apache.zookeeper&quot;: {
    &quot;level&quot;: &quot;ERROR&quot;
  },
  &quot;org.reflections&quot;: {
    &quot;level&quot;: &quot;ERROR&quot;
  },
  &quot;root&quot;: {
    &quot;level&quot;: &quot;INFO&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;logger 레벨 변경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 낮은 수준부터 가장 자세한 수준까지 유효한 로그 수준은 FATAL, ERROR, WARN, INFO, DEBUG, TRACE 입니다.&lt;/p&gt;
&lt;pre id=&quot;code_1751291308941&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl -X PUT -H &quot;Content-Type: application/json&quot; \
  -d '{&quot;level&quot;: &quot;DEBUG&quot;}' \
  localhost:8083/admin/loggers/org.apache.kafka.connect.mirror
[
  &quot;org.apache.kafka.connect.mirror&quot;,
  &quot;org.apache.kafka.connect.mirror.MirrorCheckpointConnector&quot;,
  &quot;org.apache.kafka.connect.mirror.MirrorSourceConnector&quot;
]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;logger를 다시 나열하면 방금 추가한 새 로거를 볼 수 있습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1751291417495&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl localhost:8083/admin/loggers
{
  &quot;org.apache.kafka.connect.mirror&quot;: {
    &quot;level&quot;: &quot;DEBUG&quot;
  },
  &quot;org.apache.kafka.connect.mirror.MirrorCheckpointConnector&quot;: {
    &quot;level&quot;: &quot;DEBUG&quot;
  },
  &quot;org.apache.kafka.connect.mirror.MirrorSourceConnector&quot;: {
    &quot;level&quot;: &quot;DEBUG&quot;
  },
  &quot;org.apache.zookeeper&quot;: {
    &quot;level&quot;: &quot;ERROR&quot;
  },
  &quot;org.reflections&quot;: {
    &quot;level&quot;: &quot;ERROR&quot;
  },
  &quot;root&quot;: {
    &quot;level&quot;: &quot;INFO&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 디버깅한 후에는 로그 수준을 INFO 로 다시 변경해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇지 않으면 로그가 이러한 메시지로 희석되어 다른 커넥터의 문제를 진단하기 어려울 수 있습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가능하면 root 로그 레벨을 변경하지 말고 항상 보다 구체적인 로거를 구성하는 것이 좋습니다.&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>DataPipeline/Kafka</category>
      <category>Kafka</category>
      <category>Kafka Connect</category>
      <author>wave35</author>
      <guid isPermaLink="true">https://wave35.tistory.com/220</guid>
      <comments>https://wave35.tistory.com/220#entry220comment</comments>
      <pubDate>Mon, 30 Jun 2025 17:58:17 +0900</pubDate>
    </item>
    <item>
      <title>Kafka Connect - 4장 효과적인 데이터 파이프라인 설계</title>
      <link>https://wave35.tistory.com/219</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터 변환&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 파이프라인을 통해 데이터가 흐를 때 두가지 패턴을 사용한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;ETL: 저장 공간이 제한된 시스템&lt;/li&gt;
&lt;li&gt;ELT:&amp;nbsp;오랫동안 데이터가 원본을 유지하여 다른 목적으로 재사용 용이&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카프카 커넥트는 이동중에 데이터를 변환할 수 있는 트랜스포메이션이 있으며 이는 ETL에 적합하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 민감한 개인정보를 숨김 처리하는 카프카커넥트 예시이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-06-30 오후 3.27.24.png&quot; data-origin-width=&quot;2422&quot; data-origin-height=&quot;728&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/K6enb/btsOXmUItOt/FTwr08b4nkj3hwhrRt5S41/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/K6enb/btsOXmUItOt/FTwr08b4nkj3hwhrRt5S41/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/K6enb/btsOXmUItOt/FTwr08b4nkj3hwhrRt5S41/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FK6enb%2FbtsOXmUItOt%2FFTwr08b4nkj3hwhrRt5S41%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;700&quot; height=&quot;210&quot; data-filename=&quot;스크린샷 2025-06-30 오후 3.27.24.png&quot; data-origin-width=&quot;2422&quot; data-origin-height=&quot;728&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;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;시스템간 데이터 맵핑&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터파이프라인을 구축할 때 서로 시스템간의 데이터를 맵핑을 꼼꼼히 따져봐야하며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해 &lt;b&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&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-06-30 오후 3.27.38.png&quot; data-origin-width=&quot;2392&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VKzJq/btsOWpjNfhi/kpFSVQbiuBncr3JQEA6t9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VKzJq/btsOWpjNfhi/kpFSVQbiuBncr3JQEA6t9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VKzJq/btsOWpjNfhi/kpFSVQbiuBncr3JQEA6t9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVKzJq%2FbtsOWpjNfhi%2FkpFSVQbiuBncr3JQEA6t9k%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;700&quot; height=&quot;112&quot; data-filename=&quot;스크린샷 2025-06-30 오후 3.27.38.png&quot; data-origin-width=&quot;2392&quot; data-origin-height=&quot;384&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;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-06-30 오후 3.27.47.png&quot; data-origin-width=&quot;2324&quot; data-origin-height=&quot;574&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sQLdt/btsOYOh8gle/8pDJGUjp43dzs3J1M5izq0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sQLdt/btsOYOh8gle/8pDJGUjp43dzs3J1M5izq0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sQLdt/btsOYOh8gle/8pDJGUjp43dzs3J1M5izq0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsQLdt%2FbtsOYOh8gle%2F8pDJGUjp43dzs3J1M5izq0%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;700&quot; height=&quot;173&quot; data-filename=&quot;스크린샷 2025-06-30 오후 3.27.47.png&quot; data-origin-width=&quot;2324&quot; data-origin-height=&quot;574&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커넥터는 동일한 파티션으로 보내야하므로 키를 통해 식별한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;소스 작업은 데이터를 하나 이상의 파티션으로 전송할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-06-30 오후 3.39.57.png&quot; data-origin-width=&quot;1722&quot; data-origin-height=&quot;810&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ppGSG/btsOWDJm8oe/bp62kAyPAKFTrbuggKaWb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ppGSG/btsOWDJm8oe/bp62kAyPAKFTrbuggKaWb1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ppGSG/btsOWDJm8oe/bp62kAyPAKFTrbuggKaWb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FppGSG%2FbtsOWDJm8oe%2Fbp62kAyPAKFTrbuggKaWb1%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;600&quot; height=&quot;282&quot; data-filename=&quot;스크린샷 2025-06-30 오후 3.39.57.png&quot; data-origin-width=&quot;1722&quot; data-origin-height=&quot;810&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;/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;파티션은&amp;nbsp;특정&amp;nbsp;커넥터에&amp;nbsp;대해&amp;nbsp;하나의&amp;nbsp;싱크&amp;nbsp;작업에서만&amp;nbsp;읽을&amp;nbsp;수&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;span style=&quot;font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif; letter-spacing: 0px;&quot;&gt;3개의 파티션에서 데이터를 읽는 두 개의 싱크 작업을 보여줍니다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-06-30 오후 3.41.09.png&quot; data-origin-width=&quot;1720&quot; data-origin-height=&quot;792&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxQZWg/btsOWLAcDvO/g6xC5fQKe7ZtbWRYvJ723k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxQZWg/btsOWLAcDvO/g6xC5fQKe7ZtbWRYvJ723k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxQZWg/btsOWLAcDvO/g6xC5fQKe7ZtbWRYvJ723k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxQZWg%2FbtsOWLAcDvO%2Fg6xC5fQKe7ZtbWRYvJ723k%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;600&quot; height=&quot;276&quot; data-filename=&quot;스크린샷 2025-06-30 오후 3.41.09.png&quot; data-origin-width=&quot;1720&quot; data-origin-height=&quot;792&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;span&gt;&amp;nbsp;&lt;/span&gt;Kafka Connect 데이터 파이프라인을 설계할 때,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;tasks.max&lt;span&gt;&amp;nbsp;옵션&amp;nbsp;&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스키마&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Kafka&amp;nbsp;Connect&amp;nbsp;파이프라인은&amp;nbsp;스키마를&amp;nbsp;사용하여&amp;nbsp;Kafka에&amp;nbsp;저장된&amp;nbsp;데이터를&amp;nbsp;설명할&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;(&amp;nbsp;옵션&amp;nbsp;수정을&amp;nbsp;통해&amp;nbsp;)&lt;/p&gt;
&lt;pre id=&quot;code_1751266316853&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{&quot;schema&quot;:{&quot;type&quot;:&quot;string&quot;,&quot;optional&quot;:false},&quot;payload&quot;:&quot;This is a string&quot;}
{&quot;schema&quot;:{&quot;type&quot;:&quot;string&quot;,&quot;optional&quot;:false},&quot;payload&quot;:&quot;Another string&quot;}
{&quot;schema&quot;:{&quot;type&quot;:&quot;string&quot;,&quot;optional&quot;:false},&quot;payload&quot;:&quot;A third string&quot;}
{&quot;schema&quot;:{&quot;type&quot;:&quot;string&quot;,&quot;optional&quot;:false},&quot;payload&quot;:&quot;The final string&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 레코드에 대해 스키마 정보를 전송하면 오버헤드가 커지므로, 스키마 레지스트리를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장&amp;nbsp;일반적으로&amp;nbsp;사용되는&amp;nbsp;두&amp;nbsp;가지는&amp;nbsp;Confluent&amp;nbsp;스키마&amp;nbsp;레지스트리와&amp;nbsp;Apicurio&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;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;내부 사용 토픽&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분산 모드의 Kafka Connect는 토픽을 사용하여 상태를 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커넥터를 시작할 때 토픽이 아직 없는 경우 자동으로 생성한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구성 : config.storage.topic&lt;/li&gt;
&lt;li&gt;오프셋 : offset.storage.topic&lt;/li&gt;
&lt;li&gt;상태 : status.storage.topic&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;config.storage.topic&lt;/h4&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;h4 data-ke-size=&quot;size20&quot;&gt;offset.storage.topic&lt;/h4&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;h4 data-ke-size=&quot;size20&quot;&gt;status.storage.topic&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커넥터 및 작업의 현재 상태를 에 저장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;REST API 사용자가 쿼리하는 데이터의 중심이 되는 곳이다.&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;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1751265781309&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;Kafka Connect&quot; data-og-description=&quot;4장. 효과적인 데이터 파이프라인 설계 이 작품은 AI를 사용하여 번역되었습니다. 여러분의 피드백과 의견을 환영합니다: translation-feedback@oreilly.com 이 장에서는 Kafka Connect를 사용해 탄력적이고 &quot; data-og-host=&quot;www.oreilly.com&quot; data-og-source-url=&quot;https://www.oreilly.com/library/view/kafka-connect/9798341654662/ch04.html#each_partition_can_only_be_read_by_one&quot; data-og-url=&quot;https://www.oreilly.com/library/view/kafka-connect/9798341654662/ch04.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/b5LADO/hyZcb22dF5/S2qYLqqKwEPbdK65ReOQSk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/kBAsa/hyZgbtxoeI/YZJ02mqnghtA7KHGzpRLk1/img.png?width=1250&amp;amp;height=343&amp;amp;face=0_0_1250_343,https://scrap.kakaocdn.net/dn/c2UHuo/hyZcfdj2Hw/GjPDgEx8Vk60znmBGzwZak/img.png?width=1142&amp;amp;height=260&amp;amp;face=0_0_1142_260&quot;&gt;&lt;a href=&quot;https://www.oreilly.com/library/view/kafka-connect/9798341654662/ch04.html#each_partition_can_only_be_read_by_one&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.oreilly.com/library/view/kafka-connect/9798341654662/ch04.html#each_partition_can_only_be_read_by_one&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/b5LADO/hyZcb22dF5/S2qYLqqKwEPbdK65ReOQSk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/kBAsa/hyZgbtxoeI/YZJ02mqnghtA7KHGzpRLk1/img.png?width=1250&amp;amp;height=343&amp;amp;face=0_0_1250_343,https://scrap.kakaocdn.net/dn/c2UHuo/hyZcfdj2Hw/GjPDgEx8Vk60znmBGzwZak/img.png?width=1142&amp;amp;height=260&amp;amp;face=0_0_1142_260');&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;Kafka Connect&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;4장. 효과적인 데이터 파이프라인 설계 이 작품은 AI를 사용하여 번역되었습니다. 여러분의 피드백과 의견을 환영합니다: translation-feedback@oreilly.com 이 장에서는 Kafka Connect를 사용해 탄력적이고&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.oreilly.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;&amp;nbsp;&lt;/p&gt;</description>
      <category>DataPipeline/Kafka</category>
      <category>Kafka</category>
      <category>Kafka Connect</category>
      <author>wave35</author>
      <guid isPermaLink="true">https://wave35.tistory.com/219</guid>
      <comments>https://wave35.tistory.com/219#entry219comment</comments>
      <pubDate>Mon, 30 Jun 2025 16:04:02 +0900</pubDate>
    </item>
    <item>
      <title>Elasticsearch 바이블 - 6장 클러스터 운영</title>
      <link>https://wave35.tistory.com/218</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ 클러스터 설정 API ]&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클러스터 설정 조회&lt;/h3&gt;
&lt;pre id=&quot;code_1747088096090&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET /_cluster/settings&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;클러스터 설정 업데이트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- persistent : 클러스터를 재시작해도 유지되는 영구 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- transient : 클러스터 재시작시 초기화되는 임시 설정&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 설정 적용 우선 순위는 transient &amp;gt; persistent &amp;gt; config/elasticsearch.yml&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- persistent 설정은 모든 마스터 후보 노드의 path.data 경로 내 파일로 지정된다.&lt;/p&gt;
&lt;pre id=&quot;code_1747088173137&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PUT /_cluster/settings
{
	&quot;persistent&quot;: {
  	&quot;설정_키&quot;: &quot;설정_값&quot;
 },
 	&quot;transient&quot;: {
   	&quot;설정_키&quot;: &quot;설정_값&quot;
 	}
}&lt;/code&gt;&lt;/pre&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;h2 data-ke-size=&quot;size26&quot;&gt;[ _cat API를 통한 클러스터 관리와 모니터링 ]&lt;/h2&gt;
&lt;pre id=&quot;code_1747088571099&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET _cat/health
클러스터의 전반적인 상태를 빠르게 조회한다. (green, yellow, red 구분)

GET _cat/indices
인덱스의 종류와 상태를 조회한다.
인덱스가 몇개의 샤드로 배치되었는지 복제본, 용량 등을 확인한다.

GET _cat/nodes
각 노드의 상태를 조회한다.
노드 역할, IP, heap 사용량 등을 확인한다.

GET _cat/shards
샤드의 상태를 조회한다.
샤드에 문서가 몇개 있는지, 크기와 배치된 노드 등을 파악한다.

GET _cat/stats
클러스터의 노드, 샤드, 인덱스에 대한 통계정보를 제공한다.&lt;/code&gt;&lt;/pre&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;h2 data-ke-size=&quot;size26&quot;&gt;[ 인덱스 운영 전략 ]&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;템플릿과 명시적 매핑 활용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mapping이 자동으로 생성되는 것보단 최대한 명시적으로 mapping을 지정하는 것이 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;라우팅 활용&lt;/h3&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;h3 data-ke-size=&quot;size23&quot;&gt;시계열 인덱스 이름&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시계열 데이터를 색인한다면 인덱스 이름에도 시간 값을 넣는 것을 고려&lt;/p&gt;
&lt;pre id=&quot;code_1747229987287&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;예시) api-history-20250514&lt;/code&gt;&lt;/pre&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;h3 data-ke-size=&quot;size23&quot;&gt;alias&lt;/h3&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;다만 여러 인덱스를 가리키는 alias는 단건 문서 조회 작업이 불가능하다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;alias 생성&lt;/h4&gt;
&lt;pre id=&quot;code_1747230199963&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST _aliases
{
  &quot;actions&quot;: [
    {
      &quot;add&quot;: {
        &quot;index&quot;: &quot;logs-nginx-access-prod&quot;,
        &quot;alias&quot;: &quot;logs&quot;
      },
      &quot;add&quot;: {
        &quot;index&quot;: &quot;logs-nginx-access-prod-2&quot;,
        &quot;alias&quot;: &quot;logs&quot;
      }
    }
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;와일드 카드 사용&lt;/h4&gt;
&lt;pre id=&quot;code_1747230298194&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST _aliases
{
  &quot;actions&quot;: [
    {
      &quot;add&quot;: {
        &quot;index&quot;: &quot;logs-*&quot;,
        &quot;alias&quot;: &quot;all-logs&quot;
      }
    }
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ailas 삭제&lt;/h4&gt;
&lt;pre id=&quot;code_1747230341430&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST _aliases
{
  &quot;actions&quot;: [
    {
      &quot;remove&quot;: {
        &quot;index&quot;: &quot;logs-nginx-access-prod&quot;,
        &quot;alias&quot;: &quot;logs&quot;
      }
    }
  ]
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;롤오버&lt;/h3&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;h4 data-ke-size=&quot;size20&quot;&gt;롤오버 조건&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;max_age: 인덱스가 특정 시간 이상 됨 (예: &quot;7d&quot; - 7일)&lt;/li&gt;
&lt;li&gt;max_docs: 인덱스에 포함된 문서 수가 특정 숫자를 초과함 (예: 1,000,000)&lt;/li&gt;
&lt;li&gt;max_primary_shard_size: 프라이머리 샤드 크기가 특정 크기를 초과함 (예: &quot;50gb&quot;)&lt;/li&gt;
&lt;li&gt;max_primary_shard_docs:&amp;nbsp;프라이머리&amp;nbsp;샤드&amp;nbsp;당&amp;nbsp;문서&amp;nbsp;수가&amp;nbsp;특정&amp;nbsp;숫자를&amp;nbsp;초과함&amp;nbsp;(예:&amp;nbsp;2,000)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;롤오버 방식&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;데이터 스트림 사용&lt;/li&gt;
&lt;li&gt;인덱스 별칭 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;롤오버 요청&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;logs-alias가 가리키는 인덱스가 7일 이상이거나, 100만개 이상이거나, primary shardzmrlrk 50gb를 초과할 때&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새 인덱스(logs-000002)로 롤오버 실행&lt;/p&gt;
&lt;pre id=&quot;code_1747230798870&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST /logs-alias/_rollover/logs-000002
{
  &quot;conditions&quot;: {
    &quot;max_age&quot;: &quot;7d&quot;,
    &quot;max_docs&quot;: 1000000,
    &quot;max_primary_shard_size&quot;: &quot;50gb&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Dry Run 테스트&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;롤오버를 실제로 적용하지 않고 테스트만 실행&lt;/p&gt;
&lt;pre id=&quot;code_1747230998258&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST /logs-alias/_rollover?dry_run
{
  &quot;conditions&quot;: {
    &quot;max_age&quot;: &quot;7d&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;reindex&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;reindex는 원본 인덱스 내 문서의 _source를 읽어서 대상 인덱스에 새로 색인하는 작업이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;기본 요청&lt;/h4&gt;
&lt;pre id=&quot;code_1747259617976&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST _reindex
{
  &quot;source&quot;: {
    &quot;index&quot;: &quot;my-index-000001&quot;
  },
  &quot;dest&quot;: {
    &quot;index&quot;: &quot;my-new-index-000001&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;여러 인덱스에서 재인덱싱&lt;/h4&gt;
&lt;pre id=&quot;code_1747259641126&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST _reindex
{
  &quot;source&quot;: {
    &quot;index&quot;: [&quot;my-index-000001&quot;, &quot;my-index-000002&quot;]
  },
  &quot;dest&quot;: {
    &quot;index&quot;: &quot;my-new-index-000002&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;쿼리를 사용한 선택적 재인덱싱&lt;/h4&gt;
&lt;pre id=&quot;code_1747259682775&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST _reindex
{
  &quot;source&quot;: {
    &quot;index&quot;: &quot;my-index-000001&quot;,
    &quot;query&quot;: {
      &quot;term&quot;: {
        &quot;user.id&quot;: &quot;developer&quot;
      }
    }
  },
  &quot;dest&quot;: {
    &quot;index&quot;: &quot;my-new-index-000001&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;스냅샷과 복구&lt;/h3&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;h4 data-ke-size=&quot;size20&quot;&gt;스냅샷 저장소 등록&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;파일 시스템 저장소&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- fs는 공유 파일 시스템을 저장소로 사용할 때 지정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- location 경로는 사전에 elasticsearch.yml에 path.reop 설정으로 등록되어야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1747260244208&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PUT _snapshot/my_backup
{
  &quot;type&quot;: &quot;fs&quot;,
  &quot;settings&quot;: {
    &quot;location&quot;: &quot;/mount/backups&quot;,
    &quot;compress&quot;: true
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;Amazon S3 저장소&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747260373991&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PUT _snapshot/s3_backup
{
  &quot;type&quot;: &quot;s3&quot;,
  &quot;settings&quot;: {
    &quot;bucket&quot;: &quot;my-es-snapshots&quot;,
    &quot;region&quot;: &quot;ap-northeast-2&quot;,
    &quot;client&quot;: &quot;default&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스냅샷 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;전체 스냅샷&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747260450090&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PUT _snapshot/my_backup/snapshot_1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;특정 인덱스만 포함하는 스냅샷&lt;/b&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1747260501455&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PUT _snapshot/my_backup/snapshot_2
{
  &quot;indices&quot;: &quot;index_1,index_2*&quot;,
  &quot;ignore_unavailable&quot;: true,
  &quot;include_global_state&quot;: false
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;ignore_unavailable&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;true: 존재하는 인덱스만 처리하고 존재하지 않는 인덱스는 무시합니다.&lt;/li&gt;
&lt;li&gt;false: (기본값) 지정한 인덱스가 하나라도 존재하지 않으면 오류를 발생시키고 전체 작업이 실패&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;include_global_state&lt;/b&gt; : 스냅샷에 클러스터 전역 상태를 포함할지 여부를 결정합니다. (기본 true)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스냅샷 상태 확인&lt;/h4&gt;
&lt;pre id=&quot;code_1747260542919&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET _snapshot/my_backup/snapshot_1/_status&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스냅샷 목록 조회&lt;/h4&gt;
&lt;pre id=&quot;code_1747260551720&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET _snapshot/my_backup/_all&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스냅샷에서 인덱스 복원&lt;/h4&gt;
&lt;pre id=&quot;code_1747260589472&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST _snapshot/my_backup/snapshot_1/_restore&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스냅샷 삭제&lt;/h4&gt;
&lt;pre id=&quot;code_1747260600837&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DELETE _snapshot/my_backup/snapshot_1&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스냅샷 생명 주기 관리 (SLM)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자동화된 스냅샷 관리를 위해 스냅샷 생명 주기 관리(snapshot lifecycle management, SLM)를 사용합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SLM 정책 생성&lt;/p&gt;
&lt;pre id=&quot;code_1747261036235&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PUT _slm/policy/daily-snapshots
{
  &quot;schedule&quot;: &quot;0 30 1 * * ?&quot;, 
  &quot;name&quot;: &quot;&amp;lt;daily-snap-{now/d}&amp;gt;&quot;, 
  &quot;repository&quot;: &quot;my_backup&quot;, 
  &quot;config&quot;: { 
    &quot;indices&quot;: [&quot;*&quot;],
    &quot;ignore_unavailable&quot;: true,
    &quot;include_global_state&quot;: true
  },
  &quot;retention&quot;: { 
    &quot;expire_after&quot;: &quot;30d&quot;, 
    &quot;min_count&quot;: 5, 
    &quot;max_count&quot;: 50 
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;schedule : 스냅샷을 찍는 시간 지정 (UTC)&lt;/li&gt;
&lt;li&gt;name : 스냅샷 이름 지정&lt;/li&gt;
&lt;li&gt;repository : 스냅샷 저장소의 이름을 지정&lt;/li&gt;
&lt;li&gt;config.indices : 어떤 인덱스의 스냅샷을 찍을지 지정&lt;/li&gt;
&lt;li&gt;retention : 스냅샷 유지정책을 지정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SLM 정책 실행&lt;/p&gt;
&lt;pre id=&quot;code_1747261195736&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST _slm/policy/daily-snapshots/_execute&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SLM 정책 확인&lt;/p&gt;
&lt;pre id=&quot;code_1747261210086&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET _slm/policy/daily-snapshots&lt;/code&gt;&lt;/pre&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;h3 data-ke-size=&quot;size23&quot;&gt;인덱스 생명 주기 관리 (ILM)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Index Lifecycle Management (ILM)은 인덱스를 hot-warm-cold-frozen-delete 페이즈로 구분해서&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스가 특정 크기나 문서 수에 도달했을 때 새 인덱스로 전환(롤오버)&lt;/li&gt;
&lt;li&gt;일별, 주별, 월별로 새 인덱스를 생성하고 이전 인덱스 보관&lt;/li&gt;
&lt;li&gt;더 이상 필요하지 않은 인덱스를 삭제하여 데이터 보존 정책 적용&lt;/li&gt;
&lt;li&gt;인덱스를&amp;nbsp;사용&amp;nbsp;패턴에&amp;nbsp;따라&amp;nbsp;적합한&amp;nbsp;데이터&amp;nbsp;계층으로&amp;nbsp;이동&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;생명주기 단계&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핫(Hot) 단계&lt;/p&gt;
&lt;pre id=&quot;code_1747352335973&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;특징: 활발한 쓰기 및 조회가 이루어지는 단계

노드 배치: 성능이 가장 좋은 노드에 배치

가능한 작업:
우선순위 설정(Set Priority)
언팔로우(Unfollow)
롤오버(Rollover)
읽기 전용 설정(Read-Only)
다운샘플링(Downsample)
축소(Shrink)
강제 병합(Force Merge)
검색 가능 스냅샷(Searchable Snapshot)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;웜(Warm) 단계&lt;/p&gt;
&lt;pre id=&quot;code_1747352363626&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;특징: 업데이트가 거의 없고 조회 빈도가 낮아진 단계

노드 배치: 중간 성능의 노드에 배치

가능한 작업:
우선순위 설정
언팔로우
읽기 전용 설정
다운샘플링
할당(Allocate)
마이그레이션(Migrate)
축소
강제 병합&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;콜드(Cold) 단계&lt;/p&gt;
&lt;pre id=&quot;code_1747352386875&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;특징: 더 이상 업데이트되지 않고 가끔 조회되는 단계

노드 배치: 저비용 스토리지 노드에 배치

가능한 작업:
우선순위 설정
언팔로우
읽기 전용 설정
다운샘플링
검색 가능 스냅샷
할당
마이그레이션&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로즌(Frozen) 단계&lt;/p&gt;
&lt;pre id=&quot;code_1747352403555&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;특징: 거의 조회되지 않는 데이터를 위한 단계

노드 배치: 가장 저비용 스토리지에 배치

가능한 작업:
언팔로우
검색 가능 스냅샷&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;삭제(Delete) 단계&lt;/p&gt;
&lt;pre id=&quot;code_1747352433275&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;특징: 데이터를 영구적으로 제거하는 단계

가능한 작업:
스냅샷 대기(Wait For Snapshot)
삭제(Delete)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;페이즈 전환 예시&lt;/h4&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;처음 hot 페이즈에서는 매일 자동으로 롤오버를 수행하고, 3일이 지난 인덱스는 warm 페이즈로 전환된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;warm 페이즈는 읽기 전용 인덱스로 바꾸고 단일 세그먼트로 강제 병합한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7일지난 인덱스는 cold 페이즈로 전환되고 성능이 떨어지느 노드로 이동시킨 후 샤드 복구 우선순위를 낮춘다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;30일이 지난 인덱스는 delete 페이즈로 이동시킨다. 스냅샷으로 백업을 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ILM 정책 생성과 적용&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스 크기가 50GB에 도달하거나 30일이 경과하면 롤오버&lt;/li&gt;
&lt;li&gt;롤오버 후 2일이 지나면 warm 단계로 이동, 샤드 수를 1개로 줄이고 세그먼트 병한&lt;/li&gt;
&lt;li&gt;롤오버 후 7일이 지나면 cold 단계로 이동, cold node로 이동&lt;/li&gt;
&lt;li&gt;롤오버 후 30일이 지나면 인덱스 삭제&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1747352763842&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PUT _ilm/policy/metrics_policy
{
  &quot;policy&quot;: {
    &quot;phases&quot;: {
      &quot;hot&quot;: {
        &quot;actions&quot;: {
          &quot;rollover&quot;: {
            &quot;max_size&quot;: &quot;50GB&quot;,
            &quot;max_age&quot;: &quot;30d&quot;
          }
        }
      },
      &quot;warm&quot;: {
        &quot;min_age&quot;: &quot;2d&quot;,
        &quot;actions&quot;: {
          &quot;shrink&quot;: {
            &quot;number_of_shards&quot;: 1
          },
          &quot;forcemerge&quot;: {
            &quot;max_num_segments&quot;: 1
          }
        }
      },
      &quot;cold&quot;: {
        &quot;min_age&quot;: &quot;7d&quot;,
        &quot;actions&quot;: {
          &quot;allocate&quot;: {
            &quot;require&quot;: {
              &quot;data&quot;: &quot;cold&quot;
            }
          }
        }
      },
      &quot;delete&quot;: {
        &quot;min_age&quot;: &quot;30d&quot;,
        &quot;actions&quot;: {
          &quot;delete&quot;: {}
        }
      }
    }
  }
}&lt;/code&gt;&lt;/pre&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;h2 data-ke-size=&quot;size26&quot;&gt;[ 서킷 브레이커 ]&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드가 중단되는 문제를 발생시킬만한 무거운 작업의 수행을 미리 차단하는 역할을 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;특정 작업이 너무 많은 메모리를 사용하는 것을 방지&lt;/li&gt;
&lt;li&gt;JVM 힙 메모리 부족으로 인한 노드 실패 예방&lt;/li&gt;
&lt;li&gt;클러스터의 안정적인 성능 저하 방지&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;필드 데이터 서킷 브레이커&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;indices.breaker.fielddata.limit (default 40%)&lt;/li&gt;
&lt;li&gt;fielddata가 메모리에 올라갈 때 얼마만큼 메모리를 사용할지 예상&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;요청 서킷 브레이커&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Indices.breaker.request.limit (default 60%)&lt;/li&gt;
&lt;li&gt;요청 하나의 데이터 구조가 메모리를 과다하게 사용하는지 계산&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;부모 서킷 브레이커&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든&amp;nbsp;서킷&amp;nbsp;브레이커의&amp;nbsp;총&amp;nbsp;메모리&amp;nbsp;사용량&amp;nbsp;제어&lt;/li&gt;
&lt;li&gt;indices.breaker.total.use_real_memory: true 면 indices.breaker.total.limit: 95%&lt;/li&gt;
&lt;li&gt;indices.breaker.total.use_real_memory: false 면&lt;span&gt;&amp;nbsp;&lt;/span&gt;indices.breaker.total.limit: 70%&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;서킷 브레이커 설정&lt;/h3&gt;
&lt;pre id=&quot;code_1748135310413&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PUT /_cluster/settings
{
  &quot;persistent&quot;: {
    // 부모 서킷 브레이커 설정
    &quot;indices.breaker.total.use_real_memory&quot;: true,
    &quot;indices.breaker.total.limit&quot;: &quot;95%&quot;,
    
    // 필드 데이터 서킷 브레이커 설정
    &quot;indices.breaker.fielddata.limit&quot;: &quot;40%&quot;,
    &quot;indices.breaker.fielddata.overhead&quot;: 1.03,
    
    // 요청 서킷 브레이커 설정
    &quot;indices.breaker.request.limit&quot;: &quot;60%&quot;,
    &quot;indices.breaker.request.overhead&quot;: 1.0
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;indices.breaker.total.use_real_memory는 정적 설정이므로 노드 재시작이 필요&lt;/li&gt;
&lt;li&gt;다른 설정들은 동적으로 변경 가능&lt;/li&gt;
&lt;li&gt;하위 브레이커들의 합(40% + 60% = 100%)이 부모 브레이커 제한(95%)을 초과하지 않도록 주의&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;현재 서킷 브레이커 상태 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1748135382175&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET /_nodes/stats/breaker&lt;/code&gt;&lt;/pre&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;h2 data-ke-size=&quot;size26&quot;&gt;[ 슬로우 로그 설정 ]&lt;/h2&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;설정 이후에 {Cluster_name}_index_search_slowlog.log 파일이 생서된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;모든 인덱스에 대한 슬로우 로그 설정&lt;/h3&gt;
&lt;pre id=&quot;code_1748136477910&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PUT /_all/_settings
{
  &quot;index.search.slowlog.threshold.query.warn&quot;: &quot;10s&quot;,
  &quot;index.search.slowlog.threshold.fetch.warn&quot;: &quot;1s&quot;,
  &quot;index.indexing.slowlog.threshold.index.warn&quot;: &quot;10s&quot;,
  &quot;index.search.slowlog.include.user&quot;: true
}&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;검색 슬로우 로그 예시&lt;/h3&gt;
&lt;pre id=&quot;code_1748136501625&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;@timestamp&quot;: &quot;2024-12-11T22:34:22.613Z&quot;,
  &quot;elasticsearch.cluster.name&quot;: &quot;my-cluster&quot;,
  &quot;elasticsearch.node.name&quot;: &quot;node-1&quot;,
  &quot;elasticsearch.slowlog.id&quot;: &quot;search-123&quot;,
  &quot;elasticsearch.slowlog.search_type&quot;: &quot;QUERY_THEN_FETCH&quot;,
  &quot;elasticsearch.slowlog.source&quot;: &quot;{\&quot;query\&quot;:{\&quot;match_all\&quot;:{}}}&quot;,
  &quot;elasticsearch.slowlog.took&quot;: &quot;747.3micros&quot;,
  &quot;elasticsearch.slowlog.total_hits&quot;: &quot;1000 hits&quot;,
  &quot;log.level&quot;: &quot;WARN&quot;,
  &quot;user.name&quot;: &quot;elastic&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;query: 쿼리 실행 단계의 임계값&lt;/li&gt;
&lt;li&gt;fetch: 결과 가져오기 단계의 임계값&lt;/li&gt;
&lt;li&gt;각 단계별로 4가지 로그 레벨(warn, info, debug, trace) 설정 가능&lt;/li&gt;
&lt;li&gt;include.user:&amp;nbsp;사용자&amp;nbsp;정보&amp;nbsp;포함&amp;nbsp;여부&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;인덱싱 슬로우 로그 예시&lt;/h3&gt;
&lt;pre id=&quot;code_1748136524487&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;{
  &quot;@timestamp&quot;: &quot;2024-12-11T22:34:22.613Z&quot;,
  &quot;elasticsearch.index.name&quot;: &quot;my-index-000001&quot;,
  &quot;elasticsearch.slowlog.source&quot;: &quot;{\&quot;key\&quot;:\&quot;value\&quot;}&quot;,
  &quot;elasticsearch.slowlog.took&quot;: &quot;0.01ms&quot;,
  &quot;log.level&quot;: &quot;WARN&quot;,
  &quot;user.name&quot;: &quot;elastic&quot;
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;index: 문서 인덱싱 작업의 임계값&lt;/li&gt;
&lt;li&gt;source: 로그에 포함할 문서 소스의 최대 문자 수&lt;/li&gt;
&lt;li&gt;reformat:&amp;nbsp;소스&amp;nbsp;포맷팅&amp;nbsp;여부&lt;/li&gt;
&lt;/ul&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>DataPipeline/Elasticsearch</category>
      <category>ElasticSearch</category>
      <author>wave35</author>
      <guid isPermaLink="true">https://wave35.tistory.com/218</guid>
      <comments>https://wave35.tistory.com/218#entry218comment</comments>
      <pubDate>Sun, 25 May 2025 10:32:50 +0900</pubDate>
    </item>
    <item>
      <title>Elasticsearch 바이블 - 4장 검색 및 집계 API</title>
      <link>https://wave35.tistory.com/217</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ 문서 API ]&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;색인 API&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서 단건을 색인한다.&lt;/p&gt;
&lt;pre id=&quot;code_1746430914968&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PUT /my_index/_doc/1
{
  &quot;title&quot;: &quot;Elasticsearch Guide&quot;,
  &quot;author&quot;: &quot;John Doe&quot;,
  &quot;published_date&quot;: &quot;2023-01-01&quot;
}

# 라우팅지정
PUT /my_index/_doc/2?routing=myid2&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;조회 API&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서 단건을 조회한다.&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1746431150510&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET /my_index/_doc/1
&amp;gt;&amp;gt;&amp;gt;
{
  &quot;_index&quot;: &quot;my_index&quot;,
  &quot;_id&quot;: &quot;1&quot;,
  &quot;_version&quot;: 1,
  &quot;_seq_no&quot;: 0,
  &quot;_primary_term&quot;: 1,
  &quot;found&quot;: true,
  &quot;_source&quot;: {
    &quot;title&quot;: &quot;Elasticsearch Guide&quot;
  }
}

# 필드 필터링 : _source_includes, _source_excludes 옵션을 사용
GET /my_index/_doc/1?_source_includes=t*&amp;amp;_source_excludes=author
{
  ...
  &quot;_source&quot;: {
    &quot;title&quot;: &quot;Elasticsearch Guide&quot;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;업데이트 API&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청 본문에 doc이나 script를 지정하여 업데이트할 내용을 기술한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루씬의 세그먼트는 불변이라 내부적으로는 새로운 문서를 만들어 색인하는 형태이다.&lt;/p&gt;
&lt;pre id=&quot;code_1746431708635&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST /my_index/_update/1
{
  &quot;doc&quot;: {
    &quot;author&quot;: &quot;Jane Doe&quot;
  }
}&lt;/code&gt;&lt;/pre&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;doc_as_upsert&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;위의 케이스에도 새로 문서를 추가하는 upsert 기능이 필요하다면 doc_as_upsert 옵션을 지정한다.&lt;/p&gt;
&lt;pre id=&quot;code_1746431890645&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST /my_index/_update/2
{
  &quot;doc&quot;: {
    &quot;author&quot;: &quot;mike tyson&quot;
  },
  &quot;doc_as_upsert&quot;: true
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;삭제 API&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지정한 문서 하나를 삭제한다.&lt;/p&gt;
&lt;pre id=&quot;code_1746431947962&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;DELETE /my_index/_doc/2&lt;/code&gt;&lt;/pre&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;h2 data-ke-size=&quot;size26&quot;&gt;[ bulk API ]&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 문서에 대한 색인, 업데이트, 삭제 작업을 한번에 수행 할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 API와는 다르게 요청 본문을 NDJSON 형태로 만들어서 보낸다.( 여러줄의 JSON )&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Content-Type 헤더도 application/json 대신 application/x-ndjson을 사용해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1746492351716&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST /_bulk
{ &quot;index&quot; : { &quot;_index&quot; : &quot;test&quot;, &quot;_id&quot; : &quot;1&quot; } }
{ &quot;field1&quot; : &quot;value1&quot; }
{ &quot;delete&quot; : { &quot;_index&quot; : &quot;test&quot;, &quot;_id&quot; : &quot;2&quot; } }
{ &quot;create&quot; : { &quot;_index&quot; : &quot;test&quot;, &quot;_id&quot; : &quot;3&quot; } }
{ &quot;field1&quot; : &quot;value3&quot; }
{ &quot;update&quot; : { &quot;_id&quot; : &quot;1&quot;, &quot;_index&quot; : &quot;test&quot; } }
{ &quot;doc&quot; : { &quot;field2&quot; : &quot;value2&quot; } }
{ &quot;index&quot; : { &quot;_index&quot; : &quot;test&quot;, &quot;_id&quot; : &quot;4&quot; } }
{ &quot;field1&quot; : &quot;value4&quot; }
{ &quot;create&quot; : { &quot;_index&quot; : &quot;test&quot;, &quot;_id&quot; : &quot;5&quot; } }
{ &quot;field1&quot; : &quot;value5&quot; }&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청의 순서를 보장하지는 않지만,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동인한 index, _id, routing 조합이 가진 요청은 bulk API에 기술된 순서로 동작한다.&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;h2 data-ke-size=&quot;size26&quot;&gt;[ muti get API ]&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;_id를 여럿 지정하여 한번에 문서를 조회하는 API 이다.&lt;/p&gt;
&lt;pre id=&quot;code_1746492695920&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET /_mget
{
  &quot;docs&quot;: [
    {
      &quot;_index&quot;: &quot;my_index&quot;,
      &quot;_id&quot;: &quot;1&quot;
    },
    {
      &quot;_index&quot;: &quot;my_index&quot;,
      &quot;_id&quot;: &quot;2&quot;
    },
    {
      &quot;_index&quot;: &quot;my_index&quot;,
      &quot;_id&quot;: &quot;3&quot;
    }
  ]
}

또는

GET my_index/_mget
{
	&quot;ids&quot;: [&quot;1&quot;, &quot;2&quot;, &quot;3&quot;]
}

&amp;gt;&amp;gt;&amp;gt; 출력
{
  &quot;docs&quot;: [
    {
      &quot;_index&quot;: &quot;my_index&quot;,
      &quot;_id&quot;: &quot;1&quot;,
      &quot;_version&quot;: 2,
      &quot;_seq_no&quot;: 1,
      &quot;_primary_term&quot;: 1,
      &quot;found&quot;: true,
      &quot;_source&quot;: {
        &quot;title&quot;: &quot;Elasticsearch Guide&quot;,
        &quot;author&quot;: &quot;John Doe&quot;,
        &quot;published_date&quot;: &quot;2023-01-01&quot;
      }
    },
    {
      &quot;_index&quot;: &quot;my_index&quot;,
      &quot;_id&quot;: &quot;2&quot;,
      &quot;_version&quot;: 1,
      &quot;_seq_no&quot;: 2,
      &quot;_primary_term&quot;: 1,
      &quot;found&quot;: true,
      &quot;_source&quot;: {
        &quot;title&quot;: &quot;Elasticsearch Guide&quot;,
        &quot;author&quot;: &quot;Jame Dom&quot;,
        &quot;published_date&quot;: &quot;2024-01-01&quot;
      }
    },
    {
      &quot;_index&quot;: &quot;my_index&quot;,
      &quot;_id&quot;: &quot;3&quot;,
      &quot;found&quot;: false
    }
  ]
}&lt;/code&gt;&lt;/pre&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;h2 data-ke-size=&quot;size26&quot;&gt;[ 검색 API ]&lt;/h2&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;pre id=&quot;code_1746493118667&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET _search
GET my_index/_search
GET my_inde*,test*/_search&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;match&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지정한 필드의 내용이 질의와 매치되는 문서를 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필드가 text 타입이라면 필드의 값도 질의어도 모두 애널라이저로 분석된다.&lt;/p&gt;
&lt;pre id=&quot;code_1746495821249&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET /korean_data/_search
{
    &quot;query&quot;:{
        &quot;match&quot;:{
            &quot;address&quot;:{
                &quot;query&quot;:&quot;도산대거리&quot;
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;term&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지정한 필드와 값이 정확히 일치하는 문서를 찾는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;keyword 타입과 잘 맞는다.&lt;/p&gt;
&lt;pre id=&quot;code_1746496224196&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET /korean_data/_search
{
    &quot;query&quot;:{
        &quot;term&quot;:{
            &quot;address_district&quot;:{
                &quot;value&quot;:&quot;세종특별자치시 동구&quot;
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;terms&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;질의어와 정확히 일치하는 문서를 찾는 것은 term과 유사하며&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;range&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지정한 필드이 값이 특정 범위 내에 있는 문서를 찾는 쿼리이다.&lt;/p&gt;
&lt;pre id=&quot;code_1746496473328&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET /korean_data/_search
{
    &quot;query&quot;:{
        &quot;range&quot;:{
            &quot;generated_at&quot;:{
                &quot;gte&quot;:&quot;2025-05-06T09:38&quot;,
                &quot;lt&quot;:&quot;2025-05-06T09:40&quot;
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;prefix&lt;/h3&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;pre id=&quot;code_1746496797274&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET /korean_data/_search
{
    &quot;query&quot;:{
        &quot;prefix&quot;:{
            &quot;name&quot;:{
                &quot;value&quot;:&quot;김경&quot;
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;bool&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 쿼리를 조합하여 검색하는 쿼리이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;must, must_not, filter, should 4가지의 조건절을 조합한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;must 절과 filter절에 들어간 하위 쿼리는 AND 조건을 만족해야 하며,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;should 절의 하위쿼리는 OR 조건으로 검색하는 것과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1746497198819&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET /korean_data/_search
{
  &quot;query&quot;: {
    &quot;bool&quot;: {
      &quot;must&quot;: [
        {
          &quot;match&quot;: {
            &quot;address&quot;: &quot;서울특별시&quot;
          }
        }
      ],
      &quot;must_not&quot;: [
        {
          &quot;match&quot;: {
            &quot;address&quot;: &quot;서초구&quot;
          }
        }
      ],
      &quot;filter&quot;: [
        {
          &quot;range&quot;: {
            &quot;publish_date&quot;: {
              &quot;gte&quot;: &quot;2025-05-06T09:38:00&quot;,
              &quot;lte&quot;: &quot;2025-05-06T10:40:00&quot;
            }
          }
        }
      ],
      &quot;should&quot;: [
        {
          &quot;match&quot;: {
            &quot;name&quot;: &quot;권종수&quot;
          }
        },
        {
          &quot;match&quot;: {
            &quot;job&quot;: &quot;화학&quot;
          }
        }
      ],
      &quot;minimum_should_match&quot;: 1
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ 그외매개변수 ]&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;라우팅&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 API도 마찬가지로 라우팅을 지정해 주는 것이 좋다.&lt;/p&gt;
&lt;pre id=&quot;code_1746498644004&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET korean_data/_search?routing=r12
{
	...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;explain&lt;/h3&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;pre id=&quot;code_1746498779971&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET /korean_data/_search?explain=true
{
	...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;검색결과 정렬&lt;/h3&gt;
&lt;pre id=&quot;code_1746928615988&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET /korean_data/_search
{
  &quot;size&quot;: 3,
  &quot;sort&quot;: [
    {
      &quot;generated_at&quot;: {
        &quot;order&quot;: &quot;desc&quot;
      }
    },
    {
      &quot;job.keyword&quot;: {
        &quot;order&quot;: &quot;asc&quot;
      }
    }
  ],
  &quot;query&quot;: {
    &quot;match_all&quot;: {}
  }
}&lt;/code&gt;&lt;/pre&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;h2 data-ke-size=&quot;size26&quot;&gt;[ 집계 API ]&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;매트릭 집계&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서에 대한 산술적인 연산을 수행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;size = 0 으로 지정하면 집계 연산의 결과만 받아 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;stats 집계&lt;/h3&gt;
&lt;pre id=&quot;code_1746927481357&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET /korean_data/_search
{
    &quot;size&quot;: 0,
    &quot;aggs&quot;: {
        &quot;stats_agg&quot;: {
            &quot;stats&quot;: {
            &quot;field&quot;: &quot;generated_at&quot;
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;cardinality 집계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지정한 필드가 가진 고유한 값의 개수를 계산해서 반환한다.&lt;/p&gt;
&lt;pre id=&quot;code_1746927987402&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET /korean_data/_search
{
    &quot;size&quot;: 0,
    &quot;aggs&quot;: {
        &quot;cardinality_agg&quot;: {
            &quot;cardinality&quot;: {
            &quot;field&quot;: &quot;job.keyword&quot;
            }
        }
    }
}
# &amp;gt;&amp;gt;&amp;gt; 출력
{
	...
    &quot;aggregations&quot;: {
    &quot;cardinality_agg&quot;: {
      &quot;value&quot;: 97
    }
  }
}&lt;/code&gt;&lt;/pre&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;h4 data-ke-size=&quot;size20&quot;&gt;버킷집계&lt;/h4&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;h3 data-ke-size=&quot;size23&quot;&gt;range 집계&lt;/h3&gt;
&lt;pre id=&quot;code_1746927910251&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET /korean_data/_search
{
    &quot;size&quot;: 0,
    &quot;aggs&quot;: {
        &quot;range_agg&quot;: {
            &quot;range&quot;: {
            &quot;field&quot;: &quot;generated_at&quot;,
            &quot;ranges&quot;: [
                { &quot;to&quot;: &quot;2025-05-11T10:43:00&quot; },
                { &quot;from&quot;: &quot;2025-05-11T10:43:00&quot;, &quot;to&quot;: &quot;2025-05-11T10:44:00&quot; },
                { &quot;from&quot;: &quot;2025-05-11T10:43:00&quot; }
            ]
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;data_range 집계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 간단한 날짜 시간 계산식을 사용할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1746928193500&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET /korean_data/_search
{
    &quot;size&quot;: 0,
    &quot;aggs&quot;: {
        &quot;date_range_agg&quot;: {
            &quot;date_range&quot;: {
            &quot;field&quot;: &quot;timestamp&quot;,
            &quot;ranges&quot;: [
                { &quot;to&quot;: &quot;now-1M/M&quot; },
                { &quot;from&quot;: &quot;now-1M/M&quot;, &quot;to&quot;: &quot;now/M&quot; },
                { &quot;from&quot;: &quot;now/M&quot; }
            ]
            }
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;terms 집계&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 지정한 필드에 대해 가장 빈도수가 높은 term 순서대로 버킷을 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- size로 최대 몇개까지 버킷을 생성할지 지정한다.&lt;/p&gt;
&lt;pre id=&quot;code_1746928414546&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET /korean_data/_search
{
    &quot;size&quot;: 0,
    &quot;aggs&quot;: {
        &quot;terms_agg&quot;: {
            &quot;terms&quot;: {
            &quot;field&quot;: &quot;job.keyword&quot;,
            &quot;size&quot;: 10
            }
        }
    }
}
# &amp;gt;&amp;gt;&amp;gt; 출력
{
	...
    &quot;aggregations&quot;: {
        &quot;terms_agg&quot;: {
          &quot;doc_count_error_upper_bound&quot;: 0,
          &quot;sum_other_doc_count&quot;: 127,
          &quot;buckets&quot;: [
            {
              &quot;key&quot;: &quot;비파괴 검사원&quot;,
              &quot;doc_count&quot;: 3
            },
            {
              &quot;key&quot;: &quot;양식 주방장 및 조리사&quot;,
              &quot;doc_count&quot;: 3
            },
            ...
          ]
     }
   }
}&lt;/code&gt;&lt;/pre&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DataPipeline/Elasticsearch</category>
      <category>ElasticSearch</category>
      <author>wave35</author>
      <guid isPermaLink="true">https://wave35.tistory.com/217</guid>
      <comments>https://wave35.tistory.com/217#entry217comment</comments>
      <pubDate>Sun, 11 May 2025 10:57:24 +0900</pubDate>
    </item>
    <item>
      <title>SideProject - 카프카 시스템 구성 (아자르 비즈니스 메트릭 참조)</title>
      <link>https://wave35.tistory.com/216</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ 프로젝트 목적 아키텍처 ]&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 프로젝트는 카프카(Kafka) 기반의 분산 메시징 시스템을 구축하고 운영하고 있는,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아자르(Azar) 비즈니스 메트릭을 생성하는 프로세스를 흉내내어본 것입니다.&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;Git 참조 : &lt;a href=&quot;https://github.com/ehdrn3020/kafka_system_with_azar&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://github.com/ehdrn3020/kafka_system_with_azar&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Azar 참조 : &lt;a href=&quot;https://hyperconnect.github.io/2022/10/14/grafana-with-ksqlDB.html&quot;&gt;https://hyperconnect.github.io/2022/10/14/grafana-with-ksqlDB.html&lt;/a&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS 환경에서 Kafka 클러스터 구성&lt;/li&gt;
&lt;li&gt;Zookeeper를 통한 분산 코디네이션&lt;/li&gt;
&lt;li&gt;Schema Registry를 활용한 데이터 스키마 관리&lt;/li&gt;
&lt;li&gt;Kafka&amp;nbsp;Connect를&amp;nbsp;활용한&amp;nbsp;데이터&amp;nbsp;파이프라인&amp;nbsp;구성&lt;/li&gt;
&lt;li&gt;ElasticSearch(OpenSearch)를 통한 데이터 저장 및 검색&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아키텍처는 3대의 서버로 구성된 Kafka 클러스터를 중심으로,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 생성부터 저장까지의 전체 파이프라인을 Ansible을 통해 자동화하여 구축할 수 있도록 설계되었습니다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;architecture_azar.png&quot; data-origin-width=&quot;1786&quot; data-origin-height=&quot;726&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/weEOq/btsNBzmT2nU/STabek96ALNxCFA9pNe1OK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/weEOq/btsNBzmT2nU/STabek96ALNxCFA9pNe1OK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/weEOq/btsNBzmT2nU/STabek96ALNxCFA9pNe1OK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FweEOq%2FbtsNBzmT2nU%2FSTabek96ALNxCFA9pNe1OK%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;750&quot; height=&quot;305&quot; data-filename=&quot;architecture_azar.png&quot; data-origin-width=&quot;1786&quot; data-origin-height=&quot;726&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;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ 파일 구조 ]&lt;/h2&gt;
&lt;pre id=&quot;code_1745760993337&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;kafka_system_with_azar/
├── .git/                     # Git 저장소 정보
├── .idea/                    # IDE 설정 파일
├── README.md                 # 프로젝트 설명 및 사용법
├── schema_registry/          # 스키마 레지스트리 관련 코드
│   ├── opensearch_sink_example.py  # OpenSearch 싱크 예제
│   ├── producer_avro.py      # Avro 형식 데이터 생성자
│   ├── consumer_avro.py      # Avro 형식 데이터 소비자
│   ├── README.md             # 스키마 레지스트리 설명
│   └── single_mode/          # 단일 모드 구성 파일
├── ansible/                  # Ansible 자동화 스크립트
│   ├── inventory/            # 서버 인벤토리 정보
│   ├── group_vars/           # 그룹 변수 설정
│   ├── roles/                # Ansible 역할 정의
│   ├── connector.yml         # Connector 설치 플레이북
│   ├── opensearch.yml        # OpenSearch 설치 플레이북
│   ├── schema_registry.yml   # 스키마 레지스트리 설치 플레이북
│   ├── kafka.yml             # Kafka 설치 플레이북
│   └── zookeeper.yml         # Zookeeper 설치 플레이북
├── setting_aws/              # AWS 환경 설정 스크립트
│   └── setup_server.sh       # EC2 서버 설정 스크립트&lt;/code&gt;&lt;/pre&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;h2 data-ke-size=&quot;size26&quot;&gt;[ 시스템 설정 ]&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;AWS Server Setting&lt;/h3&gt;
&lt;pre id=&quot;code_1745761449698&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;### .env 파일 생성
- setting_aws/env_example 참조하여 생성

### keypair.pem 키 생성
- ec2 접속을 위해 keypair.pem 키를 setting_aws 폴더에 생성
- 파일 권한 수정 : sudo chmod 600 setting_aws/keypair.pem

### EC2 서버 실행
```commandline
sh setting_aws/setup_server.sh server_1
sh setting_aws/setup_server.sh server_2
sh setting_aws/setup_server.sh server_3
```

### scp keypair.pem
```commandline
scp -i setting_aws/keypair.pem setting_aws/keypair.pem ec2-user@server_1_ip:~
```

### SSH 접속
```commandline
ssh -i setting_aws/keypair.pem ec2-user@server_1_ip
```

### group_var host 관련 수정
```commandline
inventory/hosts 파일의 ansible_host 변수 수정
git push
cd /home/ec2-user/kafka_system_with_azar/
git pull  ( server_1 에서 실행 )
```&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Zookeeper Setting&lt;/h3&gt;
&lt;pre id=&quot;code_1745761473149&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;### zookeeper 설치
```commandline
cd /home/ec2-user/kafka_system_with_azar/ansible/
ansible-playbook -i inventory/hosts zookeeper.yml
```

### zookeeper 실행 확인
```commandline
systemctl status zookeeper
cat /data/zookeeper/myid
```&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Kafka Setting&lt;/h3&gt;
&lt;pre id=&quot;code_1745761491300&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;### kafka 설치
```commandline
ansible-playbook -i inventory/hosts kafka.yml
```
### kafka 실행 확인
```commandline
# server_1에서 토픽생성 ( 자동토픽생성(Auto Topic Creation)으로 토픽 생성 생략가능 )
/usr/local/kafka/bin/kafka-topics.sh --bootstrap-server kafka_01.com:9092 --create --topic test-overview01 --partitions 1 --replication-factor 3

# server_2에서 consumer 실행 
/usr/local/kafka/bin/kafka-console-consumer.sh --bootstrap-server kafka_01.com:9092 --topic test-overview01

# server_1 producer로 메세지 전송
/usr/local/kafka/bin/kafka-console-producer.sh --bootstrap-server kafka_02.com:9092 --topic test-overview01

# server_2에서 전송 된 메세지 확인
```&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Schema&amp;nbsp;Registery&lt;/h3&gt;
&lt;pre id=&quot;code_1745761541100&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Schema Registry
### 설치
```commandline
cd ~
sudo wget http://packages.confluent.io/archive/6.1/confluent-community-6.1.0.tar.gz -O /opt/confluent-community-6.1.0.tar.gz
sudo tar zxf /opt/confluent-community-6.1.0.tar.gz -C /usr/local/
sudo ln -s /usr/local/confluent-6.1.0 /usr/local/confluent
```

### 설정
```commandline
vi /usr/local/confluent/etc/schema-registry/schema-registry.properties
프로젝트의 schema_registry/schema-registry.properties 참조하여 업데이트
```

### 실행
```commandline
sudo vi /etc/systemd/system/schema-registry.service
프로젝트의 schema_registry/schema-registry.properties 참조하여 업데이트
sudo systemctl daemon-reload
sudo systemctl start schema-registry
sudo systemctl status schema-registry
```

### 호환성 확인
```commandline
curl -X GET http://kafka_01.com:8081/config
&amp;gt;&amp;gt;&amp;gt; 출력 값
{&quot;compatibilityLevel&quot;:&quot;FULL&quot;}
```

### 예시 - python 파일을 통해 메세지 전송
```commandline
# 가상환경에 필요한 모듈 설치
cd /home/ec2-user/kafka_system_with_azar/schema_registry
python -m venv venv
source  venv/bin/activate
pip install confluent-kafka[avro]

# 모듈 설치시 호환성
- 해당 예제는 confluent-kafka==2.8.0 설치하여 librdkafka 1.8.2 이상의 버전이 필요합니다.
- confluent-kafka Python라이브러리는 librdkafka를 래핑(wrapping)한 라이브러리입니다.
- librdkafka는 Apache Kafka 브로커와 통신하는 역할을 하며, Kafka 브로커의 버전과 호환성이 있습니다.
- python3.9 이상에서는 librdkafka 1.x.x 이상이 설치되지만, python3.7은 librdkafka 0.11.x 버전이 설치됩니다.
- librdkafka 0.11.x 버전은 confluent-kafka 1.0.0 이하와 호환되므로, 아래 py파일의 코드가 실행되지 않을 수 있습니다. 

# 메세지 전송 ( Schema Registry가 실행 중인 서버 )
python producer_avro.py
&amp;gt;&amp;gt;&amp;gt; Message delivered to kafka-avro2 [0]

# 메세지 확인
python consumer_avro.py
&amp;gt;&amp;gt;&amp;gt; {'name': 'Peter', 'class': 1}
```

### 스키마 적용 확인
```commandline
curl http://kafka_01.com:8081/schemas | python -m json.tool
&amp;gt;&amp;gt;&amp;gt;
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   375  100   375    0     0  16769      0 --:--:-- --:--:-- --:--:-- 17045
[
    {
        &quot;subject&quot;: &quot;kafka-avro2-value&quot;,
        &quot;version&quot;: 1,
        &quot;id&quot;: 1,
        &quot;schema&quot;: &quot;{
            \&quot;type\&quot;:\&quot;record\&quot;,
            \&quot;name\&quot;:\&quot;Student\&quot;,
            \&quot;namespace\&quot;:\&quot;student.avro\&quot;,
            \&quot;doc\&quot;:\&quot;This is an example of Avro.\&quot;,
            \&quot;fields\&quot;:[
                {\&quot;name\&quot;:\&quot;name\&quot;,\&quot;type\&quot;:[\&quot;null\&quot;,\&quot;string\&quot;],\&quot;doc\&quot;:\&quot;Name of the student\&quot;,\&quot;default\&quot;:null},
                {\&quot;name\&quot;:\&quot;class\&quot;,\&quot;type\&quot;:\&quot;int\&quot;,\&quot;doc\&quot;:\&quot;Class of the student\&quot;,\&quot;default\&quot;:1}
            ]
        }&quot;
    }
]
```&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 내용을 ansible을 통해 자동화&lt;/p&gt;
&lt;pre id=&quot;code_1745761623148&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;```
# 설치, 설정파일 수정 및 실행
ansible-playbook -i inventory/hosts schema_registry.yml

# 데몬 확인
sudo systemctl status schema-registry

# 호환성 확인
curl -X GET http://kafka_01.com:8081/config
&amp;gt;&amp;gt;&amp;gt; 출력 값
{&quot;compatibilityLevel&quot;:&quot;FULL&quot;}
```&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;ElasticSearch&amp;nbsp;&lt;/h3&gt;
&lt;pre id=&quot;code_1745761647364&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;### opensearch 설치
```commandline
# kafka01 호스트에 싱글 노드로 설치
ansible-playbook -i inventory/hosts opensearch.yml
```

### opensearch 확인
```commandline
sudo systemctl status opensearch

# 클러스터 내 각 노드의 정보
curl -X GET &quot;http://kafka_01.com:9200/_cat/nodes?v&quot;
# 클러스터의 전체 상태(Health) 를 조회
curl -X GET &quot;http://kafka_01.com:9200/_cluster/health?pretty&quot;
```&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Connector Sink&lt;/h3&gt;
&lt;pre id=&quot;code_1745761671834&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;### 커넥터 설치
```commandline
ansible-playbook -i inventory/hosts connector.yml
```

### 커넥터 확인
```commandline
# 실행 확인
sudo systemctl status kafka-connect
# 에러시 로그 확인
journalctl -u kafka-connect -f

# 클러스터에 현재 등록된 커넥터 목록을 확인
curl http://localhost:8083/connectors | python -m json.tool
# 커넥터 플러그인 확인
curl http://localhost:8083/connector-plugins | jq
[
  {
    &quot;class&quot;: &quot;io.aiven.kafka.connect.opensearch.OpensearchSinkConnector&quot;,
    &quot;type&quot;: &quot;sink&quot;,
    &quot;version&quot;: &quot;3.1.1&quot;
  },
...]
```

### 토픽 생성
```commandline
# 생성
/usr/local/kafka/bin/kafka-topics.sh --create \
    --bootstrap-server kafka_01.com:9092,kafka_02.com:9092,kafka_03.com:9092 \
    --replication-factor 3 \
    --partitions 3 \
    --topic opensearch-sink
/usr/local/kafka/bin/kafka-topics.sh --list --bootstrap-server kafka_01.com:9092,kafka_02.com:9092,kafka_03.com:9092

# 확인
/usr/local/kafka/bin/kafka-topics.sh --list --bootstrap-server kafka_01.com:9092
```

### 컨넥터 등록
```commandline
# API로 opensearch sink connector 등록
curl -X POST http://kafka_01.com:8083/connectors -H &quot;Content-Type: application/json&quot; -d '{
  &quot;name&quot;: &quot;opensearch-sink&quot;,
  &quot;config&quot;: {
    &quot;connector.class&quot;: &quot;io.aiven.kafka.connect.opensearch.OpensearchSinkConnector&quot;,
    &quot;tasks.max&quot;: &quot;1&quot;,
    &quot;topics&quot;: &quot;opensearch-sink&quot;,
    &quot;connection.url&quot;: &quot;http://kafka_01.com:9200&quot;,

    &quot;key.converter&quot;: &quot;io.confluent.connect.avro.AvroConverter&quot;,
    &quot;value.converter&quot;: &quot;io.confluent.connect.avro.AvroConverter&quot;,
    &quot;key.converter.schema.registry.url&quot;: &quot;http://kafka_01.com:8081&quot;,
    &quot;value.converter.schema.registry.url&quot;: &quot;http://kafka_01.com:8081&quot;,

    &quot;schema.registry.url&quot;: &quot;http://kafka_01.com:8081&quot;,
    &quot;value.converter.schemas.enable&quot;: &quot;false&quot;,
    &quot;schema.ignore&quot;: &quot;true&quot;,
    &quot;type.name&quot;: &quot;kafka-connect&quot;
  }
}'

# 등록 확인
curl http://localhost:8083/connectors | python -m json.tool
...
[
    &quot;opensearch-sink&quot;
]

# 상태 확인
curl -X GET http://kafka_01.com:8083/connectors/opensearch-sink/status | jq

# 커넥터 삭제
curl -X DELETE http://localhost:8083/connectors/opensearch-sink
```&lt;/code&gt;&lt;/pre&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;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ kafka 데이터 전송 ]&lt;/h2&gt;
&lt;pre id=&quot;code_1745761717996&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;```commandline
# 가상환경에 필요한 모듈 설치 ( kafka_01.com 호스트에서 실행 )
cd /home/ec2-user/kafka_system_with_azar/schema_registry
python -m venv venv
source  venv/bin/activate
pip install confluent-kafka[avro]

# 메세지 전송 ( Schema Registry가 실행 중인 서버 )
python opensearch_sink_example.py
&amp;gt;&amp;gt;&amp;gt; Message delivered to kafka-avro2 [0]
```

### schema 등록확인
```commandline
# subjects list 확인
curl http://kafka_01.com:8081/subjects
&amp;gt;&amp;gt;&amp;gt; [&quot;opensearch-sink-value&quot;]

# subject 버전 확인
curl http://kafka_01.com:8081/subjects/opensearch-sink-value/versions
&amp;gt;&amp;gt;&amp;gt; [1]

```

### opensearch index store 확인
```commandline
curl -X GET &quot;http://kafka_01.com:9200/_cat/indices?v&quot;
curl -X GET &quot;http://kafka_01.com:9200/opensearch-sink*/_search?pretty&quot;
```&lt;/code&gt;&lt;/pre&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>사이드프로젝트</category>
      <author>wave35</author>
      <guid isPermaLink="true">https://wave35.tistory.com/216</guid>
      <comments>https://wave35.tistory.com/216#entry216comment</comments>
      <pubDate>Sun, 27 Apr 2025 22:49:03 +0900</pubDate>
    </item>
    <item>
      <title>Elasticsearch 바이블 - 3장 인덱스 설계</title>
      <link>https://wave35.tistory.com/214</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ 인덱스 설정 ]&lt;/h2&gt;
&lt;pre id=&quot;code_1745034752314&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET [index_name]/_settings&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스 설정은 인덱스명 뒤에 _settings를 넣어 GET 메서드로 호출한다.&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;pre id=&quot;code_1745115212475&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PUT /my_index
{
    &quot;settings&quot;: {
        &quot;number_of_shards&quot;: 2,
        &quot;number_of_replicas&quot;: 2
    }
}
&amp;gt;&amp;gt;&amp;gt; 
{
  &quot;acknowledged&quot;: true,
  &quot;shards_acknowledged&quot;: true,
  &quot;index&quot;: &quot;my_index&quot;
}


GET my_index
&amp;gt;&amp;gt;&amp;gt;
{
  &quot;my_index&quot;: {
    &quot;aliases&quot;: {},
    &quot;mappings&quot;: {},
    &quot;settings&quot;: {
      &quot;index&quot;: {
        &quot;routing&quot;: {
          &quot;allocation&quot;: {
            &quot;include&quot;: {
              &quot;_tier_preference&quot;: &quot;data_content&quot;
            }
          }
        },
        &quot;number_of_shards&quot;: &quot;2&quot;,
        &quot;provided_name&quot;: &quot;my_index&quot;,
        &quot;creation_date&quot;: &quot;1745115115705&quot;,
        &quot;number_of_replicas&quot;: &quot;2&quot;,
        &quot;uuid&quot;: &quot;3XaOBSOKTmi-PfcTLvPjiw&quot;,
        &quot;version&quot;: {
          &quot;created&quot;: &quot;8521000&quot;
        }
      }
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;number_of_shards&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인덱스 데이터를 몇 개의 샤드로 쪼갤 것인지 지정하는 값&lt;/li&gt;
&lt;li&gt;한번 지정하면 바꾸기가 쉽지 않음&lt;/li&gt;
&lt;li&gt;샤드 당 루씬 인덱스가 하나씩 더 생성되어 너무 크게 설정하면 클러스터 성능이 떨어짐&lt;/li&gt;
&lt;li&gt;너무 적게 설정하면 샤드 크기가 커져 복구시간이 오래 걸리고 안정성이 떨어짐&lt;/li&gt;
&lt;li&gt;기본 값은 1&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;number_of_replicas&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;주 샤드 하나당 본제본 샤드를 몇 개 생성할 것인지 지정하는 값&lt;/li&gt;
&lt;li&gt;인덱스 생성 후에도 동적으로 변경 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;refresh_interval&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;엘라스틱서치가 인덱스 대상으로 refresh를 얼마나 자주 수행할 것인지 지정하는 값&lt;/li&gt;
&lt;li&gt;기본 값은 &quot;1s&quot;, 1초&lt;/li&gt;
&lt;/ul&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;h2 data-ke-size=&quot;size26&quot;&gt;[ 맵핑 필드와 타입 ]&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;동적 맵핑&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무 내용이 없던 mappings 항목에 필드의 타입과 정보가 추가된 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스에 문서가 색인 될 때, 기존에 매핑 정보가 없으면 자동으로 적당한 필드 타입을 지정하며 생성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1745115592095&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;POST /my_index/_doc/1
{
    &quot;title&quot;: &quot;hellow!&quot;,
    &quot;views&quot;: 1234,
    &quot;public&quot;: true,
    &quot;point&quot;: 4.5,
    &quot;created&quot;: &quot;2025-04-20T14:00:54.123Z&quot;
}

&amp;gt;&amp;gt;&amp;gt;
{
  &quot;my_index&quot;: {
    &quot;aliases&quot;: {},
    &quot;mappings&quot;: {
      &quot;properties&quot;: {
        &quot;created&quot;: {
          &quot;type&quot;: &quot;date&quot;
        },
        &quot;point&quot;: {
          &quot;type&quot;: &quot;float&quot;
        },
        &quot;public&quot;: {
          &quot;type&quot;: &quot;boolean&quot;
        },
        &quot;title&quot;: {
          &quot;type&quot;: &quot;text&quot;,
          &quot;fields&quot;: {
            &quot;keyword&quot;: {
              &quot;type&quot;: &quot;keyword&quot;,
              &quot;ignore_above&quot;: 256
            }
          }
        },
        &quot;views&quot;: {
          &quot;type&quot;: &quot;long&quot;
        }
      }
    },
    ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;명시적 맵핑&lt;/h3&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;pre id=&quot;code_1745117120562&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PUT /my_index
{
  &quot;mappings&quot;: {
    &quot;properties&quot;: {
      &quot;title&quot;: {
        &quot;type&quot;: &quot;text&quot;
      },
      &quot;views&quot;: {
        &quot;type&quot;: &quot;integer&quot;
      },
      &quot;public&quot;: {
        &quot;type&quot;: &quot;boolean&quot;
      },
      &quot;point&quot;: {
        &quot;type&quot;: &quot;float&quot;
      },
      &quot;created&quot;: {
        &quot;type&quot;: &quot;date&quot;,
        &quot;format&quot;: &quot;strict_date_time&quot; 
        // ISO 8601, 예: 2025-04-20T14:00:54.123Z
      }
    }
  }
}

# 적용확인
GET my_index/_mapping&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;필드타입&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필드타입은 boolean, text, keyword, long, integer, short, byte, double, float, half_float, scaled_float,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;date, ip, array, object, nested, geo_point, geo_shape 등이 있으며&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;h4 data-ke-size=&quot;size20&quot;&gt;text 타입과 keyword 타입&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;text :&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;애널라이저가 적용된 후 색인된다.&lt;/li&gt;
&lt;li&gt;즉 들어온 값은 분석하여 여러 토큰으로 쪼개어 역색인을 구성한다.&lt;/li&gt;
&lt;li&gt;전문검색에 적합합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;keywork :&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문자열 값을 여러 토큰으로 쪼개지 않고 역색인한다.&lt;/li&gt;
&lt;li&gt;간단한 전처리만을 실행하는 노멀라이저를 적용한다.&lt;/li&gt;
&lt;li&gt;정렬과 집계과 작은 작업일 때 적절하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-20 오후 11.22.18.png&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/MXVU9/btsNttMYzGC/4CyB72n1CeNlDYVnaktyjK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/MXVU9/btsNttMYzGC/4CyB72n1CeNlDYVnaktyjK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/MXVU9/btsNttMYzGC/4CyB72n1CeNlDYVnaktyjK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FMXVU9%2FbtsNttMYzGC%2F4CyB72n1CeNlDYVnaktyjK%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;650&quot; height=&quot;324&quot; data-filename=&quot;스크린샷 2025-04-20 오후 11.22.18.png&quot; data-origin-width=&quot;718&quot; data-origin-height=&quot;358&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/field-data-types&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/field-data-types&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ 애널라이저와 토크나이저 ]&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애널라이저는 3단계로 이루어져있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;캐릭터 필터 (Character Filter)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;원본 텍스트에서 특수 문자 제거 등의 전처리&lt;/li&gt;
&lt;li&gt;0개 이상의 캐릭터필터를 지정할 수 있으며 순서대로 수행된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;토크나이저 (Tokenizer)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;텍스트를 개별 토큰으로 분리&lt;/li&gt;
&lt;li&gt;한 개의 토크나이저만 지정할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;토큰 필터 (Token Filter)&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;토큰 변형, 추가 또는 제거&lt;/li&gt;
&lt;li&gt;lowercase / pattern_replace / trim / tuncate 등이 있다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-27 오후 6.40.24.png&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;480&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bHIfcS/btsNCz0HvDI/Nh6tkTtyWa3AXTzY67jQa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bHIfcS/btsNCz0HvDI/Nh6tkTtyWa3AXTzY67jQa1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bHIfcS/btsNCz0HvDI/Nh6tkTtyWa3AXTzY67jQa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbHIfcS%2FbtsNCz0HvDI%2FNh6tkTtyWa3AXTzY67jQa1%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;500&quot; height=&quot;474&quot; data-filename=&quot;스크린샷 2025-04-27 오후 6.40.24.png&quot; data-origin-width=&quot;506&quot; data-origin-height=&quot;480&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;/p&gt;
&lt;pre id=&quot;code_1745748000690&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;curl -X GET &quot;localhost:9200/_analyze&quot; -H 'Content-Type: application/json' -d '
{
  &quot;char_filter&quot;: [&quot;html_strip&quot;],
  &quot;tokenizer&quot;: &quot;standard&quot;,
  &quot;filter&quot;: [&quot;uppercase&quot;],
  &quot;text&quot;: &quot;&amp;lt;p&amp;gt;hellow, hi word!&amp;lt;/p&amp;gt;&quot;
}'

&amp;gt;&amp;gt;&amp;gt;
{
  &quot;tokens&quot;: [
    {
      &quot;token&quot;: &quot;HELLOW&quot;,
      &quot;start_offset&quot;: 0,
      &quot;end_offset&quot;: 6,
      &quot;type&quot;: &quot;&amp;lt;ALPHANUM&amp;gt;&quot;,
      &quot;position&quot;: 0
    },
    {
      &quot;token&quot;: &quot;HI&quot;,
      &quot;start_offset&quot;: 8,
      &quot;end_offset&quot;: 10,
      &quot;type&quot;: &quot;&amp;lt;ALPHANUM&amp;gt;&quot;,
      &quot;position&quot;: 1
    },
    {
      &quot;token&quot;: &quot;WORD&quot;,
      &quot;start_offset&quot;: 11,
      &quot;end_offset&quot;: 15,
      &quot;type&quot;: &quot;&amp;lt;ALPHANUM&amp;gt;&quot;,
      &quot;position&quot;: 2
    }
  ]
}&lt;/code&gt;&lt;/pre&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;h2 data-ke-size=&quot;size26&quot;&gt;[ 템플릿 ]&lt;/h2&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;h3 data-ke-size=&quot;size23&quot;&gt;인덱스 템플릿&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인덱스 패턴에는 * 와일드카드를 사용할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;priority 값을 이용하면 인덱스 템플릿 간 우선 적용순위를 조정한다. (높을수록 우선순위 높음)&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;pre id=&quot;code_1745755752927&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PUT /_template/my_template
{
  &quot;index_patterns&quot;: [&quot;test-te*&quot;, &quot;bar*&quot;],
  &quot;priority&quot;: 1,
  &quot;template&quot; {
      &quot;settings&quot;: {
        &quot;number_of_shards&quot;: 1
      },
      &quot;mappings&quot;: {
        &quot;properties&quot;: {
          &quot;host_name&quot;: {
            &quot;type&quot;: &quot;keyword&quot;
          },
          &quot;created_at&quot;: {
            &quot;type&quot;: &quot;date&quot;,
            &quot;format&quot;: &quot;EEE MMM dd HH:mm:ss Z yyyy&quot;
          }
        }
      }
   }
}
# 생성된 템플릿 조회
GET /_template/my_template

# 위 템플릿에 맞는 인덱스 생성
PUT test-text-1 
GET test-text-1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;컴포넌트 템플릿&lt;/h3&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;/p&gt;
&lt;pre id=&quot;code_1745757692282&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PUT /_component_template/log_mappings
{
  &quot;template&quot;: {
    &quot;mappings&quot;: {
      &quot;properties&quot;: {
        &quot;host_name&quot;: {
          &quot;type&quot;: &quot;keyword&quot;
        },
        &quot;created_at&quot;: {
          &quot;type&quot;: &quot;date&quot;,
          &quot;format&quot;: &quot;EEE MMM dd HH:mm:ss Z yyyy&quot;
        },
        &quot;message&quot;: {
          &quot;type&quot;: &quot;text&quot;
        },
        &quot;level&quot;: {
          &quot;type&quot;: &quot;keyword&quot;
        }
      }
    }
  }
}

PUT /_index_template/logs_template
{
  &quot;index_patterns&quot;: [&quot;logs-*&quot;, &quot;app-logs-*&quot;],
  &quot;composed_of&quot;: [&quot;log_mappings&quot;],
  &quot;priority&quot;: 100,
  &quot;template&quot;: {
    &quot;settings&quot;: {
      &quot;number_of_shards&quot;: 1,
      &quot;number_of_replicas&quot;: 1
    }
  }
}

PUT /_component_template/basic_settings
{
  &quot;template&quot;: {
    &quot;settings&quot;: {
      &quot;number_of_shards&quot;: 2,
      &quot;number_of_replicas&quot;: 1
    }
  }
}

PUT /_index_template/complete_logs_template
{
  &quot;index_patterns&quot;: [&quot;logs-*&quot;],
  &quot;composed_of&quot;: [&quot;basic_settings&quot;, &quot;log_mappings&quot;],
  &quot;priority&quot;: 200
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;동적 템플릿&lt;/h3&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- description_text와 같이 _text로 끝나는 문자열 필드는 text 타입으로 매핑&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- user_id_keyword와 같이 _keyword로 끝나는 문자열 필드는 keyword 타입으로 매핑&lt;/p&gt;
&lt;pre id=&quot;code_1745758135437&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PUT my-index
{
  &quot;mappings&quot;: {
    &quot;dynamic_templates&quot;: [
      {
        &quot;text_fields&quot;: {
          &quot;match_mapping_type&quot;: &quot;string&quot;,
          &quot;match&quot;: &quot;*_text&quot;,
          &quot;mapping&quot;: {
            &quot;type&quot;: &quot;text&quot;
          }
        }
      },
      {
        &quot;keyword_fields&quot;: {
          &quot;match_mapping_type&quot;: &quot;string&quot;,
          &quot;match&quot;: &quot;*_keyword&quot;,
          &quot;mapping&quot;: {
            &quot;type&quot;: &quot;keyword&quot;
          }
        }
      }
    ]
  }
}&lt;/code&gt;&lt;/pre&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;엘라스틱서치가 인덱스를 구성하는 샤드 중, 몇 번 샤드를 대상으로 작업을 수행하지 지정하기 위해 사용하는 값&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 5개의 샤드를 가진 익덱스를 생성&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- routing 값으로 라우팅을 지정&lt;/p&gt;
&lt;pre id=&quot;code_1745759141382&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PUT my-index
{
	&quot;settings&quot;:{
    	&quot;number_of_shards&quot; 5,
        &quot;number_of_replicas&quot;: 1
    }
}

PUT my-index/_doc/1?routing=user1
{
  &quot;title&quot;: &quot;This is a document&quot;
}&lt;/code&gt;&lt;/pre&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;pre id=&quot;code_1745759524940&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GET my-index/_search
{
  &quot;took&quot;: 5,
  &quot;timed_out&quot;: false,
  &quot;_shards&quot;: {
    &quot;total&quot;: 2,            // 실제 검색이 수행된 샤드 수 (user1, user2에 해당하는 샤드만)
    &quot;successful&quot;: 2,
    &quot;skipped&quot;: 0,
    &quot;failed&quot;: 0
  },
  &quot;hits&quot;: {
    &quot;total&quot;: {
      &quot;value&quot;: 42,
      &quot;relation&quot;: &quot;eq&quot;
    },
    &quot;max_score&quot;: 1.0,
    &quot;hits&quot;: [
      {
        &quot;_index&quot;: &quot;my-index&quot;,
        &quot;_id&quot;: &quot;1&quot;,
        &quot;_score&quot;: 1.0,
        &quot;_routing&quot;: &quot;user1&quot;,    // 이 문서는 user1 라우팅 값으로 저장됨
        &quot;_source&quot;: {
          &quot;title&quot;: &quot;Document for User 1&quot;,
          &quot;content&quot;: &quot;This is content for user1&quot;,
          &quot;timestamp&quot;: &quot;2023-07-15T10:30:00Z&quot;,
          &quot;user_id&quot;: &quot;user1&quot;
        }
      },
      {
        &quot;_index&quot;: &quot;my-index&quot;,
        &quot;_id&quot;: &quot;3&quot;,
        &quot;_score&quot;: 1.0,
        &quot;_routing&quot;: &quot;user2&quot;,    // 이 문서는 user2 라우팅 값으로 저장됨
        &quot;_source&quot;: {
          &quot;title&quot;: &quot;Document for User 2&quot;,
          &quot;content&quot;: &quot;This is content for user2&quot;,
          &quot;timestamp&quot;: &quot;2023-07-14T14:20:00Z&quot;,
          &quot;user_id&quot;: &quot;user2&quot;
        }
      },
	  ...
    ]
  }
}&lt;/code&gt;&lt;/pre&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;pre id=&quot;code_1745759569904&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PUT my-index
{
  &quot;mappings&quot;: {
    &quot;_routing&quot;: {
      &quot;required&quot;: true
    }
  }
}&lt;/code&gt;&lt;/pre&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>DataPipeline/Elasticsearch</category>
      <category>ElasticSearch</category>
      <author>wave35</author>
      <guid isPermaLink="true">https://wave35.tistory.com/214</guid>
      <comments>https://wave35.tistory.com/214#entry214comment</comments>
      <pubDate>Sun, 27 Apr 2025 22:13:19 +0900</pubDate>
    </item>
    <item>
      <title>Python - bisect 예제</title>
      <link>https://wave35.tistory.com/215</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ bisect 모듈 ]&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bisect 모듈은 이진 탐색을 쉽게 쓸 수 있게 해주는 파이썬 표준 라이브러리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;&lt;b&gt;정렬된 리스트&lt;/b&gt;&quot;에 값을 효율적으로 삽입하거나 위치를 찾을 때 유용&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;h2 data-ke-size=&quot;size26&quot;&gt;[ 사용 함수 ]&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 27.5193%;&quot;&gt;함수&lt;/td&gt;
&lt;td style=&quot;width: 39.1473%;&quot;&gt;의미&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;반환값&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 27.5193%;&quot;&gt;&lt;span&gt;bisect_left(a, x)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 39.1473%;&quot;&gt;좌측 삽입 위치 탐색&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;x를 a에 넣을 때 왼쪽 인덱스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 27.5193%;&quot;&gt;bisect_right(a, x)&lt;/td&gt;
&lt;td style=&quot;width: 39.1473%;&quot;&gt;우측 삽입 위치 탐색&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;x를 a에 넣을 때 오른쪽 인덱스&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 27.5193%;&quot;&gt;insort_left(a, x)&lt;/td&gt;
&lt;td style=&quot;width: 39.1473%;&quot;&gt;bisect_left 위치에 삽입&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;리스트 a가 정렬된 상태 유지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 27.5193%;&quot;&gt;insort_right(a, x)&lt;/td&gt;
&lt;td style=&quot;width: 39.1473%;&quot;&gt;bisect_right 위치에 삽입&lt;/td&gt;
&lt;td style=&quot;width: 33.3333%;&quot;&gt;동일&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ 예제 ]&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬 리스트에 중복을 허용하며 요소 삽입&lt;/p&gt;
&lt;pre id=&quot;code_1745415101406&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import bisect

scores = [15, 22, 22, 30]
bisect.insort(scores, 22)      # insort == insort_right
print(scores)                  # [15, 22, 22, 22, 30]&lt;/code&gt;&lt;/pre&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;pre id=&quot;code_1745415022224&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;a = [1, 2, 2, 2, 3, 4, 5]
lo = bisect.bisect_left(a, 2)   # 1
hi = bisect.bisect_right(a, 2)  # 4
count = hi - lo                 # 3&lt;/code&gt;&lt;/pre&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;pre id=&quot;code_1745415230445&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;from bisect import insort_right

leaderboard = []          # (score, user) 튜플의 내림차순 리스트
def add_score(score, user):
    # 점수가 큰 순으로 정렬하기 위해 음수 key 사용, 튜플(-score, user)
    insort_right(leaderboard, (-score, user))

add_score(300, &quot;Alice&quot;)
add_score(270, &quot;Bob&quot;)
add_score(285, &quot;Carol&quot;)

print(leaderboard[:3])    

&amp;gt;&amp;gt;&amp;gt; 출력
[(-300, 'Alice'), (-285, 'Carol'), (-270, 'Bob')]&lt;/code&gt;&lt;/pre&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;h2 data-ke-size=&quot;size26&quot;&gt;[ 문제 ]&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정수 배열 nums와 정수 n이 주어질 때, 증가 부분 수열(subsequence) 구하기&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 1&lt;/p&gt;
&lt;pre id=&quot;code_1745414446078&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;nums = [2, 1, 5, 0, 4, 6]
n    = 3

가능한 증가 부분 수열 예: [2, 5, 6], [1, 4, 6], [0, 4, 6] 등이 있으므로
&amp;rarr; True&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예제 2&lt;/p&gt;
&lt;pre id=&quot;code_1745414493506&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;nums = [5, 4, 3, 2, 1]
n    = 2

모든 값이 내림차순이므로 길이 2인 증가 부분 수열도 만들 수 없음
&amp;rarr; False&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 O(N log N) 시간에 해결하는 표준 기법(그리디+이진 탐색)을 구현한 예시&lt;/p&gt;
&lt;pre id=&quot;code_1745414282025&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import bisect

def increasingSubsequence(nums, n):
    tail = []  # 각 길이별 수열의 최소 마지막 값 저장

    for num in nums:
        idx = bisect.bisect_left(tail, num)  # 현재 num이 들어갈 자리
        if idx == len(tail):
            tail.append(num)  # 새로운 길이의 수열이 생김
        else:
            tail[idx] = num  # 기존 길이 수열의 최소 마지막 값 갱신

        if len(tail) &amp;gt;= n:
            return True  # n중 수열 발견

    return False&lt;/code&gt;&lt;/pre&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>Programming/Python</category>
      <category>python</category>
      <author>wave35</author>
      <guid isPermaLink="true">https://wave35.tistory.com/215</guid>
      <comments>https://wave35.tistory.com/215#entry215comment</comments>
      <pubDate>Wed, 23 Apr 2025 22:40:30 +0900</pubDate>
    </item>
    <item>
      <title>엘라스틱서치 바이블 - 2장 기본동작과 구조</title>
      <link>https://wave35.tistory.com/213</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ 구조 개괄 ]&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;기본적인 용어&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;문서 : 엘라스틱서치가 저장하고 색인을 생성하는 JSON 문서&lt;/li&gt;
&lt;li&gt;인덱스 : 문서를 모아 놓은 단위, 인덱스 단위로 검색을 요청&lt;/li&gt;
&lt;li&gt;샤드 : 인덱스는 그 내용을 여러 샤드로 분리하여 분산 저장하여 고가용성을 제공&lt;/li&gt;
&lt;li&gt;_id : 인덱스 내 문서에 부여되는 고유한 구분자&lt;/li&gt;
&lt;li&gt;노드 : 엘라스틱서치 프로세스 하나가 노드 하나를 구성&lt;/li&gt;
&lt;li&gt;노드의 역할 : 데이터노드, 마스터노드, 조정노드 등 하나의 역할을 맡아 수행
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클러스터를 관리하는 역할을 마스터노드에서 진행&lt;/li&gt;
&lt;li&gt;샤드를 보유하고 샤드에 읽기 쓰기 작업을 수행하는 노드는 데이터노드&lt;/li&gt;
&lt;li&gt;클라이언트의 요청을 받아서 노드에 요청을 분배하는 노드는 조정노드&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-15 오전 6.57.58.png&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;377&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cto0ka/btsNkitoMka/uXTTqmDi3DrvpsEjb8gSo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cto0ka/btsNkitoMka/uXTTqmDi3DrvpsEjb8gSo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cto0ka/btsNkitoMka/uXTTqmDi3DrvpsEjb8gSo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcto0ka%2FbtsNkitoMka%2FuXTTqmDi3DrvpsEjb8gSo0%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;650&quot; height=&quot;355&quot; data-filename=&quot;스크린샷 2025-04-15 오전 6.57.58.png&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;377&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;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-15 오전 6.59.27.png&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;321&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tciYO/btsNhsJpFLh/cQhaghH4FIYKyrrJdCBQ4k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tciYO/btsNhsJpFLh/cQhaghH4FIYKyrrJdCBQ4k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tciYO/btsNhsJpFLh/cQhaghH4FIYKyrrJdCBQ4k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtciYO%2FbtsNhsJpFLh%2FcQhaghH4FIYKyrrJdCBQ4k%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;650&quot; height=&quot;315&quot; data-filename=&quot;스크린샷 2025-04-15 오전 6.59.27.png&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;321&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;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ 내부 구조와 루씬 ]&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;루씬 flush&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서 색인 요청이 들어오면 루씬은 문서를 분석해서 역색인을 생성한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최초 메모리 버퍼에 들어가며 주기적으로 디스크에 flush한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;루씬 commit&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디스크에 파일이 기록하는 것까지 보장하지는 않으므로&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fsync 시스템콜을 통해 주기적으로 싱크를 맞추는 작업을 수행한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-15 오전 7.08.44.png&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;272&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bzJdXz/btsNjLuvoAo/YdWx6Qa4LwyCZ8dd3EKtjk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bzJdXz/btsNjLuvoAo/YdWx6Qa4LwyCZ8dd3EKtjk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bzJdXz/btsNjLuvoAo/YdWx6Qa4LwyCZ8dd3EKtjk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbzJdXz%2FbtsNjLuvoAo%2FYdWx6Qa4LwyCZ8dd3EKtjk%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;650&quot; height=&quot;261&quot; data-filename=&quot;스크린샷 2025-04-15 오전 7.08.44.png&quot; data-origin-width=&quot;678&quot; data-origin-height=&quot;272&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;h3 data-ke-size=&quot;size23&quot;&gt;세그먼트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디스크에 기록된 파일들이 모이면 세그먼트라는 단위가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;루씬의 검색 대상이며 불변(immutable)인 데이터로 구성된다.&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;h3 data-ke-size=&quot;size23&quot;&gt;루씬 인덱스와 엘라스틱서치 인덱스&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 세그먼트가 모이면 하나의 루씬 인덱스가 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;엘라스틱 샤드는 루씬인덱스의 래핑(wrap)한 단위이며 여러개 보이면 엘라스틱 인덱스가 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-15 오전 7.08.05.png&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;301&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tMqiC/btsNlYsMmhY/AYlh4RNjvyfOD8Y2C9uGQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tMqiC/btsNlYsMmhY/AYlh4RNjvyfOD8Y2C9uGQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tMqiC/btsNlYsMmhY/AYlh4RNjvyfOD8Y2C9uGQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtMqiC%2FbtsNlYsMmhY%2FAYlh4RNjvyfOD8Y2C9uGQk%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;650&quot; height=&quot;284&quot; data-filename=&quot;스크린샷 2025-04-15 오전 7.08.05.png&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;301&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;&lt;/p&gt;</description>
      <category>DataPipeline/Elasticsearch</category>
      <category>ElasticSearch</category>
      <author>wave35</author>
      <guid isPermaLink="true">https://wave35.tistory.com/213</guid>
      <comments>https://wave35.tistory.com/213#entry213comment</comments>
      <pubDate>Tue, 15 Apr 2025 07:10:51 +0900</pubDate>
    </item>
    <item>
      <title>AWS - DynamoDB GSI</title>
      <link>https://wave35.tistory.com/212</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ GSI 개념 ]&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DyanmoDB는 기본키(PK) 기준으로 데이터를 조회하며&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;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 PK = user_id 로 지정했는데 age로 조회하고 싶을 경우,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 테이블에선 조회가 가능할지 모르지만 저장된 데이터가 클 경우 에러 혹은 검색이 되지 않습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이럴 때 GSI나 LSI를 사용하면 데이터 복제 없이도 새로운 쿼리 경로를 생성할 수 있습니다.&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;아래는 DynamoDB에서 지원하는 인덱스 2가지 입니다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Global Secondary Index : 기본 키와 관계없이 다른 파키션 키와 정렬 키를 사용하는 인덱스&lt;/li&gt;
&lt;li&gt;Local Secondary Index : 기본 파키션 키는 유지하고 다른 정렬 키를 사용하는 인덱스&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ PK와 SK의 역할 ]&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;DynamoDB 테이블의 기본 키는 아래 두 가지 형태 중 하나로 정의된다:&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;단일 키&lt;/b&gt;: &lt;/span&gt;&lt;span&gt;Partition Key&lt;/span&gt;&lt;span&gt;만 존재 : 완전히 고유한 값으로만 조회 가능&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;복합 키&lt;/b&gt;: &lt;/span&gt;&lt;span&gt;Partition Key + Sort Key&lt;/span&gt;&lt;span&gt; 조합 : 파티션 키로 필터링한 후, 정렬 키로 정렬된 범위 쿼리 가능&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1744339041824&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Table: Users
Partition Key: user_id
Sort Key: position
&amp;rarr; 특정 유저의 포지션 별로 데이터를 쉽게 조회 가능&lt;/code&gt;&lt;/pre&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;h2 data-ke-size=&quot;size26&quot;&gt;[ 기본 쿼리의 한계 ]&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기본 키가 아닌 속성으로는 직접 조회가 &lt;b&gt;불가능&lt;/b&gt;하거나, &lt;b&gt;스캔(Scan)&lt;/b&gt; 연산이 발생합니다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;성능 저하&lt;/b&gt;: 전체 테이블을 읽는 스캔은 매우 비효율적&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;비용 증가&lt;/b&gt;: 읽은 모든 항목에 대해 비용 발생&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;&lt;b&gt;응답 지연&lt;/b&gt;: 필터링이 클라이언트 또는 DynamoDB 내부에서 발생하므로 지연&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;그러므로 인덱스(GSI 또는 LSI)를 설계하여 다양한 쿼리가 가능하게 구현합니다.&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ GIS (Global Secondary Index ) ]&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;GSI 생성&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;AWS DynamoDB Console에서 GSI를 생성합니다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-11 오전 11.53.31.png&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;358&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKzlcn/btsNhj5UHAT/QNaNu7KX5K20CK2Wv3nr3K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKzlcn/btsNhj5UHAT/QNaNu7KX5K20CK2Wv3nr3K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKzlcn/btsNhj5UHAT/QNaNu7KX5K20CK2Wv3nr3K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKzlcn%2FbtsNhj5UHAT%2FQNaNu7KX5K20CK2Wv3nr3K%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;800&quot; height=&quot;290&quot; data-filename=&quot;스크린샷 2025-04-11 오전 11.53.31.png&quot; data-origin-width=&quot;986&quot; data-origin-height=&quot;358&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignLeft&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2025-04-11 오전 11.53.39.png&quot; data-origin-width=&quot;960&quot; data-origin-height=&quot;428&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vqbEX/btsNhPQLwDc/jRfvN81SQzgePm6REC4Fo0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vqbEX/btsNhPQLwDc/jRfvN81SQzgePm6REC4Fo0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vqbEX/btsNhPQLwDc/jRfvN81SQzgePm6REC4Fo0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvqbEX%2FbtsNhPQLwDc%2FjRfvN81SQzgePm6REC4Fo0%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;800&quot; height=&quot;357&quot; data-filename=&quot;스크린샷 2025-04-11 오전 11.53.39.png&quot; data-origin-width=&quot;960&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;속성 프로젝션&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;GSI( 또는 LIS)를 만들 때 선택하는 속성 프로젝션(Attribute Projection)은&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;인덱스에 어떤 데이터를 저장할지 결정합니다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;다시 말해 인덱스만 보고 원하는 데이터를 얻을 수 있는지, &lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&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&gt;예제 테이블 Employee ( PK = emp_id )&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1744340810870&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;aws dynamodb create-table \
  --table-name Employee \
  --attribute-definitions \
      AttributeName=emp_id,AttributeType=S \
      AttributeName=department,AttributeType=S \
  --key-schema AttributeName=emp_id,KeyType=HASH \
  --billing-mode PAY_PER_REQUEST&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;ALL 옵션&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;all 옵션으로 GSI를 생성할 경우 인덱스에 모든 속성이 포함되어 있어서 바로 조회가 가능합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;쿼리만으로 모든 속성 조회가 가능하지만, 인덱스가 커짐에 따라 쓰기 비용이 증가합니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1744340830176&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 생성
aws dynamodb update-table \
  --table-name Employee \
  --attribute-definitions AttributeName=department,AttributeType=S \
  --global-secondary-index-updates \
    '[{
      &quot;Create&quot;: {
        &quot;IndexName&quot;: &quot;GSI_All&quot;,
        &quot;KeySchema&quot;: [{&quot;AttributeName&quot;:&quot;department&quot;,&quot;KeyType&quot;:&quot;HASH&quot;}],
        &quot;Projection&quot;: {&quot;ProjectionType&quot;:&quot;ALL&quot;}
      }
    }]'

# 조회
aws dynamodb query \
  --table-name Employee \
  --index-name GSI_All \
  --key-condition-expression &quot;department = :dept&quot; \
  --expression-attribute-values '{&quot;:dept&quot;: {&quot;S&quot;: &quot;Engineering&quot;}}'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;KEYS_ONLY 옵션&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;GSI로 조회한 속성이 기본키가 아닌 경우에는 한번 더 조회가 필요합니다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;기본키만 저장되므로 저장 효율이 높지만, 추가 조회가 필요할 수 있습니다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1744340916346&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 생성
aws dynamodb update-table \
  --table-name Employee \
  --global-secondary-index-updates \
    '[{
      &quot;Create&quot;: {
        &quot;IndexName&quot;: &quot;GSI_KeysOnly&quot;,
        &quot;KeySchema&quot;: [{&quot;AttributeName&quot;:&quot;department&quot;,&quot;KeyType&quot;:&quot;HASH&quot;}],
        &quot;Projection&quot;: {&quot;ProjectionType&quot;:&quot;KEYS_ONLY&quot;}
      }
    }]'

# 조회
# 1단계: 인덱스에서 기본 키 조회
aws dynamodb query \
  --table-name Employee \
  --index-name GSI_KeysOnly \
  --key-condition-expression &quot;department = :dept&quot; \
  --expression-attribute-values '{&quot;:dept&quot;: {&quot;S&quot;: &quot;Engineering&quot;}}'

# 2단계: 반환된 emp_id로 본 테이블에서 전체 정보 조회
aws dynamodb get-item \
  --table-name Employee \
  --key '{&quot;emp_id&quot;: {&quot;S&quot;: &quot;e001&quot;}}'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;INCLUDE 옵션&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;name과 position은 바로 조회가 가능하며 다른 속성은 key_only옵션처럼 본테이블에서 조회가 필요합니다.&lt;/span&gt;&lt;span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1744341158890&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 생성
aws dynamodb update-table \
  --table-name Employee \
  --global-secondary-index-updates \
    '[{
      &quot;Create&quot;: {
        &quot;IndexName&quot;: &quot;GSI_Include&quot;,
        &quot;KeySchema&quot;: [{&quot;AttributeName&quot;:&quot;department&quot;,&quot;KeyType&quot;:&quot;HASH&quot;}],
        &quot;Projection&quot;: {
          &quot;ProjectionType&quot;:&quot;INCLUDE&quot;,
          &quot;NonKeyAttributes&quot;:[&quot;name&quot;, &quot;position&quot;]
        }
      }
    }]'

# 조회
aws dynamodb query \
  --table-name Employee \
  --index-name GSI_Include \
  --key-condition-expression &quot;department = :dept&quot; \
  --expression-attribute-values '{&quot;:dept&quot;: {&quot;S&quot;: &quot;Engineering&quot;}}'&lt;/code&gt;&lt;/pre&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;h2 data-ke-size=&quot;size26&quot;&gt;[ LSI (Local Secondary Index) ]&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;LSI 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저 활동의 로그 테이블&lt;/p&gt;
&lt;pre id=&quot;code_1744423529281&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# Table Name: UserActivityLog
# Partition Key: user_id
# Sort Key: activity_time
# LSI: (Sort Key로 action_type 추가)

aws dynamodb create-table \
  --table-name UserActivityLog \
  --attribute-definitions \
      AttributeName=user_id,AttributeType=S \
      AttributeName=activity_time,AttributeType=S \
      AttributeName=action_type,AttributeType=S \
  --key-schema \
      AttributeName=user_id,KeyType=HASH \
      AttributeName=activity_time,KeyType=RANGE \
  --local-secondary-indexes \
      '[{
          &quot;IndexName&quot;: &quot;ActionTypeIndex&quot;,
          &quot;KeySchema&quot;: [
            {&quot;AttributeName&quot;: &quot;user_id&quot;, &quot;KeyType&quot;: &quot;HASH&quot;},
            {&quot;AttributeName&quot;: &quot;action_type&quot;, &quot;KeyType&quot;: &quot;RANGE&quot;}
          ],
          &quot;Projection&quot;: {
            &quot;ProjectionType&quot;: &quot;ALL&quot;
          }
        }]'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;LSI 조회&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;action_type 기준으로 조회&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- PK + SK 의 조합이 아닌 PK + LSI 키로 조회 가능&lt;/p&gt;
&lt;pre id=&quot;code_1744423565582&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;aws dynamodb query \
  --table-name UserActivityLog \
  --index-name ActionTypeIndex \
  --key-condition-expression &quot;user_id = :uid AND action_type = :atype&quot; \
  --expression-attribute-values '{
    &quot;:uid&quot;: {&quot;S&quot;: &quot;user123&quot;},
    &quot;:atype&quot;: {&quot;S&quot;: &quot;login&quot;}
  }'&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;LSI 주의점&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테이블 생성 시에만 정의 가능 : 반드시 처음에 정의해야 합니다.&lt;/li&gt;
&lt;li&gt;같은 파티션 내 10GB 제한 : 하나의 파티션 키에 대한 총 크기가 10GB를 넘을 수 없습니다.&lt;/li&gt;
&lt;li&gt;파티션 키는 동일 : LSI는 테이블의 PK와 같은 값을 공유합니다.&lt;/li&gt;
&lt;li&gt;쓰기비용 : LSI도 데이터 복제가 필요하므로 쓰기 비용이 증가할 수 있습니다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ GSI와 LIS 차이 ]&lt;/h2&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 140px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style12&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 22.7519%; height: 20px;&quot;&gt;항목&lt;/td&gt;
&lt;td style=&quot;width: 37.6356%; height: 20px;&quot;&gt;GSI&lt;/td&gt;
&lt;td style=&quot;width: 39.6124%; height: 20px;&quot;&gt;LSI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 22.7519%; height: 20px;&quot;&gt;Partition Key&lt;/td&gt;
&lt;td style=&quot;width: 37.6356%; height: 20px;&quot;&gt;기본 테이블과 달라도 됨&lt;/td&gt;
&lt;td style=&quot;width: 39.6124%; height: 20px;&quot;&gt;기본 테이블의 PK와 같고 SK만 다르게 지정&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 22.7519%; height: 20px;&quot;&gt;생성 시점&lt;/td&gt;
&lt;td style=&quot;width: 37.6356%; height: 20px;&quot;&gt;언제든지&lt;/td&gt;
&lt;td style=&quot;width: 39.6124%; height: 20px;&quot;&gt;테이블 생성시에만&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 22.7519%; height: 20px;&quot;&gt;저장 위치&lt;/td&gt;
&lt;td style=&quot;width: 37.6356%; height: 20px;&quot;&gt;테이블과 별로도 저장&lt;/td&gt;
&lt;td style=&quot;width: 39.6124%; height: 20px;&quot;&gt;기본 테이블과 같은 파티션에 저장 (10GB 제한)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 22.7519%; height: 20px;&quot;&gt;처리량&lt;/td&gt;
&lt;td style=&quot;width: 37.6356%; height: 20px;&quot;&gt;GSI 별도로 설정 또는 On-demand로 처리 가능&lt;/td&gt;
&lt;td style=&quot;width: 39.6124%; height: 20px;&quot;&gt;기본 테이블의 처리량을 공유함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 22.7519%; height: 20px;&quot;&gt;쿼리 성능&lt;/td&gt;
&lt;td style=&quot;width: 37.6356%; height: 20px;&quot;&gt;다양한 키 조합 쿼리 가능&lt;/td&gt;
&lt;td style=&quot;width: 39.6124%; height: 20px;&quot;&gt;같은 PK내에서 다른 SK로 조회&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 20px;&quot;&gt;
&lt;td style=&quot;width: 22.7519%; height: 20px;&quot;&gt;비용&lt;/td&gt;
&lt;td style=&quot;width: 37.6356%; height: 20px;&quot;&gt;GSI 당 별도 비용&lt;/td&gt;
&lt;td style=&quot;width: 39.6124%; height: 20px;&quot;&gt;추가 비용 없음&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;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;[ 적용 예시 ]&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블 스키마&lt;/p&gt;
&lt;pre id=&quot;code_1744425394469&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PK: user_id
SK: timestamp&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 1) 특정 유저의 특정 활동 유형만 조회&lt;/p&gt;
&lt;pre id=&quot;code_1744425480337&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;LSI: activity_type 로 지정

조회시 아래 키 사용
PK = user_id, 
SK = activity_type&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시 2) 특정 직무에서 월급이 높은 순으로 조회&lt;/p&gt;
&lt;pre id=&quot;code_1744425517238&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;GSI: position, salary 지정

조회 시 아래 키 사용
PK=position, 
SK=salary&lt;/code&gt;&lt;/pre&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>Platform/AWS</category>
      <category>AWS</category>
      <author>wave35</author>
      <guid isPermaLink="true">https://wave35.tistory.com/212</guid>
      <comments>https://wave35.tistory.com/212#entry212comment</comments>
      <pubDate>Fri, 11 Apr 2025 12:17:55 +0900</pubDate>
    </item>
  </channel>
</rss>