<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Daily Tech Notes</title>
    <link>https://hazel-developer.tistory.com/</link>
    <description>매일매일 개발하고 기록하기</description>
    <language>ko</language>
    <pubDate>Fri, 10 Apr 2026 23:33:33 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>Hazel_song</managingEditor>
    <image>
      <title>Daily Tech Notes</title>
      <url>https://tistory1.daumcdn.net/tistory/4330646/attach/f7c3989c3a1e408f8bb01b5fc062cc16</url>
      <link>https://hazel-developer.tistory.com</link>
    </image>
    <item>
      <title>[Data Governance]데이터 거버넌스란? 개념에 대한 간단한 정리</title>
      <link>https://hazel-developer.tistory.com/311</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Intro&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 거버넌스..! 요즘 참 은근하게 많이 들리는 개념이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거버넌스라는 용어만 잘라서 정의를 살펴보면 &quot;관리, 통합, 정책&quot; 등의 용어와도 관련이 있어 보인다.&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;&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;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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;데이터의 품질이 좋다는 것&lt;/span&gt;은 어떤 의미일까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;바로 원천에서 부터 로드되는 다양한 저장소까지 데이터가 가공되고 흘러가면서도 &lt;span style=&quot;background-color: #f6e199;&quot;&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;데이터분야에서 일하게되면 알수 있듯이, 데이터는 서비스에서 생성되어 서버에 저장된 그대로가 바로 분석 등의 다양한 용도로 활용되는 것이 아니다.&amp;nbsp; 그 목적에 맞게 여러 파이프라인을 거쳐 가공되고 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 과정에 대한 정책과 관리가 이루어 지지 않는다면 데이터가 어느순간 잘못 전달될 수도 있고, 잘못 전달된 데이터를 파악하려 할때 어디가 원인인지 파악하기 어려워서 해결에도 많은 시간이 소요된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&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;그냥 테이블을 삭제하고 파이프라인을 정리하면 될것 같지만, 해당 데이터들의 파이프라인은 점차 단계를 거칠수록 서로 엮이고 엮여서 1개의 마트테이블이 되곤 한다.&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;h4 data-ke-size=&quot;size20&quot;&gt;데이터 보안 측면&lt;/h4&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 거버넌스는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;이러한 보안적 이슈를 반영하여 회사 내 데이터 관련 정책을 고민하고 운영하는 것&lt;/span&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 정책을 통해 데이터에 접근이 가능하도록 하여 회사 내 데이터에 대한 접근을 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자칫 사람들이 데이터에 접근하기 어렵도록 제한을 두는 것이 데이터 거버넌스로 여겨질 수 있는데, 오히려 반대이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&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;size18&quot;&gt;사례&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 일배치를 통해 소스 데이터를 전달할때는 개인정보는 아예 null 처리 하여 익명데이터로 제공하는 경우가 꽤 있다.&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;&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, &lt;b&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;데이터와 관련하여 파편화된 많은 이슈들(보안, 수집/가공/적재의 라이프사이클, 접근 권한 등)을, 하나의 프로세스로 모아서 중앙화하여 관리하는 것이 데이터 거버넌스&lt;/span&gt;&lt;/b&gt;라고 할 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;데이터의 수집(생성)부터 사용, 그리고 폐기에 이르는, 데이터의 모든 과정을 관리하며 관련된 정책을 설정&lt;br /&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;테이블 생성자/소유자는 누구?&lt;/li&gt;
&lt;li&gt;생성, 수정, 폐기 시의 프로세스&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터의 모든 과정에서, 사용자의 활용(접근부터 수정까지)을 관리
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;누가 어느 데이터에 접근가능한지&lt;/li&gt;
&lt;li&gt;&amp;nbsp;어떤 데이터는, 어떤 관리방안이 필요한지&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터의 보안 이슈를 파악하고 정책을 수립 및 관리&lt;/li&gt;
&lt;li&gt;모든 데이터의 라이프사이클 내에서의 각 관계 파악 - 데이터 리니지&lt;/li&gt;
&lt;li&gt;데이터 활용을 위한 데이터 디스커버리(카탈로그) 관리&lt;/li&gt;
&lt;/ol&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;a href=&quot;https://www.ciokorea.com/news/200624&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;해당 글&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;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;중요한 데이터 요소를 식별&lt;/span&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;/ul&gt;
&lt;/li&gt;
&lt;li&gt;데이터 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;라이프 사이클 전체를 위한 정책과 절차를 설정&lt;/span&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;li&gt;거버넌스 프로세스에 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;비즈니스 사용자를 참여&lt;/span&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;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;마스터 데이터 관리&lt;/span&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 data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;outro&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;</description>
      <category>DE/Study</category>
      <author>Hazel_song</author>
      <guid isPermaLink="true">https://hazel-developer.tistory.com/311</guid>
      <comments>https://hazel-developer.tistory.com/311#entry311comment</comments>
      <pubDate>Tue, 2 May 2023 22:09:24 +0900</pubDate>
    </item>
    <item>
      <title>[데이터 중심 애플리케이션 설계] CH 03. 저장소와 검색(2)</title>
      <link>https://hazel-developer.tistory.com/310</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Point&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;OLTP와 OLAP&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;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;초창기 비즈니스 데이터 처리는 판매, 발주, 급여 발주와 같은 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;커머셜 트랜잭션 즉 상거래&lt;/span&gt;와 같은 데이터 쓰기가 대부분&lt;/li&gt;
&lt;li&gt;데이터베이스 처리가 확장되었어도 트랜잭션이란 용어는 그대로 사용되어 이제는 논리 단위 형태로서 읽기와 쓰기 그룹을 나타낸다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;트랜잭션이 반드시 ACID(원자성, 일관성, 격리성, 지속성)을 가질 필요는 없다. 트랜잭션 처리는 주기적으로 수행되는 일괄처리 작업과 달리 클라이언트가 지연시간이 낮은 읽기와 쓰기를 가능하게 한다는 의미이다. &lt;br /&gt;&lt;br /&gt;즉 주기적으로 스케줄처리가 되는것과 달리 요청에 따른 응답을 최대한 빠르게(실시간에 가깝게)처리하는 것이 트랜잭션 처리이다.&lt;br /&gt;ACID는 7장에서 일괄처리는 10장에서 다룰 예정이다&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;OLTP VS OLAP&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;이전에 OLTP와 OLAP에 대해 간단히 정리한 블로그글 : &lt;a style=&quot;color: #000000;&quot; href=&quot;https://hazel-developer.tistory.com/254&quot; data-token-index=&quot;0&quot;&gt;&lt;span&gt;OLTP VS OLAP 기본비교&lt;/span&gt;&lt;/a&gt;&lt;/i&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;우선 OLTP와 OLAP는 데이터 처리 방식에 대한 개념이다.&lt;/span&gt;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span&gt;OLTP : online transaction processing&lt;/span&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;span style=&quot;background-color: #f6e199;&quot;&gt;해당 사용자에 대한 정보를 삽입/갱신하는 데이터 처리&lt;/span&gt;가 주로 이루어진다&lt;/li&gt;
&lt;li&gt;이런 데이터를 처리하는 애플리케이션은 주로 대화식이며, 이러한 접근 패턴을 OLTP라고 한다.&lt;/li&gt;
&lt;li&gt;OLTP시스템은 실제 서비스 운영에 이용되므로, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;높은 가용성과 낮은 지연시간의 트랜잭션 처리&lt;/span&gt;를 기대한다.&lt;/li&gt;
&lt;li&gt;이런 상황에서 대량의 데이터를 불러오는 분석질의는 OLTP의 성능을 저하한다.&lt;/li&gt;
&lt;li&gt;OLTP는 로우지향방식으로 데이터를 배치한다. 테이블에서 특정 사용자 즉, pk key값을 기준으로 하나의 로우의 모든 값은 서로 인접하게 저장된다. 이점은 문서 데이터 베이스와 유사하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;OLAP : online analytics processing&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;span style=&quot;background-color: #f6e199;&quot;&gt;분석 시에는 많은 레코드를 한번에 집계&lt;/span&gt;해온다.&lt;/li&gt;
&lt;li&gt;분석 질의는 원시 데이터를 반환하는 것이 아니라 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;많은 수의 레코드를 스캔해서 레코드당 일부 칼럼만 읽어 집계통계를 계산&lt;/span&gt;해야 한다.&lt;/li&gt;
&lt;li&gt;예를 들어 1월의 매장의 총 수익은? 최근 프로모션 기간동안 얼마나 많은 바나나를 판매햇는가 같은 데이터를 추출할때&lt;/li&gt;
&lt;li&gt;OLAP에서 사용되는 칼럼지향 방식은 모든 값을 하나의 로우에 저장하지 않는다. 각 칼럼별로 모든 값을 함께 저장한다. 각 칼럼을 개별 파일에 저장하면 질의에 사용되는 칼럼만 읽고 구분 분석하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-10 오후 8.56.33.png&quot; data-origin-width=&quot;1736&quot; data-origin-height=&quot;430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AeBMv/btr9zNY5Nl7/XLV8uwpEAxkPKyUHM9QMe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AeBMv/btr9zNY5Nl7/XLV8uwpEAxkPKyUHM9QMe0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AeBMv/btr9zNY5Nl7/XLV8uwpEAxkPKyUHM9QMe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAeBMv%2Fbtr9zNY5Nl7%2FXLV8uwpEAxkPKyUHM9QMe0%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;1736&quot; height=&quot;430&quot; data-filename=&quot;스크린샷 2023-04-10 오후 8.56.33.png&quot; data-origin-width=&quot;1736&quot; data-origin-height=&quot;430&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;이렇듯 OLTP시스템은 대개 사업 운영에 중요하기 때문에 일반적으로 높은 가용성과 낮은 지연 시간의 트랜잭션 처리를 기대한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 OLTP 시스템에 분석가가 즉석 분석질의(adhoc analytic query)를 실행하게 되면 해당 쿼리는 매우 비싸기 때문에 중요한 트랜잭션 처리의 성능을 저하시킬 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;분석가들이 OLPT 작업에 영향을 주지않고 마음껏 질의할 수 있도록 존재하는 개별 데이터 베이스가 데이터 웨어하우스&lt;/span&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 웨어하우스는 회사 내의 모든 다양한 OLTP 시스템에 있는 데이터의 읽기전용 복사본이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터는 &lt;b&gt;OLTP 데이터 베이스에서 (주기적인 덤프나 지속적인 갱신 스트림을 사용해) 추출(extract)하고 분석 친화적인 스키마로 변환(transform)하고 데이터 웨어하우스에 적재(load)&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;이렇듯 데이터 웨어하우스로 데이터를 가져오는 이 과정을 ETL이라고 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (1).png&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;1224&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zUnXd/btr9zOjlOfl/0SY5cAB5ET7GiHFqeMKNRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zUnXd/btr9zOjlOfl/0SY5cAB5ET7GiHFqeMKNRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zUnXd/btr9zOjlOfl/0SY5cAB5ET7GiHFqeMKNRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzUnXd%2Fbtr9zOjlOfl%2F0SY5cAB5ET7GiHFqeMKNRK%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;1708&quot; height=&quot;1224&quot; data-filename=&quot;Untitled (1).png&quot; data-origin-width=&quot;1708&quot; data-origin-height=&quot;1224&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 소규모 기업은 다양한 OLTP시스템을 가지고 있지 않고 적은 양의 데이터를 가지고 있으므로, 일반적인 SQL 데이터베이스에 질의를 하거나 스프레드 시트에서도 분석이 가능하여 별도의 데이터 웨어하우스가 없는 경우가 많다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 개별 데이터 웨어하우스를 사용한다면 분석 접근 패턴에 맞게 데이터베이스를 최적화할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가령, 색인 알고리즘은 OLTP에서 특정 키에 해당하는 특정 로우를 빠르게 읽는 것에 적합하지, 분석 질의의 응답에서는 그렇게 어울리지 않는다.&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;분석용 스키마 : star schema(별모양 스키마) &amp;amp; snowflake schema(눈꽃모양 스키마)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분석에서는 트랜잭션 처리영역과는 달리 데이터 모델이 그렇게 다양하지 않으며, 대부분의 데이터 웨어하우스는&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;star schema(차원 모델링 - dimension modeling)로 상당히 정형화된 방식을 사용한다. &amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-10 오후 9.21.44.png&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;481&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FX6hq/btr9C5qQVe2/gZSrkK5S5B8KpdzdPTKUm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FX6hq/btr9C5qQVe2/gZSrkK5S5B8KpdzdPTKUm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FX6hq/btr9C5qQVe2/gZSrkK5S5B8KpdzdPTKUm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFX6hq%2Fbtr9C5qQVe2%2FgZSrkK5S5B8KpdzdPTKUm0%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;486&quot; height=&quot;481&quot; data-filename=&quot;스크린샷 2023-04-10 오후 9.21.44.png&quot; data-origin-width=&quot;486&quot; data-origin-height=&quot;481&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예제가 star schema다.&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;스키마 중심에는 fact table이 있고 각 로우는 특정 시각에 발생한 이벤트에 해당한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가령 식료품점에서 a 고객의 b구매가 c 식료품 점에 있었다고 하면, 이러한 이벤트의 사실만 기록되어있는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터베이스 기준으로 본다면 주요 데이터의 키값들이 각 관계에 맞게 하나의 로우에 위치해있다. 해당 키값에 대해 데이터가 자세하게 기재된 각각의 dimension table들이 존재한다.&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;b&gt; fact table에서 가격과 수수료 등의 메인 정보를 제외한 주요 값들은 모두 dimension table의 외래키&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;위의 예시에서 31이라는 product_sk를 관련된 dimension table에서 찾으면 31 키를 가진 상품 데이터가 자세하게 나온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해당 스키마의 변형이 눈꽃송이 스키마다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 star schema의 dimension table에서 더 하위 차원의 다양한 정보가 담긴 dimension table이 추가된 형태다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가령 product 테이블을 브랜드와 상품으로 분리하여, 각 브랜드에 대한 정보를 담긴 하위 dimension table을 만들고 각 product는 brand는 키로 저장하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 &lt;u&gt;점점 더 중복된 데이터를 줄이고 정규화 할 수록 눈꽃송이 스키마&lt;/u&gt;에 가까워지고, 단순화하여 작업하기 용이할수록 star schema가 되며 분석가들은 후자를 선호한다.&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;분석 데이터 베이스에 담긴 테이블들은 보통 100개이상의 컬럼을 가지지만, 실제로 분석 시에는 한번에 4개 또는 5개 칼럼만 접근한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(분석용으로는 select *을 거의 필요하지 않다)&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;대부분의 OLTP로 데이터를 처리하는 데이터베이스는 로우지향 방식으로 데이터를 배치한다.&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;u&gt;특정 키를 기준으로 데이터를 조회할때 모든 칼럼에 대한 특정인의 정보를 빠르게 조회 및 수정할 수 있다.&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 로우지향저장소에서 적은 컬럼이지만 모든 로우의 데이터를 불러들이려면 비효율적이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;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;rarr; 문서데이터 모델인 파케이가 칼럼 저장소 형식이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;칼럼 압축&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업하고자 하는 데이터를 압축해버리면 디스크 처리량이 줄어들 수 있으며 칼럼 지향 저장소는 이러한 압축에 적합하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-10 오후 9.44.06.png&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;355&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5pthr/btr9AteYlRx/MF6zOhtv0CDJqBR6otLmH1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5pthr/btr9AteYlRx/MF6zOhtv0CDJqBR6otLmH1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5pthr/btr9AteYlRx/MF6zOhtv0CDJqBR6otLmH1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5pthr%2Fbtr9AteYlRx%2FMF6zOhtv0CDJqBR6otLmH1%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;482&quot; height=&quot;355&quot; data-filename=&quot;스크린샷 2023-04-10 오후 9.44.06.png&quot; data-origin-width=&quot;482&quot; data-origin-height=&quot;355&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예시를 보면 하나의 컬럼에 중복된 로우값들이 많다. 이 때 압축을 사용하면 매우 효율적인데 데이터 웨어하우스에서 특히 &lt;span style=&quot;background-color: #f6e199;&quot;&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;n개의 고유값을 가진 n개의 개별 비트맵으로 변환하여, 고유값 하나가 하나의 비트맵이고 각 로우는 한 비트를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 예시에서 고유값들을 가져와서 product_sk 칼럼에 29라는 값을 가진 로우는 1, 없으면 0을 주어 해당 고유값에 대한 하나의 비트맵을 만 들수 있다.&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;만약에 n이 더 큰 경우 즉 대부분의 비트맵에 0이 많은 경우, 아래 예시처럼 비트맵을 추가적으로 런 랭스 부호화할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;비트맵 색인(&lt;a href=&quot;https://chaelin1211.github.io/study/2021/06/12/Database-index.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;참고링크&lt;/a&gt;)&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;데이터웨어하우스에서 적합한 색인 방식이다.&lt;br /&gt;비트를 이용하여 컬럼값을 저장하고 이를 이용하여 ROWID를 자동으로 생성하는 인덱스의 한 방법이다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;B트리 인덱스가 가지는 문제를 해결하기 위해 등장했다.&lt;br /&gt;- B트리 인덱스는 실제 컬럼값을 인덱스에도 보관하고 있어야 한다는 점이 대용량 데이터를 관리할 때 부담이 된다.&lt;br /&gt;- 또한 분포도도 좋아야한다.&amp;nbsp;&lt;br /&gt;즉 저장공간의 장비가 B트리 인덱스의 가장 큰 문제이다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;비트맵의 ​​각 비트는 가능한 ROWID에 해당하며, 비트가 설정된 경우 해당 ROWID가있는 행에 키 값이 포함되어 있음을 의미한다.&lt;br /&gt;비트맵 인덱스는 ROWID와 일련의 비트만 저장하기 때문에 B-tree 인덱스보다 작다.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;B-tree Index vs Bitmap Index&lt;/b&gt;&lt;br /&gt;- 카디널리티 차이: 일반적으로 비트맵 인덱스는 중복이 많은(낮은 카디널리티) 열에 사용되는 반면 B-tree 인덱스는 중복이 적은(높은 카디널리티) 열에 가장 적합합니다.&lt;br /&gt;예로, 성별 컬럼처럼 0, 1 두 개의 값으로만 이루어지는 Column의 경우 비트맵 인덱스가 이상적입니다.전화번호나 고객 이름과 같이 중복이 적은 경우엔 B-tree 인덱스가 이상적입니다.&lt;br /&gt;- 내부 구조 차이 : 내부 구조가 상당히 다릅니다. B-tree 인덱스에는 인덱스 노드 (데이터 블록 크기 기준)가 있으며 트리 형식입니다.&lt;br /&gt;- NULL 인덱싱: 일반적인 B-tree의 경우, 모든 열 값이 NULL인 경우 인덱스에서 제외됩니다.위에서 본 비트맵 인덱스의 특징처럼 비트맵 인덱스는 NULL도 인덱싱합니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;카산드라와 HBase같은 빅테이블로부터 내려오는 칼럼 패밀리 개념은 칼럼 지향 저장소와는 다르다. 각 칼럼 패밀리 안에는 로우 키에 따라 로우와 모든 칼럼을 함께 저장하여 칼럼 압축을 사용하지 않는다. 따라서 빅테이블 모델은 대부분 로우 지향이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;메모리 대역폭과 벡터화 처리&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;수백만 로우를 스캔할 때는 &lt;b&gt;디스크로부터 데이터를 가져오는 대역폭이 큰 병목&lt;/b&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;메인 메모리에서 CPU 캐시로 가는 대역폭의 효율적 사용도 필요&lt;/li&gt;
&lt;li&gt;CPU 명령 처리 파이프라인에서 분기예측 실패와 버블을 피해야 한다&lt;/li&gt;
&lt;li&gt;단일 명령 다중 데이터 명령을 사용하게끔 신경써야 한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;칼럼 저장소 배치는 CPU 주기를 효율적으로 사용하기에도 적합하다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;압축된 칼럼 데이터를 CPU의 L1 캐시에 딱 맞게 덩어리로 나누어 가져오는 작업을 &lt;u&gt;&lt;b&gt;타이트 루프(?)&lt;/b&gt;&lt;/u&gt;에서 반복한다
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CPU는 함수 호출이 많이 필요한 코드나 각 레코드 처리를 위해 분기가 필요한 코드보다 타이트 루프를 훨씬 빨리 수행한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;칼럼 압축으로 같은 양의 L1 캐시에 칼럼의 더 많은 로우를 저장할 수 있다&lt;/li&gt;
&lt;li&gt;비트 AND나 OR같은 연산자는 압축된 칼럼 데이터 덩어리에 바로 연산할 수 있게 설계 -&amp;gt; 벡터화 처리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;칼럼 저장소와 순서정렬&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;칼럼 저장소에서는 로우가 저장되는 순서가 반드시 중요하지 않다. 따라서 삽입된 순서로 저장하는 방식이 가장 쉽다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 각 칼럼을 독립적으로 정렬할수는 없다. &amp;rarr; 이는 당연하다. 독립적으로 정렬하면 연관된 로우를 알수가 없어지기 때문이다.&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;보통 date_key 같은 날짜를 첫번째 정렬 기준으로 삼고, 그 다음에 product_sk 같은 키컬럼을 두번째 정렬기준으로 삼는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 순서 정렬을 하면 칼럼 압축에 도움이 된다. 순서 정렬시에 고유값을 많이 포함하는 경우가 아니라면 중복된 값이 연속으로 드러날 것이고 비트맵 부호화 압축을 효율적으로 수행할 수 있다. 이러한 압축 효과는 첫번째 정렬키에서 가장 강력하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;칼럼 지향 저장소에 쓰기&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 작업은 분석가가 수행하는 읽기 전용 질의이며 이에 따라 쓰기작업이 어렵다는 단점이 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 쓰기 최적화는 합리적이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;B트리는 압축된 칼럼에서는 불가능하다. 정렬된 테이블의 중간에 있는 로우에 삽입을 원하는 경우 모든 칼럼 파일을 재작성해야한다.&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;이때 LSM 트리를 활용할 수 있다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;집계: 데이터 큐브와 구체화 뷰&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 데이터 웨어하우스가 칼럼 저장이 필수는 아니지만 분석 질의에 대해 상당히 빠르기때문에 급속하게 인기를 얻는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 웨어하우스의 다른 측면으로 구체화 집계가 있다.&lt;/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;데이터 베이스는 보통 이러한 작업을 자동으로 수행한다. 하지만 이런 갱신으로 인한 쓰기 비용은 비싸서 OLTP에서는 자주사용하지 않는다.&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;데이터 큐브 혹은 OLAP큐브라고도 알려져있는 구체화 뷰의 사례중 하나는 n차원의 테이블이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2023-04-10 오후 10.27.10.png&quot; data-origin-width=&quot;485&quot; data-origin-height=&quot;253&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cXOS7w/btr9DUo5XtO/NdwGbncnPAMrMC6O2lww8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cXOS7w/btr9DUo5XtO/NdwGbncnPAMrMC6O2lww8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cXOS7w/btr9DUo5XtO/NdwGbncnPAMrMC6O2lww8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcXOS7w%2Fbtr9DUo5XtO%2FNdwGbncnPAMrMC6O2lww8k%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;485&quot; height=&quot;253&quot; data-filename=&quot;스크린샷 2023-04-10 오후 10.27.10.png&quot; data-origin-width=&quot;485&quot; data-origin-height=&quot;253&quot;/&gt;&lt;/span&gt;&lt;/figure&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;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;OLTP VS OLAP
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OLTP는 대량의 요청을 받은, 사용자 대면, 작은 수의 레코드만 질의에서 다룸, 일부 키에 대한 데이터를 조회하므로 색인을 사용&lt;/li&gt;
&lt;li&gt;OLAP는 최종적으로 비즈니스 분석가가 사용. 질의가 매우 복잡하고 많은 양을 다룬다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;OLTP 측변에서 주요한 두가지 관점을 살펴봄
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;로그 구조화 관점에서 SS 테이블, LSM 트리 등
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;최근에 개발. 임의 접근 쓰기를 체계적으로 디스크에 순차쓰기로 바꾼 것&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;제자리 갱신관점에서 덮어쓰기할수있는 B트리&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;OLAP는 색인을 사용하지 보다는 질의가 디스크에서 읽는 데이터의 양을 최소화하기 위해 데이터를 작게 부호화하는 일이 중요
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;칼럼지향 저장소가 도움&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>WIR(What I Read)</category>
      <author>Hazel_song</author>
      <guid isPermaLink="true">https://hazel-developer.tistory.com/310</guid>
      <comments>https://hazel-developer.tistory.com/310#entry310comment</comments>
      <pubDate>Tue, 11 Apr 2023 21:38:06 +0900</pubDate>
    </item>
    <item>
      <title>[Apache Spark]Spark Shuffle에 대해서</title>
      <link>https://hazel-developer.tistory.com/309</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Intro&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전에 두번이나 spark에 대해서 글을 정리 했었는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;background-color: #f1f1f1; color: #444444; text-align: center;&quot; href=&quot;https://hazel-developer.tistory.com/307&quot;&gt;[Apache Spark]Join Strategy(조인 수행 전략/방식)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a style=&quot;background-color: #f1f1f1; color: #444444; text-align: center;&quot; href=&quot;https://hazel-developer.tistory.com/308&quot;&gt;[Apache Spark]Spark Partition에 대한 간단한 정리&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;해당 개념에 대해서 정리하다보니 spark shuffle에 대해 자주 언급이 되길래 개인적으로 이 개념에 대해서 내가 얼마나 이해를 하고 있는걸까? 라는 생각이 들면서 한번 정리할 필요성을 느꼈다.&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;어째 다시 back to basic 하는 느낌이 들기는 하지만, 정석대로 차차 공부해가기엔 너무나 공부할 양이 많은 spark에 대해서 이렇게 관련된 개념들에 대해서 추가적으로 정리해가면서 공부하는 것도 나쁘지는 않겠다는 생각도 들었다.&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;이번에는 spark의 shuffle에 대한 개념에 대해서 정리해보려 한다.&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;h3 data-ke-size=&quot;size23&quot;&gt;Spark Shuffle이란?&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (8).png&quot; data-origin-width=&quot;1188&quot; data-origin-height=&quot;638&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/RrYfr/btr8UUXX4e2/EqXqe2Q00WzVR6saT4q8Dk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/RrYfr/btr8UUXX4e2/EqXqe2Q00WzVR6saT4q8Dk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/RrYfr/btr8UUXX4e2/EqXqe2Q00WzVR6saT4q8Dk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FRrYfr%2Fbtr8UUXX4e2%2FEqXqe2Q00WzVR6saT4q8Dk%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;1188&quot; height=&quot;638&quot; data-filename=&quot;Untitled (8).png&quot; data-origin-width=&quot;1188&quot; data-origin-height=&quot;638&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;shuffle에 대해서 이해하려 한다면 막 섞여있는 카드를 떠올리면된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 팩 내에 들어있는 카드를 순서대로 정리하려고 할때, 카드가 마구잡이로 섞여있다면 우리는 A부터 K까지 하나하나 순서를 정리하고 같은 그룹내의 카드끼리 또 묶어야 할 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇듯 카드를 적절한 순서로 정리하기 위해 카드를 이동시키는 과정이 Shuffle이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;shuffle은 spark에서 데이터를 재분배하는 방법이며,&lt;/li&gt;
&lt;li&gt;효율적인 spark application을 개발하기 위해 상당히 중요한 개념이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (9).png&quot; data-origin-width=&quot;1044&quot; data-origin-height=&quot;422&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUvYxv/btr88rAOW2v/7GvorMQKgtaiZkb48EoyY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUvYxv/btr88rAOW2v/7GvorMQKgtaiZkb48EoyY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUvYxv/btr88rAOW2v/7GvorMQKgtaiZkb48EoyY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUvYxv%2Fbtr88rAOW2v%2F7GvorMQKgtaiZkb48EoyY0%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;1044&quot; height=&quot;422&quot; data-filename=&quot;Untitled (9).png&quot; data-origin-width=&quot;1044&quot; data-origin-height=&quot;422&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Shuffle 을 이해하기 위해서는, reduceByKey 의 작동 방식을 알아야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;reduceByKey 는 동일한 Key 를 가지고 있는, 모든 record 값을 취합하는 작업이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(A, 1), (A, 2), (A, 3) &amp;rarr; (A, 6)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만, Spark 의 분산처리는 파티션 단위로 진행되기 때문에, 동일한 Key 의 모든 record 값을 취합하기 위해선, 동일한 Key 를 가진 튜플 데이터가 전부 같은 파티션에 있어야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서, 모든 튜플 데이터가 여러 클러스터에 분산 저장되어 있을 때, &lt;b&gt;동일한 Key 를 가진 튜플 데이터를 동일한 파티션에 두기 위해, 데이터의 위치를 재조정하는 방법이 Shuffle 이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 가지 예시를 들어보면. 테이블에 전화 통화 기록 목록이 있고 매일 발생한 통화량을 계산한다고 가정 해보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;날짜&amp;rdquo;를 키로 설정하고 각 레코드에 대해 값으로 &amp;ldquo;1&amp;rdquo;을 지정한 다음, 각 키의 값을 합산하여 결과 값을 계산할 수 있을 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만일 데이터가 여러 클러스터에 저장되어 있다면 어떻게 해야 동일한 키의 값을 합산할 수 있을까? 이를 위한 유일한 방법은 같은 키의 모든 값을 동일한 시스템에 두는 것이다. 그런 다음 이 값들을 합치면 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (10).png&quot; data-origin-width=&quot;1249&quot; data-origin-height=&quot;798&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bs2pNg/btr88ssXh4q/hCIS3xkJMyke6BT4j8jhDK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bs2pNg/btr88ssXh4q/hCIS3xkJMyke6BT4j8jhDK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bs2pNg/btr88ssXh4q/hCIS3xkJMyke6BT4j8jhDK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbs2pNg%2Fbtr88ssXh4q%2FhCIS3xkJMyke6BT4j8jhDK%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;1249&quot; height=&quot;798&quot; data-filename=&quot;Untitled (10).png&quot; data-origin-width=&quot;1249&quot; data-origin-height=&quot;798&quot;/&gt;&lt;/span&gt;&lt;/figure&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;filter(), sample(), map(), flatMap() 등의 transformation이 이에 해당하며, 이 경우 Shuffle이 필요 없습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 &lt;b&gt;Narrow Transformation&lt;/b&gt; 이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반면, 서로 다른 파티션으로부터 특정한 값을 기준으로 추출하고 싶은 경우, 그 값을 기준으로 Shuffle이 발생하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;groupByKey(), reduceByKey() 등이 이에 해당하며, 이를 &lt;b&gt;Wide Transformation&lt;/b&gt; 이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1228&quot; data-origin-height=&quot;747&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bErOK5/btr8JZ7XRAr/6Uq2klow81FIk0dC1tDdS1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bErOK5/btr8JZ7XRAr/6Uq2klow81FIk0dC1tDdS1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bErOK5/btr8JZ7XRAr/6Uq2klow81FIk0dC1tDdS1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbErOK5%2Fbtr8JZ7XRAr%2F6Uq2klow81FIk0dC1tDdS1%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;1228&quot; height=&quot;747&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;1228&quot; data-origin-height=&quot;747&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 개의 테이블을 Join 할 때에도 Shuffle 이 발생할 수 있다. 위의 예시 처럼 두 테이블에서 키 값을 기준으로 Join 하게 되면, 동일한 키를 가진 데이터가 동일한 파티션으로 이동한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 이 때, 셔플 되는 데이터의 양이 성능에 영향을 미칠 수 있다. 만일 C의 데이터의 크기가 A보다 훨씬 크다면, C에 대한 작업으로 인해 전체의 수행시간이 오래 걸리게 될 것 이다.&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;Shuffle의 유형&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Map-side 셔플&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것은 네트워크를 통해 데이터를 전송하기 전에 단일 노드 내에서 데이터를 셔플 하는 것을 포함합니다. 이는 특히 데이터가 이미 분할되어 있거나 데이터의 일부만 셔플해야 하는 경우 전체 셔플보다 더 효율적일 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Reduce-side 셔플&lt;/h4&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;Shuffle 성능 최적화&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;&lt;b&gt;[셔플이 발생되는 함수들]&lt;/b&gt;&lt;br /&gt;- groupByKey: 이 연산은 RDD의 각 키에 대한 값을 그룹화하므로 셔플이 필요하다. 이 작업은 특히 키에 대한 데이터가 여러 파티션에 분산되어 있는 경우 비효율적일 수 있다. &lt;br /&gt;- reduceByKey: 이 연산은 RDD의 각 키 값에 함수를 적용하며 각 키의 값을 결합하기 위해 셔플이 필요하다. groupByKey와 달리 reduceByKey는 데이터를 셔플 하기 전에 각 파티션에 로컬로 reduce 함수를 적용하므로 더 효율적일 수 있다. &lt;br /&gt;- aggregateByKey: 이 연산은 RDD의 각 키 값에 집계 함수를 적용하고 각 키의 값을 결합하기 위해 셔플이 필요하다. reduceByKey와 마찬가지로 aggregateByKey는 데이터를 셔플하기 전에 각 파티션에 로컬로 집계 함수를 적용한다. &lt;br /&gt;- sortByKey: 이 작업은 RDD의 요소를 키별로 정렬하는 작업으로, 데이터를 키별로 그룹화한 다음 정렬하기 위해 셔플이 필요하다. &lt;br /&gt;- join: 이 작업은 키를 기준으로 두 개의 RDD를 조인하며, 각 키의 데이터를 한데 모으기 위해 셔플이 필요하다. 이 작업은 셔플 오버헤드 측면에서 가장 비용이 많이 드는 작업이다. &lt;br /&gt;- cogroup: 이 작업은 키를 기준으로 여러 RDD의 값을 그룹화. &lt;br /&gt;- distinct: 이 작업은 RDD에서 중복을 제거하며, 데이터를 키별로 그룹화한 다음 중복을 제거하기 위해 셔플이 필요하다. &lt;br /&gt;- repartition: 이 작업은 RDD의 데이터를 지정된 수의 파티션으로 재분배하며, 이 작업에는 셔플이 필요할 수 있다. &lt;br /&gt;- zip: 이 작업은 길이가 같은 두 개의 RDD에서 요소 쌍을 생성하며, 동일한 인덱스를 가진 요소에서 쌍이 생성되도록 셔플이 필요하다. &lt;br /&gt;- coalesce: 이 작업은 RDD의 파티션 수를 줄이며, 파티션 수를 늘리는 경우 셔플이 필요할 수 있다. &lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;셔플을 유발하는 연산을 염두에 두고 신중하게 사용하여 성능 오버헤드를 최소화하는 것이 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;셔플 성능을 최적화하는 한 가지 일반적인 접근 방식은 groupByKey 대신 reduceByKey 및 aggregateByKey와 같은 연산을 사용하여 보다 효율적인 셔플을 수행하는 것 이다.&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;구성 매개변수 조정: Apache Spark는 셔플 성능을 최적화하기 위해 조정할 수 있는 여러 구성 매개변수(예: 셔플에 사용되는 메모리, 리듀서 수, 사용되는 직렬화 형식. 이러한 매개변수를 조정하면 성능이 향상되고 셔플의 영향을 줄일 수 있다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;Partitioning: 파티션 수는 셔플 성능에 영향을 미칠 수 있다. 데이터가 잘 분할되지 않으면 셔플링이 효율적으로 발생하지 않을 수 있다. 데이터 크기, 클러스터 구성 및 사용 가능한 메모리 양을 기반으로 파티션 수를 신중하게 선택하는 것이 중요하다.&lt;/li&gt;
&lt;li&gt;Memory: 사용 가능한 메모리 양은 또한 임팩트 셔플 성능. Spark는 사용 가능한 메모리를 활용하여 디스크 I/O 양을 줄일 수 있는 메모리 기반 셔플 알고리즘을 사용한다. 따라서 셔플에 사용할 수 있는 메모리 양을 늘리면 성능이 향상될 수 있다.&lt;/li&gt;
&lt;li&gt;Serialization: 직렬화는 데이터를 네트워크를 통해 전송할 수 있는 형식으로 변환하는 프로세스이다. 올바른 직렬화 형식을 선택하면 셔플 성능에 영향을 미칠 수 있다.&lt;/li&gt;
&lt;li&gt;Network: 네트워크 대역폭과 대기 시간은 셔플 작업의 성능에 영향을 미칠 수 있다. 셔플 되는 데이터의 양을 처리할 수 있도록 네트워크를 적절하게 구성하는 것이 중요하다.&lt;/li&gt;
&lt;li&gt;Configuration: Spark에는 셔플 성능을 최적화하기 위해 조정할 수 있는 여러 구성 매개변수가 있다. 예를 들어 spark.shuffle.compress를 true로 설정하면 셔플 단계에서 데이터를 압축하여 네트워크 I/O의 양을 줄일 수 있다.&lt;/li&gt;
&lt;/ul&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;적절한 API 사용: Apache Spark는 다음과 같이 설계된 여러 API를 제공한다.&amp;nbsp;&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;Shuffle과 Partition 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;관련 설정 : spark.sql.shuffle.partitions&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이는 shuffle에 대해 가장 비용이 많이 드는 작업인 join 작업 시에 중요한 설정이다.&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;Partition의 크기가 크고 연산에 쓰이는 메모리가 부족하다면 Shuffle Spill(데이터를 직렬화하고 스토리지에 저장, 처리 이후에는 역 직렬 화하고 연산 재개함)이 일어나게 된다. Shuffle Spill이 일어나면, Task가 지연되고 에러가 발생할 수 있다.&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;Memory Limit Over와 같이, Shuffle Spill도 메모리 부족으로 나타나는데, 보통 이에 대한 대응을 Core 당 메모리를 늘리는 것으로 해결한다. 하지만, 모든 사람이 메모리가 부족하다고 메모리 할당량을 늘린다면, 클러스터가 사용성이 더 떨어지고 작업이 더욱더 실패하게 될 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 Partition의 크기를 결정하는 옵션인 spark.sql.shuffle.partitions를 우선적으로 고려해 설정해야 한다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 무엇보다 가장 중요한 최적화 부분은 코드(쿼리)이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 최적화의 우선순위는 &lt;b&gt;쿼리 &amp;gt; Partition 수 &amp;gt; Core 당 메모리 증가&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;쿼리는 최대한 groupBy로 집계를 한 후 Join을 하고 그다음에 Partition 수를 조절한 다음, 그래도 안된다면 Core 당 메모리를 증가시켜야 한다.&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;Partition 수를 증가시킨다면 Task 수도 늘어나서 실행 시간이 증가될 수 있지만, Shuffle Spill이 일어나지 않도록 한다면 시간이 더 감소된다. 따라서, Shuffle Spill이 일어나지 않게 하는 선인 Shuffle Partition의 크기를 100 ~ 200MB로 설정하는 것이 최적이다.&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;Spark Shuffle Properties&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;spark.shuffle.compress: 엔진이 shuffle 출력을 압축할지 여부를 지정&lt;/li&gt;
&lt;li&gt;spark.shuffle.spill.compress: 중간 shuffle spill 파일을 압축할지 여부를 지정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Shuffle에는 위의 두 가지 중요한 Spark Property 가 있다. 둘 다 기본적으로 값이 &amp;ldquo;true&amp;rdquo;이며, spark.io.compression.codec 압축 코덱을 기본으로한다.&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;이외에도 Spark Shuffle 관련된 Property는 아래의 공식문서에서 확인할 수 있다. &lt;a href=&quot;https://spark.apache.org/docs/latest/configuration.html#shuffle-behavior&quot;&gt;https://spark.apache.org/docs/latest/configuration.html#shuffle-behavior&lt;/a&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;outro&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 shuffle은 파티션을 기반으로 분산작업이 수행되는 분산시스템인 spark에서 있어서 필수불가결한 작업인 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;shuffle에 대한 이해를 바탕으로 이와 밀접한 관련이 있는 파티션과, 메모리 사용에 대해 이해하고 제대로 설정을 해야 spark가 가지는 분산 시스템의 장점을 적극 활용할 수 있을것 같다.&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;Reference&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.notion.so/Spark-79a0a5d9416049f8b1ee6c822d413d96&quot;&gt;[Apache Spark] 스파크의 셔플(Shuffle)에 대하여&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;a href=&quot;https://tech.kakao.com/2021/10/08/spark-shuffle-partition/&quot;&gt;Spark Shuffle Partition과 최적화&lt;/a&gt;&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;a href=&quot;https://wooono.tistory.com/48&quot;&gt;[Spark] Shuffle 이란?&lt;/a&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;a href=&quot;https://swalloow.github.io/spark-shuffling/&quot;&gt;Spark의 Shuffling 이해하기&lt;/a&gt;&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;a href=&quot;https://medium.com/road-to-data-engineering/spark-performance-optimization-series-3-shuffle-104738a83a9e&quot;&gt;Spark Performance Optimization Series: #3. Shuffle&lt;/a&gt;&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;&lt;b&gt;&lt;a href=&quot;https://thedatafreak.medium.com/spark-optimization-reducing-shuffle-9cb2c109e977&quot;&gt;Spark Optimization : Reducing Shuffle&lt;/a&gt;&lt;/b&gt;&lt;/b&gt;&lt;/p&gt;</description>
      <category>DE/Study</category>
      <author>Hazel_song</author>
      <guid isPermaLink="true">https://hazel-developer.tistory.com/309</guid>
      <comments>https://hazel-developer.tistory.com/309#entry309comment</comments>
      <pubDate>Sun, 9 Apr 2023 16:31:28 +0900</pubDate>
    </item>
    <item>
      <title>[데이터 중심 애플리케이션 설계] CH 02. 데이터 모델과 질의 언어</title>
      <link>https://hazel-developer.tistory.com/218</link>
      <description>&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;개발자는 객체나 데이터 구조, 그리고 이러한 데이터 구조를 다루는 API를 모델링한다.&lt;/li&gt;
&lt;li&gt;데이터 구조를 저장할때는 JSON이나 XML 문서, 관계형 데이터베이스 테이블이나 그래프 모델같은 범용 데이터 모델로 표현한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;데이터베이스 소프트웨어를 개발하는 엔지니어는 데이터를 메모리나 디스크 또는 네트워크 상의 바이트 단위로 표현하는 방법을 결정한다.&lt;/li&gt;
&lt;li&gt;그렇다면 더 하위 계층의 하드웨어 엔지니어는 자기장 등의 관점에서 바이트를 표현하는 방법을 알아낸다.&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;각 계층의 엔지니어들은 추상화를 통해 하위계층의 복잡성을 숨겨서 효율적으로 일할 수 있게 한다.&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;가장 잘 알려진 데이터 모델은 SQL이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터는 (테이블이라 불리는) 관계(relation)로 구성되고 각 관계는 순서없는 튜플(row)의 모음이다.&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;이러한 데이터의 관계를 잘 표현하기 위한 관계형 모델은 관계형 데이터베이스 관리 시스템(RDBMS)과 SQL을 통해서 구현되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜잭션처리(은행거래, 항공예약 등)과 일괄처리(고객 송장 작성, 급여지불 등)에서 일반적으로 관계형데이터 베이스 시스템이 사용되었다. 당시 데이터 저장과 질의에 대한 많은 논의가 있었는데 이 RDBMS가 점차 메인이 되었다.&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;NoSQL의 탄생&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2010년대에 등장하기 시작한 해당 데이터 베이스는 특정 데이터베이스를 뜻하는 것이 아니라 관계형 모델의 우위를 뒤집으려 등장한 비관계형 데이터베이스를 의미한다. 즉 Not Only SQL로 해석될 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NoSQL은 아래와 같은 이유로 큰 인기를 얻었다.&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;관계형 모델에서 지원하지 않는 특정 질의 언어를 지원했기 때문에 사용되기 시작했다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제로 애플리케이션의 요구사항은 제각각이므로 사용 사례에 맞는 최적의 기술 선택이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관계형 데이터베이스가 비관계형 데이터스토어와 함께 사용될 수도 있는 것이다. 이를 다중 저장소 지속성(polyglot persistence)라고 한다.&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;이런 모델 사이의 분리를 종종 임피던스 불일치(impedance mismatch)라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ORM 프레임 워크가 이러한 전환 계청을 줄여주었지만 여전히 차이를 보였다.&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;예시로 보여주고 있는 링크드인의 이력서 사례의 경우, 이력서 같은 데이터구조는 모든 내용을 갖추고 있는 문서라서 JSON표현에 매우 적합하다고 볼 수 있다. 대표적으로 몽고DB 같은 문서지향 데이터베이스에서 JSON 데이터 모델을 지원한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 JSON모델이 임피던스 불일치를 줄인다고 생각하지만 데이터 부호화 형식으로서 JSON이 가진 문제도 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그럼에도&lt;u&gt;&lt;b&gt; JSON 표현은 다중 테이블 스키마 보다 더 나은 지역성을 갖는다. 이 의미는 관계형 예제에서는 프로필을 가져오려면 다중 질의를 수행하거나, 난잡한 다중 조인을 수행해야한다. 그러나 JSON 표현에서는 모든 관련정보가 한 곳에 있어서 질의 하나로 충분하다.&amp;nbsp;&lt;/b&gt;&lt;/u&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;다대일 관계와 다대다 관계&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 정보를 여러 곳에서 참조해야 하는 경우, ID를 참조식별자로 사용하게 된다. 따라서 이러한 ID는 쓰기오버헤드와 불일치 즉, 중복과 관련된 위험이 있다. 이러한 중복을 제거하는 일이 데이터 베이스의 정규화 이면에 놓인 핵심 개념이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중복된 데이터를 정규화하려면 다대일 관계가 필요한데, 이러한 관계는 문서 모델에 적합하지 않다. 조인이 쉬운 관계형 데이터 베이스에서는 외래키로 다른 테이블의 로우를 참조할 수 있으나, 문서모델에서는 이러한 조인 지원이 약하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약에 조인없느 문서 모델에 적합한 애플리케이션이라도 기능 추가로 인해서 데이터의 상호 관계성이 강해질수도 있다.&lt;/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;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이력서를 예제로 드는 경우 위와 같은 정보가 추가되어서 참조해야 하는 경우, 다대일 관계성이 필요해진다. 또한 추천서처럼 다대다 관계가 필요할 수도 있다. 이 때&amp;nbsp;다대다 관계를 표현하기위해서는 관계형 데이터베이스만큼 적절한 것도 없다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;문서 데이터베이스는 역사를 반복하고 있나?&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같은 논쟁들로 인해서 문서 데이터베이스와 NoSQL에서 다대다 관계를 표현하는 것에 대한 한계와 그 방법에 대해 논의되었다.&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;실제로 NoSQL 등장 이전에 관계형 데이터베이스가 아닌 IBM의 정보관리 시스템이 있었다. 이는 문서 데이터베이스에서 사용하는 JSON 모델과 비슷하다. 이 또한 일대다관계에서는 잘 동작하지만, 다대다관계 표현은 어려웠고 조인도 지원하지 않았다.&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;네트워크 모델에서는 레코드간 연결은 외래키보다 프로그래밍 언어의 포인터와 더 비슷하다. 레코드에 접근하는 유일한 방법은 최상위 레코드 부터 연결경로를 따라가는 즉 접근경로방법이 있다.&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;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;계층모델이나 네트워크 모델처럼 따라가야할 접근 경로가 없다. 관계형 모델에서는 질의 최적화기는 질의의 어느 부분을 어떤 순서로 실행할지를 결정하고 사용할 색인을 자동으로 결정한다.&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;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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 다대일과 다대다 관계를 표현할때는 관계형과 문서 데이터베이스는 근본적으로다르지 않다. 둘다 관련항목은 고유한 식별자로 참조한다. 관계형은 외래키라 부르고 문서모델에서는 문서참조라 부른다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 식별자를 조인이나 후속 질의를 사용해서 읽기 시점에 확인한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;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;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 문서 모델에도 제한이 있다. 예를 들어 문서 내 중첩 항목을 바로 참조할수는 없어서 &quot;사용자 123의 직위목록의 두번째 항목&quot;과 같이 표현해야한다. 이는 계층 모델에서 접근 경로와 매우 유사하다.&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;또한 이 때 조인을 위해서 애플리케이션 코드에서 데이터베이스에 다중 요청을 만들어 흉내낼 수 있지만 복잡도가 애플리케이션으로 이동할 뿐만 아니라 일반적인 조인보다 더 느리다.&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;&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 대부분의 관계형 데이터 베이스 시스템은 이러한 스키마 변경을 수 밀리초안에 수행하는데 MySQL에서는 이 또한 예외다. 스키마 변경을 위한 Alter table 시에 전체 테이블을 복사한다. 이는 큰테이블 변경 시에 수시간까지 중단시간이 발생한다는 의미이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 스키마는 컬렉션 안의 항목들이 모두 동일한 구조가 아닐때 유리하다. 이렇게 보면 스키마 리스가 득보다 실이 많아보인다. 그러나 모든 레코드가 동일한 구조라서 예상 가능하다면 스키마가 문서화와 구조를 강제하기 위해 유용하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;질의를 위한 데이터 지역성&lt;/h4&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;이와 관련해서 지역성으 ㅣ이점은 한번에 해당 문서의 많은 부분을 필요로 하는 경우에 적용된다고 볼 수 있다.&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;p data-ke-size=&quot;size16&quot;&gt;이러한 지역성은 문서모델에만 국한되지 않는다. 오라클에서는 다중테이블 색인 클러스터 테이블을 통해 지역성을 제공한다. 또한 빅ㄷ테이블 데이터 모델의 칼럼 패밀리 개념의 카산드라나 HBase에서 또한 테이블 내에서 테이블의 중첩된 로우를 가능하게 하는 등의 방식으로 지역성을 가능하게 한다.&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;관계형 데이터 베이스와 문서 데이터 베이스는 서로의 기능을 참고하여 각자 새로운 기능을 추가함으로써 점점 더 비슷해지고 있다.&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;데이터를 질의하기 위한 언어에는 크게 명령형 언어/ 선언형 언어가 두가지가 있다. 명령형 언어는 대부분의 프로그래밍 언어로, 특정 순서로 특정연산을 수행하게끔 컴퓨터에게 명령을 내려서 원하는 값을 얻어내는 것이다. 이와 달리 선언형 언어는 방법이 아니라 알고자 하는 데이터의 패턴, 조건 등을 지정한다.&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;명령형 코드의 경우, 원하는 데이터를 얻기위한 작업 순서를 지정해주고 있다. 따라서 내부적으로 로직을 변경하다가 레코드를 옮길때 원하는 데이터의 순서가 바뀔수도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 SQL같은 선언형 코드는 특정순서를 보장하지 않으므로 순서가 바뀌어도 상관없다.&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;웹에서의 선언형 질의&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 단순히 데이터베이스 질의 언어 뿐 아니라 웹에서도 적용해 볼수 있다. 스타일을 적용하고자 할때, CSS나 XSL을 통해 더 간결하게 문서의 스타일을 지정할 수 있다. 이를 명령형 코드로 구현한 것이 자바스크립트에서 DOM API를 사용한 경우이다.&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;h4 data-ke-size=&quot;size20&quot;&gt;맵리듀스 질의&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵리듀스는 대량의 데이터를 처리하기 위한 프로그래밍 모델이다. 몽고DB와 카우치DB를 포함한 일부 NoSQL 데이터 저장소는 제한된 형태의 멥리듀스를 지원한다. 이 메커니즘은 많은 문서를 대상으로 읽기 전용 질의를 수행할 때 사용한다.&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;몽고DB의 map과 reduce 함수는 수행할 때 약간의 제약사항이 있다. 두 함수는 순수함수여야 한다. 즉 입력으로 전달된 데이터만 사용하고 추가적인 데이터베이스 질의를 수행할 수 없어야 한다. 이런 제약 사항 덕분에 데이터베이스가 임의 순서로 어디서나 이 함수를 실행할 수 있고 장애가 발생해도 함수를 재실행할 수 있다.&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;다대다관계가 매우 일반적이게 된다면 관계형 데이터 모델보다 그래프형 데이터 모델이 나을수도 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프는 두 유형의 객체로 이루어진다. 정점(vertex)(노드나 엔티티라고도 한다)과 간선(edge)(관계나 호라고도 한다)이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적으로 소셜그래프/웹그래프/도로나 철도 네트워크가 그 예이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래프는 동종 데이터에만 국한되지 않으므로 단일 데이터 저장소에 완전히 다른 유형의 객체를 일관성있게 저장할 수 있는 강력한 방법을 제공한다. 가령 페이스북을 예로 들면, 정점은 사람, 장소, 이벤트 등이 될 수 있으며 간선은 어떤 사람이 친구인지 어떤 위치에서 체크인이 발생했는지 등을 나타낸다.&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;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;/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;고유한 식별자&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;여기서 중요한 특징은 아래 세가지다&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;정점은 다른 정점과 간선으로 연결되며 특정 유형과 관련 여부를 제한하는 스키마는 없다&lt;/li&gt;
&lt;li&gt;정점의 유입과 유출 간선을 효율적으로 찾을 수 있고 그래프를 순회할 수 있다.&lt;/li&gt;
&lt;li&gt;다른 유형 관계에 서로 다른 레이블을 사용하면 단일 그래프에 다른 유형의 정보를 저장하면서도 데이터 모델을 깔끔하게 유지할 수 있다.&lt;/li&gt;
&lt;/ol&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;사이퍼는 속성 그래프를 위한 선언형 질의 언어이다.&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;SQL의 그래프 질의&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SQL로도 그래프 데이터를 표현할 수 있으나 어렵다. 관계형 데이터베이스에서는 대개 질의에 필요한 조인을 미리 알고있어야 하는데 그래프 질의에서는 찾고자 하는 정점을 찾기 전에 가변적인 여러 간선을 순회해아한다. 즉 미리 조인수를 고정할 수 없다.&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;트리플 저장소와 스파클&lt;/h4&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;목적어나 서술어는 1. 문자열이나 숫자 같은 원시 데이터타입의 값인 경우, 주어 정점에서 속성의 키, 값과 동등하고 2. 그래프의 다른 정점일 경우, 서술어는 그래프의 간선이고 주어는 꼬리 정점이며 목적어는 머리 정점이다.&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;size18&quot;&gt;&lt;b&gt;시멘틱 웹&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시멘틱 웹은 트리플 저장소와 완전히 독립적이나 많은 사람들이 밀접한 관계가 있다고 생각한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시멘틱 웹이라는 개념은, 웹사이드가 이미 사람이 읽을 수 있는 텍스트와 그림으로 정보를 게시하고 있으니 컴퓨터가 읽게끔 기계가 판독 가능한 데이터로도 정보를 게시하는 건 어떨까라는 개념이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 RDF(Resource Description Framework)라는 개념이 등장하는데, 서로 다른 웹사이트의 데이터가 일종의 전 인터넷 만물 데이터베이스인 데이터 웹에 자동으로 결합할 수 있게 하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 현재는 거의 사용되지 않는 듯 하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;RDF 데이터 모델&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RDF관점에서 서술어는 데이터를 다른 사람의 데이터와 결밯하기 위해서 URL을 사용할수도 있다. 여기서는 단순한 네임스페이스이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;스파클 질의언어&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;RDF데이터 모델을 사용한 트리플 저장소 질의언어다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;[그래프 모델과 네트워크 모델의 차이]&lt;br /&gt;&lt;br /&gt;네트워크 모델은 다른 레코드 타입과 중첩가능한 레코드 타입을 지정하는 스키마가 있다. 그래프 모델은 이러한 제한 없이 모든 정점은 다른 정점으로 가는 간선을 가질 수 있다. 즉 그래프 모델은 변화하는 요구 사항을 쉽게 적용할 수 있는 유연성을 준다.&lt;br /&gt;&lt;br /&gt;네트워트 모델에서 특정 레코드에 도달하는 유일한 방법은 레코드의 접근 경로 중 하나를 탐색하는 것이다. 그래프 모델은 고유 ID를 가지고 임의 정점을 직접 참조하거나 색인을 사용해 특정 값을 가진 정점을 빠르게 찾을 수 있다.&lt;br /&gt;&lt;br /&gt;네트워크모델에서 결국 하위 항목은 정렬된 집합이므로 데이터 베이스는 정렬을 유지해야 한다. 따라서 집합에서 새로운 레코드의 위치를 염두에 두어야 한다. 그래프 모델에서 정점과 간선은 정렬하지 않는다.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;가장 큰 차이는 네트워크 모델 질의는 명령형이다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;초석: 데이터 로그&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;질의 언어의 기반이 되는 초석을 제공했다. 데이터로그의 데이터 모델은 트리플 저장소 모델과 유사하지만 조금 더 일반화 됐다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 로그 접근방식은 질의언어와는 다른 사고가 필요하다. 그러나 다른 질의 규칙을 결합하거나 재사용할 수 있기때문에 강력한 접근 방식이다. 간단한 일회성 질의에 사용하기는 편리하지 않지만 데이터가 복잡하면 더 효과적으로 대처할 수 있다.&amp;nbsp;&lt;/p&gt;
&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;그러다 관계형 모델에도 적합하지 않은 애플리케이션이 있다는 사실을 발견했고 이 것이 NoSQL이다.&amp;nbsp; 이외에도 다양한 데이터 모델이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대표적인 세가지 모델인 문서, 관계형 그래프 모델은 현재 널리 사용되고 있다. 그러나 각각은 단일 만능 솔루션이 아니라서 각기 목적에 맞는 다양한 시스템을 보유해야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문서및 그래프 모델이 가진 공통점 중 하나는 일반적으로 저장할 데이터를 위한 스키마를 강제하지 않아 변화하는 요구사항에 맞춰 애플리케이션을 쉽게 변경할 수 있다는 점이다. 하지만 애플리케이션은 데이터가 특정 구졸르 갖는다고 가정할 가능성이 높다.&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;</description>
      <category>WIR(What I Read)</category>
      <author>Hazel_song</author>
      <guid isPermaLink="true">https://hazel-developer.tistory.com/218</guid>
      <comments>https://hazel-developer.tistory.com/218#entry218comment</comments>
      <pubDate>Mon, 27 Mar 2023 21:48:04 +0900</pubDate>
    </item>
    <item>
      <title>[Apache Spark]Spark Partition에 대한 간단한 정리</title>
      <link>https://hazel-developer.tistory.com/308</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Intro&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지난번에는 spark join에 대해서 정리를 해보았는데, 공부할 때마다 은근 많이 언급되는 개념이 spark partition이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;partiton/partitioning에 대해서 대략적으로는 어떤 느낌인지 이해는 가지만 명확하게 어떤 개념인지 설명하기가 어렵다는 생각이 들어서, 이번에는 spark partition에 대해서 간단하게 정리를 하고 넘어가려 한다.&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;사실 spark partition에 대해서도 깊이 파고들면 끊임없이 공부할 부분이 많은 개념이다보니 이번에는 partition의 개념과 종류에 대해서, 그리고 그 각 파티션에 대한 간단한 개념만 짚고 넘어가려한다.&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;Spark Component&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spark partition에 대해서 이해하고자 할때 먼저 이해하고 넘어가면 좋은 부분이 바로 spark의 구성에 대한 개념이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spark는 대표적인 분산 시스템 중 하나이며 cluster라는 일종의 하나의 컴퓨터가 있다면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 구조로 각각의 resource(memory, storage)를 지닌 node가 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;534&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/QzPq4/btr5PF5jaPp/3DOxzbRRzAdj3WCkVBAGy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/QzPq4/btr5PF5jaPp/3DOxzbRRzAdj3WCkVBAGy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/QzPq4/btr5PF5jaPp/3DOxzbRRzAdj3WCkVBAGy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FQzPq4%2Fbtr5PF5jaPp%2F3DOxzbRRzAdj3WCkVBAGy0%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;534&quot; height=&quot;250&quot; data-origin-width=&quot;534&quot; data-origin-height=&quot;250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;cluster &amp;gt; node &amp;gt; core &amp;gt; executor &amp;gt; task = partition&lt;/span&gt; 으로 세부적으로 나뉘어서 각 작업이 실행된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 알 수 있듯이 partition은 하나의 큰 데이터가 분산되어 작업이 실행되는 가장 작은 단위인 것이다.&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;spark의 component에 대해서는 하단의 공식문서 링크를 참고하면 금방 이해할 수 있을 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;partition을 이해하기 전에 기본적으로 Node, task 등의 내부 구조에 대해 조금은 짚고 넘어가야 이해가 쉬울 것이라 생각해서 간단히만 정리해보았다.&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;Spark Partition이란&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 한번 언급한대로 partition은 하나의 큰 데이터를 다루는 RDD나 Dataset을 구성하고 있는 최소단위 객체이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떻게 보면 분산 시스템인 spark의 전반적인 성능과 리소스 점유량을 좌우할 수 있는 가장 기본적이고 중요한 개념이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;partition이 가장 최소 작업 단위라면, partitioning은 이러한 최소 작업 단위를 쪼개기 위한 작업로직, 매커니즘&lt;/span&gt;을 의미한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spark의 구조에서 알수있듯이 spark 내에서 가장 최소의 연산이 task이며, 이 하나의 task에서 하나의 partition을 처리한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;1 task = 1 partition인 것&lt;/span&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이 task가 하나의 core에서 처리되는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 말인 즉슨, &lt;b&gt;partition이 많다 = task가 많다. core수가 많이 필요함&lt;/b&gt;을 의미하며/&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;반대로 &lt;b&gt;partition이 크다 = 하나의 task와 core에서 이 partition내의 데이터를 처리하기 위한 리소스가 많이 필요하다 = 메모리크기&lt;/b&gt;가 커야함을 의미한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 병렬화의 정도를 늘리고 메모리를 적게 사용하려면 partition의 수를 늘리면 되는 것이다.&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;그러면 메모리등의 리소스를 최소한으로 사용하도록 partition의 수를 최대한으로 설정해주는 것이 좋을까?&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;partition의 수를 너무 많이 설정했다면 과도한 리소스 사용을 일으키게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;너무 많은 partition들이 생성되어 너무 많은 task에서 연산이 실행되는데, 실제로 해당 spark의 각 core와 task가 가진 메모리에서는 더 많은 데이터를 처리할 수 있는데 충분히 활용하지 못하는 반면 너무 많은 task가 실행되게 때문이다.&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;이렇듯, spark partitioning은 정해진 리소스 내에서 데이터를 효율적으로 쪼개서, 동시에 여러 파티션에서 데이터 작업을 병렬적으로 수행하여 작업이 좀 더 빠르게 완료되도록 해준다.&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;기본적으로 spark의 partition은 작업이 이루어지는 클러스터가 지닌 core수와 동일하게 생성된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 2개의 cluster &amp;gt; 각 클러스터는 node를 3개씩 가지고 있고 &amp;gt; 각 node가 4개의 core를 가지고 있다면, 기본적으로 2 * 3 * 4 = 24개의 core를 가지고 있고 partition 또한 24개가 생성된다.&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;그렇다고 core수에 따라서 partition 수가 고정되어있고, 이에 따라 Partition의 수를 조정하려면 core수 자체를 조정해야 하는 것은 아니다. 특정 설정값을 통해서 partition의 수를 조정할 수 있다.(repartition, coalesce)&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;최적화된 partition 수는 데이터의 크기에 따라 다르며, 클러스터 내의 노드의 수, 그리고 각 노드에서 사용할 수 있는 리소스에 따라 다르다.&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;그리고 각 partition에 작업의 최소 연산 단위인 task가 생성된다.&amp;nbsp; 이렇게 쪼개진 각 partition들은 독립적으로 작업이 이루어진다.&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;Spark Partitioning의 종류&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그렇다면 spark에 partition을 생성하는 partitioning은 어떤 방식으로 이루어질까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;partitioning은 크게 두가지 방식이 있다.&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;hash partitioning
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 partitioning에서는 rdd의 각 키에 해시코드를 할당하고, 각 partition이 해당 해시코드를 기반으로 생성되도록 한다.&lt;/li&gt;
&lt;li&gt;따라서 모든 partition들 사이에서 균등한 분산을 보장한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;range partitioning
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해당 partitioning에서는 데이터가 각 값의 범위에 기반한다. 예를 들어 특정 데이터가 일련의 숫자들이라면 spark는 각 숫자들을 범주화하고 각 범위에 기반해서 데이터를 파티셔닝한다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본적으로 spark는 hash partitioning을 사용한다.&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;Spark Partition의 종류&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;partiton이 쓰이는 때, 즉 spark에서 실행하고자 하는 작업의 종류에 따라 partition을 구분할 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;input / output / shuffle partition이며, 각 partiton에 대해서 간단하게 정리해보았다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Input Partition&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;spark.conf.set(&quot;spark.sql.files.maxPartitionBytes&quot;,bytes)&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;처음 파일을 읽을때 생성하는 partition이다.&lt;/li&gt;
&lt;li&gt;하나의 파일(데이터)을 읽을때 각 partition이 해당 데이터에서 어느 정도의 크기(byte)를 담당해서 작업하게 할 것인지를 지정해주는 것이다. 즉 크기를 키울수록 적은 partition을 쓰게 될 것이다.&lt;/li&gt;
&lt;li&gt;기본값은 134217728(128MB)&lt;/li&gt;
&lt;li&gt;hdfs 상의 마지막 경로에 존재하는 파일의 크기가 128mb보다 크다면 스파크에서 128mb만큼 쪼개면서 파일을 읽는다&lt;/li&gt;
&lt;li&gt;파일의 크기가 작다면 그대로 읽어들여 파일 하나당 파티션 하나가 된다.&lt;/li&gt;
&lt;li&gt;file-based source인 json, parquet, orc 같은 것에만 적용된다.&lt;/li&gt;
&lt;li&gt;대부분의 경우, 필요한 칼럼만 골라서 뽑아 쓰기 때문에 파일이 128MB보다 작다. 가끔씩 큰 파일을 다룰 경우에는 이 설정값을 조절한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Output Partition&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span data-token-index=&quot;0&quot;&gt;coalesce(cnt) vs repartition(cnt)&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;Output Partition은 파일을 저장할 때 생성하는 Partition이다.&lt;/li&gt;
&lt;li&gt;이 Partition의 수가 HDFS 상의 마지막 경로의 파일 수를 지정한다.&lt;/li&gt;
&lt;li&gt;즉 partiton의 수를 많이 설정했다면, 이는 각 partition에서 담당하는 파일의 크기가 적다는 의미이다.&lt;/li&gt;
&lt;li&gt;기본적으로, HDFS는 큰 파일을 다루도록 설계되어 있어, 크기가 큰 파일로 저장하는 것이 좋다.&lt;/li&gt;
&lt;li&gt;보통 HDFS Blocksize에 맞게 설정하면 되는데, 일반적으로 Hadoop 클러스터의 HDFS Blocksize는 268435456 (256MB)로 설정되어 있어서, 통상적으로 파일 하나의 크기를 256MB에 맞도록 Partition의 수를 설정하면 된다.&lt;/li&gt;
&lt;li&gt;coalesce는 파티션을 줄일 때 사용하고, repartition()은 파티션 수를 늘리거나 줄일 때 사용한다.&lt;/li&gt;
&lt;li&gt;Partition의 수는 df.repartition(cnt), df.coalesce(cnt)를 통해 설정한다.&lt;/li&gt;
&lt;li&gt;repartition은 각 노드들 간에 데이터를 shuffle 하게 되는데, 이는 파티션의 수를 재생성하기 위해서이다.&lt;/li&gt;
&lt;li&gt;반면에 coalesce는 파티션의 수를 줄이며 이는 각 노드간이 아니라 내에서 데이터를 shuffling 함으로써 이루어진다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;여기서 언급된 데이터 shuffle의 개념에 대해서 간단하게 정리해보려 한다.&lt;br /&gt;spark에서 어떤 작업을 하든 그리고 이에 대해 공부를 한다면 가장 많이 언급되는 개념 중 하나가 shuffle인듯 하다.&lt;br /&gt;spark shuffle의 가장 기본적인 개념은 하나의 파티션에서 다른 파티션으로 데이터를 이동시키는 것이다.&lt;br /&gt;이에 따라 partition을 생성 혹은 재생성 해주는 partitioning은 설정에 따라서 단순히 partition간이 아니라 node간의 데이터 이동을 발생시키는 매우 비싼 작업이다.&lt;br /&gt;기본적으로 df shuffle은 200개의 파티션을 생성한다.&lt;br /&gt;&lt;br /&gt;즉 위에서 언급된 repartition과 coalesce 둘다 파일을 내보내서 저장할 때 partition의 수를 재설정해주는 함수인데,&lt;br /&gt;repartition의 경우는 node들 간의 shuffle을 발생시키고, coalesce는 기존의 partition의 수를 줄이기 위해서 node 내에서 각 partition 간의 shuffle을 발생시킨다는 차이점이 있는 것 같다.&lt;br /&gt;&lt;br /&gt;이는 추후에 한번 더 자세하게 다뤄볼 예정이다&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Shuffle Partition&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spark.conf.set(&quot;spark.sql.shuffle.partitions&quot;,num of partition)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;spark.sql.shuffle.partitions 옵션은 join,groupBy 혹은 aggregation 같은 연산을 할 시 data shuffling되는 파티션 수를 나타낸다.&lt;/li&gt;
&lt;li&gt;파티션의 개수는 default로는 200으로 지정 되어 있으며, spark.conf.set(&quot;spark.sql.shuffle.partitions&quot;, 1800)와 같은 형태로 조정이 가능하다.&lt;/li&gt;
&lt;li&gt;Partition의 크기가 크고 연산에 쓰이는 메모리가 부족하다면&amp;nbsp; Shuffle Spill(데이터를 직렬화하고 스토리지에 저장, 처리 이후에는 역 직렬 화하고 연산 재개함)이 일어난다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이때 Task가 지연되고 에러가 발생 -&amp;gt; 하둡클러스터에 에러가 나고 spark 강제종료되게 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Memory Limit Over와 같이, Shuffle Spill도 메모리 부족으로 나타나는데, 보통 이에 대한 대응을 Core 당 메모리를 늘리는 것으로 해결&lt;/li&gt;
&lt;li&gt;일반적으로, 하나의 Shuffle Partition 크기가 100~200MB정도 나올 수 있도록 수를 조절하는 것이 최적&lt;/li&gt;
&lt;li&gt;클러스터의 익스큐터 수보다 파티션의 수를 더 크게 지정하는 것이 대체로 좋다.&amp;nbsp;executor num &amp;gt; partition num&lt;/li&gt;
&lt;li&gt;로컬 머신에서 처리할 경우 병렬로 처리할 수 있는 task가 제한적이므로 이 값을 작게 설정&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;shuffle partition은 무엇보다 이전에 정리한 join과도 밀접하게 연관된 개념이라고 볼 수 있다.&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;각 partition의 종류에 대해 정리해보니, 결론적으로 spark로 데이터를 다룰 때 크게 세번의 partitioning을 해야하는 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 파일을 읽을 때, 정해진 파일의 크기를 적절하게 분산작업할 수 있도록, 각 분산 작업이 담당할 파일의 크기를 지정해주고,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작업 도중 join, group by 등의 복잡한 연산을 할 시에, 주어진 리소스 내에서 메모리와 core를 최대한 잘 활용할 수 있도록 partition의 수를 재설정 해줘야한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이렇게 변환 작업된 파일을 저장하여 내보낼 때 다시 partition 수를 설정해주어야 하는데, 이땐 변환 작업 완료된 파일의 크기를 잘 파악해서 각 partition이 효율적으로 담당할 수 있는 파일 크기와 partition 수를 잘 지정해주어야 한다. 즉 무작정 partition의 수를 많게 한다고 해서 효율적인 것은 아니다.&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;Outro&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 글또 활동을 하며 공부하고 정리해볼 큰 주제를 spark로 잡았다고 했을 때 주변에서 공부할게 엄청 많을거라고 하더니,&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;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 의미에서 다음에는 shuffle 혹은 repartitioning에 대해서 정리해보려 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무래도 저번에 정리한 join과도 관련되며 이번에 정리한 partition을 더 깊게 이해하고 활용하는데 있어서 위의 두 개념 중 하나를 이해하는 것이 중요하다는 생각이 들었기 때문이다.&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;h3 data-ke-size=&quot;size23&quot;&gt;Reference&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://spark.apache.org/docs/1.1.0/cluster-overview.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;spark component(공식문서)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://tech.kakao.com/2021/10/08/spark-shuffle-partition/&quot;&gt;spark shuffle partition과 최적화&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://spidyweb.tistory.com/312&quot;&gt;spark partition 개념&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sparkbyexamples.com/spark/spark-partitioning-understanding/&quot;&gt;Spark Partitioning &amp;amp; Partition Understanding&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>DE/Skill</category>
      <author>Hazel_song</author>
      <guid isPermaLink="true">https://hazel-developer.tistory.com/308</guid>
      <comments>https://hazel-developer.tistory.com/308#entry308comment</comments>
      <pubDate>Sun, 26 Mar 2023 16:25:00 +0900</pubDate>
    </item>
    <item>
      <title>[데이터 중심 애플리케이션 설계] CH 01. 신뢰할 수 있고 확장 가능하며 유지보수하기 쉬운 애플리케이션</title>
      <link>https://hazel-developer.tistory.com/215</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;요즘 대부분의 애플리케이션은 계산중심(CPU 성능)이 아닌 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;데이터 중심(데이터 복잡도, 변화 속도 등)&lt;/span&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, CPU성능은 더이상 애플리케이션의 동작을 제한하는 요소가 아니며, 이제는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;데이터의 양, 복잡도, 변화속도 그리고 이를 처리할 수 있는 방안과 기술&lt;/span&gt;이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 데이터 중심 애플리케이션은 기본적으로 &quot;데이터베이스. 캐시, 검색 색인, 스트림처리, 일괄처리&quot; 등을 필요로 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이들을 데이터 시스템이라고 묶으며, 이러한 데이터 시스템의 활용에 대한 고민과 구조 설계는 애플리케이션 서비스 개발자들 또한 고민해야 하는 사항이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(가령 클라이언트에서 A라는 정보에 대한 조회를 요청했을때, 실제 서버에서는 해당 api를 받고난 이후에, 캐시를 확인하고 데이터가 있으면 반환, 없으면 메인DB를 조회 하는 등의 다양한 데이터 시스템을 거친다.)&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;blockquote data-ke-style=&quot;style3&quot;&gt;책에서는 대표적으로 redis와 kafka를 언급하고 있다.&lt;br /&gt;redis와 kafka 둘다 큰 범위에서는 끊임없이 들어오는 데이터를 누락없이 지정한 순서에 맞게 처리하기 위한 메시지 큐이다.&lt;br /&gt;그러가 redis는 일시적으로 데이터를 처리하고, kafka는 데이터를 영구적으로 저장하는 데이터베이스처럼 영구성을 보장한다.&lt;br /&gt;즉 같은 역할을 하면서도 다른 역할을 하는 데이터 처리 기술과 점점 영역 구분이 흐려지면서 필요에 따라 여러 기능을 하는 새로운 데이터 처리 기술들이 등장하고 있다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 복잡성이 높아지면서 단일 도구로 전체 데이터 처리를 감당할 수 없으며, 각 도구들을 결합하여 데이터 처리를 구현하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 개발자는 단순히 데이터 처리를 위한 애플리케이션을 개발하는 것에서 넘어서서 다양한 데이터 처리 도구들을 적절하게 활용하는 방안을 고민하며 데이터 시스템을 설계하는 설계자 역할을 해야한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 시스템에서 가장 중요한 세가지 관심사가 있다. 신뢰성, 확장성, 유지 보수성이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;신뢰성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;소프트웨어 시스템에서 신뢰성은 애플리케이션이 &quot;무언가 잘못되더라도 지속적으로 올바르게 동작함&quot;을 의미한다.&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;span style=&quot;background-color: #f6e199;&quot;&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;여기서 결함은 잘못될 수 있는 일을 뜻하며, 이를 대처할 수 있는 시스템을 내결함성 혹은 탄력성을 지녔다고 한다. 실제로 내결함성은 100프로를 달성할 수 없다.(책에서 언급되는 최악의 상황 즉, 블랙홀이 지구상의 모든 서버를 삼키는 경우에도 잘못되지 않을 내결함성은 불가하다)&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;또한 결함과 장애는 다르다. 결함은 사양에서 벗어난 시스템의 한 구성요소로 정의되나, 장애는 사용자에게 필요한 서비스를 제공하지 못하고 시스템 전체가 멈춘 경우다. 즉 결함확률을 0으로 줄이는 것은 불가하나, 장애가 발생하지 않게끔 최대한 구조를 설계해야한다.&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;이는 즉 신뢰할 수 없는 여러 부품(결함확률이 0일 수 없는)으로 신뢰할수있는 시스템을 구축(장애가 최대한 발생하지 않게)해야하는 것이다.&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;하드웨어 결함&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하드웨어 결함에 의한 문제를 예방하기 위해서는 중복을 추가 하는 방법 즉, 하드웨어를 여러개 두는 것이 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디스크는 RAID구성으로, 서버는 이중전원 디바이스와 핫 스왑 가능한 CPU를, 데이터 센터는 건전지와 예비 전원용 디젤 발전기를 갖출 수 있다.&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;이에 따라 소프트웨어 내결함성 기술과 하드웨어 중복으로 시스템을 설계하고 있다.&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;보통 하드웨어 결함은 서로 독립적이다.&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;대표적으로 CPU, 메모리 등 공유자원을 과도하게 사용하는 일부 프로세스가 발생한 경우, 한 구성 요소의 작은 결함이 다른 구성요소의 결함을 야기하여 더 많은 결함을 발생시키는 연쇄 장애 등이 있다.&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;h4 data-ke-size=&quot;size20&quot;&gt;인적오류&lt;/h4&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;신뢰성은 얼마나 중요할까?&lt;/h4&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;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;&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;확장성을 위해서는 우선 현재 시스템의 부하를 간결하게 기술할 수 있어야 한다. 이를 측정하기 위한 지표를 부하 매개변수라고 한다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;책에서는 트위터를 예로 들고 있다. 트위터의 확장성 문제는 트윗양이 아니라 바로 팬아웃 때문이었다. 즉 개별 사용자는 많은 사람을 팔로우하고, 많은 사람이 개별사용자를 팔로우 하면서 발생하는 성능 부하가 이슈였다.&lt;br /&gt;이들의 쓰기 작업과 읽기 작업 시에 부하를 다루는 것이 중요했고, 실제로 쓰기보다는 읽기 작업 요청이 더 많으므로,&amp;nbsp;&lt;br /&gt;쓰기 작업에서 더 많은일을하고 읽기 작업에서 적은 일을 하도록 시스템을 설계했다.&lt;br /&gt;&lt;br /&gt;그러나 결국 각 개인의 팔로워 수마다 실질적인 부하원인이 다르고 이를 결정짓는 팔로우수가 부하 매개 변수가 되며,&amp;nbsp;&lt;br /&gt;결국 혼합형을 택했다.&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;성능기술하기&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 현재 시스템의 성능 또한 기술 할수 있어야 하는데, 대표적으로 처리량(하둡과 같은 일괄 처리 시스템은 초당 처리할 수 잇는 레코드수나 일정 크기의 데이터집합으로 작업을 수행할때 걸리는 전체 시간)에 관심을 가지며, 온라인 시스템에서는 서비스 응답시간 즉 클라이언트가 요청을 보내고 응답을 받는 사이의 시간을 중요하게 여긴다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;지연시간과 응답시간은 차이가 있다. 응답 시간은 클라이언트 관점으로 요청을 처리하는 실제 시간 외에도 네트워크 지연과 큐지연도 포함한다. 지연시간은 서버에서 요청을 받은 후 처리되길 기다리는 시간이다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;응답시간은 같은 요청이라도 항상 다르므로 단일값이 아니라 분포로 표현한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 서비스 평균 응답시간을 구하기 위해서는 실질적인 평균을 구하는 것보다 백분위의 중앙값을 구하는 것이 더 실제 유저의 경험을 반영하기에 좋다. 일반적인 산술평균은 얼마나 많은 사용자가 실제로 지연을 경험했는지 알려주지 않기 때문이다.&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;여기서 사용자 실제 경험에 중요한 것은 꼬리 지연시간이란 개념이다. 상위 백분위 응답시간으로 실제 사용자 표본의 95분위, 99분위 , 99.9분위의 응답시간을 살펴보는 것이다. 가령 95분위를 기준으로 한다치면, 95분위 응답시간이 1.5초이면 95프로가 1.5초 미만의 응답시간이 걸리고, 나머지 5%가 1.5초보다 응답시간이 더 걸린다는 의미이다. 아마존의 경우는 99.9분위로 서비스 응답시간 요구사항을 기술하여 까다롭게 응답시간을 관리한다.&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;(기존에는 용량확장을 기반으로 하나의 노드에 데이터베이스를 돌리는 것이 대부분이었다.)&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;유지보수에 드는 비용도 크기 때문에, 이러한 비용을 최소화 하기 위해 소프트웨어 설계부터 신경써야 한다.&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;운영의 편리함 만들기, 즉 자동화하기. &quot;좋은 소프트웨어라도 나쁘게 운영할 경우 작동을 신뢰할 수 없다.&quot; 즉 운영 중 일부 측면은 자동화하여 효율적인 운영을 꾀해야한다.&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;복잡도 관리, 이를 위해 중요한 도구 중 하나는 추상화이다. 좋은 추상화는 깔끔하고 직관적인 외관 아래로 많은 세부 구현을 숨길 수 있다. 또한 재사용성을 높여준다.&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;변화하기 쉽게 만들기. 즉 시스템의 요구사항이 끊임없이 변화할 수 있으므로 이에 유연하게 대응할 수 있도록 설계해야한다.&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>WIR(What I Read)</category>
      <author>Hazel_song</author>
      <guid isPermaLink="true">https://hazel-developer.tistory.com/215</guid>
      <comments>https://hazel-developer.tistory.com/215#entry215comment</comments>
      <pubDate>Tue, 21 Mar 2023 22:20:00 +0900</pubDate>
    </item>
    <item>
      <title>[Apache Spark]Join Strategy(조인 수행 전략/방식)</title>
      <link>https://hazel-developer.tistory.com/307</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;Intro&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 작년 말부터 업무적으로 spark를 많이 사용하게 되면서, 기존에 대략적으로만 이해하고 있던 spark에 대해서 이제 본격적으로 공부할 때가 되었다고 생각했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spark 공부의 정수인 [spark 완벽가이드]를 읽으면서 업무를 하던 와중에, spark join을 유독 많이 사용하게 되었고 이에 대해서 좀 더 자세하게 이해할 필요가 있었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 spark 중에서도 join, 그리고 이 join에는 어떤 전략들이 존재하는지를 공부해보았다.&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;일단 본인이 spark로 업무를 하면서 대부분의 작업들이 join인데, 무작위로 작업하면서 OOM에러도 많이 발생시키고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;s&gt;심지어 cluster들을 수없이 죽여서 cluster 살인마라는 별명까지 얻기에 이르른... &lt;/s&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;따라서 이번글에서는 spark에서 join을 하기 위해 어떤 방식들을 선택할 수 있는지 각 방식들에 대한 설명을 정리해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;u&gt;이번 글은 spark에 대해 엄청 어려운 개념을 설명한 글은 아니지만, 그렇다고 완전 기초적인 글도 아니므로,&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;u&gt;읽기전에 어느정도 spark에 대한 기초지식이 필요할 것 같다.&lt;/u&gt;&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;Spark Join&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단하게 spark join에 대해서 이야기해보자면 왼쪽과 오른쪽에 있는 각 데이터셋에 있는 하나 이상의 키값을 비교하는 것이고, 이를 통해 두 데이터 셋을 하나로 합쳐서 새로운 데이터셋을 만드는 것이다.&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;spark에서 join 작업을 하기 위해서는 사전에 크게 두가지를 고민하고 결정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;첫번째로는 join type을 결정해야 한다.&lt;/span&gt; 이는 조인 이후 결과 데이터 셋에 어떤 데이터가 있어야 하는지를 결정하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를들어 inner join으로 두 데이터 셋에 키가 있는 로우만 유지할지, left (outer) join으로 왼쪽 데이터 셋의 로우는 모두 유지하고 오른쪽 데이터 셋은 키가 있는 로우만 유지할지 등을 고민해야한다.&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;조인타입에는 크게 8가지가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; inner join/ outer join/ left (outer) join / right (outer) join/ left semi join/ left anti join / natural join / cross join&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size14&quot;&gt;&lt;i&gt;조인 타입에서 대해서는 데이터를 다루기 위해 sql 쿼리를 공부할 때도 많이들 공부하는 부분이다보니 더 자세히 설명은 하지 않을 예정이다.&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;두번째로 고민해야 하는 것이 조인을 수행하는 방식&lt;/span&gt;이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 말그대로 두 데이터셋을 조인할 때 어떠한 네트워크 통신 전략을 쓰며, 노드별로 어떠한 연산 작업을 통해서 작업자가 원하는 조인타입대로 데이터 셋을 구현하는 지에 대해 고민하고 결정하는 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Spark Join Strategy&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spark에서 join은 서로 다른 두 데이터셋에 대해 조건으로 건 키값을 기준으로 조회하고 합치는 작업을 진행하며, 실제로 이 작업들이 partition 단위로 분산되어 있는 노드간의 통신을 통해 이루어 진다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 말은 즉슨, spark에서 join은 매우 값비싼 작업이라는 것이다.&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;join이 수행되는 방식을 어떻게 결정하느냐에 따라서 주어진 메모리 내에서 빠르게 효율적으로 작업이 수행될 수도 있는 반면에,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;계속해서 OOM에러를 발생시키고 더 나아가서 클러스터를 괴롭히며 죽여버릴 수도 있게 되는 것이다.&lt;s&gt;(마치 내가 저지른 것처럼...)&lt;/s&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;join이 수행되는 방식을 결정하기 위해서 세부적으로 고민해야 하는 것이 두가지로 또 나눌 수 있다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;노드간 네트워크 통신전략&lt;/b&gt; : join 과정에서 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;두 데이터셋 간의 데이터를 어떻게 이동&lt;/span&gt;시킬것이냐?
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;broadcast join : 노드간 통신을 유발하지 않음&lt;/li&gt;
&lt;li&gt;shuffle join : 전체 노드간 통신을 유발&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;노드별 연산 전략&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Hash, Sort Merge , Nested Loop 등&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Broadcast Join&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (1).png&quot; data-origin-width=&quot;524&quot; data-origin-height=&quot;413&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/zW183/btr0HzIz9Id/qh1eIo2YAKyBkz4NmIrfd1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/zW183/btr0HzIz9Id/qh1eIo2YAKyBkz4NmIrfd1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/zW183/btr0HzIz9Id/qh1eIo2YAKyBkz4NmIrfd1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FzW183%2Fbtr0HzIz9Id%2Fqh1eIo2YAKyBkz4NmIrfd1%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;524&quot; height=&quot;413&quot; data-filename=&quot;Untitled (1).png&quot; data-origin-width=&quot;524&quot; data-origin-height=&quot;413&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 사이즈의 테이블을 driver을 거쳐 각 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;worker node에 copy하여 뿌리는 방식&lt;/span&gt;이다. 즉 작업이 진행될 워커노드에 join 해야하는 데이터셋중 하나를 미리 이동시켜 놓는 것이다.&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 style=&quot;background-color: #f6e199;&quot;&gt;단일 워커 노드의 메모리 크기에 적합할 정도로 충분히 작은 경우, 조인 연산 최적화&lt;/span&gt;가 가능하다.(default 10M)&lt;/li&gt;
&lt;li&gt;결국 작은 데이터 셋의 카피본이 네트워크를 통해 각 워커 노드에 이동되는 것이므로, 각 데이터의 사이즈가 충분히 작아야 한다. 이는 각 워커 노드에 대해 설정해둔 메모리 사이즈 기준이다.&lt;/li&gt;
&lt;li&gt;결국 노드간 통신이 발생하는 것 아닌가? 할 수 있지만 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;복제당시 초기에만 대규모 통신이 발생하고, 이후에 조인 프로세스 내내 노드간 통신이 이루어지는 현상이 발생하지 않는다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;즉 조인 시작 시, 단 한번의 복제가 수행되고 그 이후로는 개별 워커가 다른 워커를 기다리거나 통신할 필요가 없다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;큰 데이터셋과 작은 데이터셋을 조인할때 유용하며, 이는 map-sied join으로도 알려져 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;map-side join&lt;br /&gt;&lt;br /&gt;start schema 형태에서 큰 테이블(fact table)과 비교적 작은 테이블(dimension table)을 조인할때 큰 테이블의 데이터를 네트워크를 통해 모두 전달할 필요가 없도록 해주는 방식이 map-side join이다.&amp;nbsp;&lt;br /&gt;하둡 커뮤니티에서 map-side join이라고 하며 다른 분산시스템에서는 broadcast join이라고 불리운다.&amp;nbsp;&lt;/blockquote&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Shuffle Join&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (3).png&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;344&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HmIoK/btr0HSujaLn/dLOSwrZckHy4Fuk9i2ZZT0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HmIoK/btr0HSujaLn/dLOSwrZckHy4Fuk9i2ZZT0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HmIoK/btr0HSujaLn/dLOSwrZckHy4Fuk9i2ZZT0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHmIoK%2Fbtr0HSujaLn%2FdLOSwrZckHy4Fuk9i2ZZT0%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;657&quot; height=&quot;344&quot; data-filename=&quot;Untitled (3).png&quot; data-origin-width=&quot;657&quot; data-origin-height=&quot;344&quot;/&gt;&lt;/span&gt;&lt;/figure&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 style=&quot;background-color: #f6e199;&quot;&gt;전체 노드간 통신이 발생&lt;/span&gt;한다.&lt;/li&gt;
&lt;li&gt;네트워크가 복잡해지고 많은 자원을 사용한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;전체 조인 프로세스가 진행되는 동안 모든 워커 노드에서 통신이 발생한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Hash Join&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (5).png&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;560&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vC34h/btr0DUNgoJb/KZ8nqvDieg81pjV6r6fy11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vC34h/btr0DUNgoJb/KZ8nqvDieg81pjV6r6fy11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vC34h/btr0DUNgoJb/KZ8nqvDieg81pjV6r6fy11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvC34h%2Fbtr0DUNgoJb%2FKZ8nqvDieg81pjV6r6fy11%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;792&quot; height=&quot;560&quot; data-filename=&quot;Untitled (5).png&quot; data-origin-width=&quot;792&quot; data-origin-height=&quot;560&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Hash Join은 작은 데이터 셋의 키를 조회하면서 자주 접근될 메모리에 Hash 셋을 만들고, 큰 테이블을 순회하면서 Hash 셋을 조회하면서 Join하는 연산 방식이다.&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Broadcast Hash Join&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (6).png&quot; data-origin-width=&quot;811&quot; data-origin-height=&quot;481&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cdJlGi/btr0HyQsyAC/wLE4OXt2rIahPEW3xFSeG0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cdJlGi/btr0HyQsyAC/wLE4OXt2rIahPEW3xFSeG0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cdJlGi/btr0HyQsyAC/wLE4OXt2rIahPEW3xFSeG0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcdJlGi%2Fbtr0HyQsyAC%2FwLE4OXt2rIahPEW3xFSeG0%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;811&quot; height=&quot;481&quot; data-filename=&quot;Untitled (6).png&quot; data-origin-width=&quot;811&quot; data-origin-height=&quot;481&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Broadcast Hash Join은 작은 데이터셋을 Broadcast 한 후에 Executor에서 Hash Join을 수행&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Broadcast Hash Join은 사용자가 Hint를 지정하거나, &lt;span style=&quot;background-color: #f6e199;&quot;&gt;지정하지 않았더라도 한쪽 테이블 사이즈가&amp;nbsp;spark.sql.autoBroadcastJoinThreshold = 10 MiB&amp;nbsp;(Default) 보다 작으면 실행&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Broadcast될 대상 테이블이 클 경우 Driver를 거쳐서 Broadcasting 이 발생하므로 Driver OOM 또는 해시 테이블로 인한 Executor OOM이 발생할 수 있음&lt;/li&gt;
&lt;li&gt;모든 단일 노드에서 개별적으로 join이 수행되므로, CPU가 큰 병목 구간이 될 수있다.&lt;/li&gt;
&lt;li&gt;'=' join만 지원한다.&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;full outer join 외에 모든 조인 타입을 지원한다. &lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Shuffle Hash Join&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (7).png&quot; data-origin-width=&quot;791&quot; data-origin-height=&quot;501&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cgCbH2/btr0GKp6Ves/D4ahZerVBpWzDDpCQc4qi1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cgCbH2/btr0GKp6Ves/D4ahZerVBpWzDDpCQc4qi1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cgCbH2/btr0GKp6Ves/D4ahZerVBpWzDDpCQc4qi1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcgCbH2%2Fbtr0GKp6Ves%2FD4ahZerVBpWzDDpCQc4qi1%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;791&quot; height=&quot;501&quot; data-filename=&quot;Untitled (7).png&quot; data-origin-width=&quot;791&quot; data-origin-height=&quot;501&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Shuffle Hash Join은 Shuffle을 발생시켜 데이터를 이동한 뒤 Hash Join을 수행&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작은 쪽, 즉 메모리에 Hash 셋을 만들 테이블은&amp;nbsp;spark.sql.autoBroadcastJoinThreshold&amp;nbsp;(10MiB, Default) *&amp;nbsp;spark.sql.shuffle.partitions&amp;nbsp;(200, Default) = 2GiB 보다 작아야 함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spark 2.3부터 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;Shuffle Hash Join 대신 Shuffle Sort Merge Join이 기본 전략으로 세팅&lt;/span&gt;됨(spark.sql.join.preferSortMergeJoin&amp;nbsp;= true, Default)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Shuffle Hash Join은 큰 데이터셋을 다룰 수 있으나 Hash 셋을 빌드할 때 메모리에 올려야 하므로 너무 크다면 Executor OOM이 발생할 수 있음&lt;/li&gt;
&lt;li&gt;'=' join만 지원한다&lt;/li&gt;
&lt;li&gt;join key가 정렬되어야 할 필요는 없다&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;full outer join 제외하고 모든 조인 타입을 지원한다.&amp;nbsp;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;해당 조인 방식이 매우 expensive한 방식이라는 의견이 있다. 셔플 뿐 아니라 hashing 방식 과정에서 많은 통신과 메모리를 필요로 하기 때문이다. 특히 hash table을 유지하기 위해서도 메모리와 computation을 많이 요구하게 된다.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;(shuffle) Sort Merge Join&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled (4).png&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;501&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/GrCoX/btr0OaHw9SY/xdB4KfjoB7hwm1lvmILK30/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/GrCoX/btr0OaHw9SY/xdB4KfjoB7hwm1lvmILK30/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/GrCoX/btr0OaHw9SY/xdB4KfjoB7hwm1lvmILK30/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FGrCoX%2Fbtr0OaHw9SY%2FxdB4KfjoB7hwm1lvmILK30%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;644&quot; height=&quot;501&quot; data-filename=&quot;Untitled (4).png&quot; data-origin-width=&quot;644&quot; data-origin-height=&quot;501&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 노드 간의 all-to-all communication 방식&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 style=&quot;background-color: #f6e199;&quot;&gt;join 작업을 수행하기 전에 파티션들을 정렬한다. (이 작업만으로도 비용이 크다)&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;정렬된 데이터들을 병합하면서 join key가 같은 row들을 join한다.&lt;/li&gt;
&lt;li&gt;Sort Merge Join은 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;Shuffle Hash Join과 비교할 때, 클러스터 내 데이터 이동이 더 적은 경향이 있다.&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;u&gt;&lt;i&gt;hash set을 만드는 작업이 이루어지지 않기 때문이라고 추정한다.&lt;/i&gt;&lt;/u&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Join될 파티션들이 최대한 같은 곳에 위치해야 한다. 그렇지 않으면 파티션들을 이동시키기 위해 대량의 shuffle이 발생한다.&lt;/li&gt;
&lt;li&gt;DataFrame의 데이터가 클러스터에 균등하게 분배되어 있어야 한다. 그렇지 않으면 특정 노드에 부하가 집중되고 연산 속도가 느려진다.&lt;/li&gt;
&lt;li&gt;Spark 2.3부터 spark.sql.join.preferSortMergeJoin = true가 디폴트로 활성화 되어있어, 큰 데이터셋에 대해 주로 사용&lt;/li&gt;
&lt;li&gt;Join Key가 정렬 가능해야함&lt;/li&gt;
&lt;li&gt;Join 과정에서 Shuffle Hash Join과 달리 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;Memory가 아닌 Disk를 이용할 수 있기 때문에&lt;/span&gt; OOM이 발생하지 않음&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;모든 join 타입을 지원한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Broadcast Nested Loop Join&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Broadcast Nested Loop Join은 선택할 수 있는 전략이 없을 경우 마지막으로 선택되는 Join 전략이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작은 데이터셋을 Broadcast한 후 이중 반복문을 돌며 하나씩 데이터를 비교해 Join하는 방식이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;어떻게 Join 수행 방식을 결정하면 좋을까?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;broadcast join : 한 쪽의 데이터셋이 충분히 작은 경우 해당 방식을 선택하는 것이 좋다&lt;/li&gt;
&lt;li&gt;shuffle hash join : broadcast를 하기에는 데이터의 양이 많으나, hash map을 짜기에 한쪽 데이터양이 작은 경우&lt;/li&gt;
&lt;li&gt;sort-merge join : join key가 정렬가능할 때&lt;/li&gt;
&lt;li&gt;broadcast nested loop join : 이는 가장 최종적으로 고려하는 방식이다. 이는 OOM이 발생할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;join strategy 설정방법&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래의 힌트를 spark config에 설정하면 원하는 방식의 join를 적용해서 사용할 수 있다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;BROADCAST
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;힌트 사용시 autoBroadcastJoinThreshold 옵션을 무시하고 동작&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;MERGE (SHUFFLE_MERGE 과 동일)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Sort-mege 조인을 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SHUFFLE_HASH&lt;/li&gt;
&lt;li&gt;SHUFFLE_REPLICATE_NL : nested loop join을 사용하기 위한 힌트이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 JOIN 대상 양쪽 테이블에 Join 을 위한 다른 Hint 가 각각 지정된다면 Spark 는 다음의 우선순위를 이용해 결정한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;BROADCAST &amp;gt; MERGE &amp;gt; SHUFFLE_HASH &amp;gt; SHUFFLE_REPLICATE_NL&lt;/li&gt;
&lt;li&gt;양쪽 테이블에 모두 똑같은 Join Hint 가 지정된다면 (Broadcast 및 Shuffle Hash) Spark 는 테이블의 사이즈를 고려해 작은쪽을 기반 테이블로 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;효율적인 join을 방해하는 것&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Data Skewness&lt;/b&gt;&amp;nbsp;: join key가 클러스터에 균일하게 분포해 있지 않으면 특정 파티션이 매우 커질 수 있다. 이는 Spark이 parallel하게 연산을 수행하는 것을 방해한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;All-to-all communication&lt;/b&gt;&amp;nbsp;: broadcast join이 아닐 경우, 두 DF의 데이터 모두에서 대규모 shuffle이 발생한다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Limited executor memory&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Data Skewness를 해결하려면?&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Repartitioning&lt;/b&gt;&amp;nbsp;: 단순히 repartition을 수행하는 것으로 데이터를 파티션들에 더 골고루 분배할 수 있다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Key Salting&lt;/b&gt;&amp;nbsp;: 근본적으로 파티셔닝되는 칼럼 키값에 salting을 적용하여 키가 고르게 분배될 수 있도록 할 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Key Salting이란&lt;br /&gt;&lt;br /&gt;한쪽으로 키값이 몰려있는 데이터를 random int 값을 데이터 뒤에 붙여서 키값 자체를 재분배하는 기술이다.&amp;nbsp;&lt;/blockquote&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Outtro&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음 spark join 작업을 할때는 결과물만 고려해서 join type에 대해서만 열심히 고민했었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 실제로 spark를 사용하는 목적자체가 좀 더 효율적으로 큰 데이터를 다루기 위함이고, 이러한 목적에 맞게 spark를 잘 사용하기 위해서는 수행방식에 대해서도 잘 알고 고민해야겠다고 느꼈다.&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;spark join에 대해서 공부하다보니, 관련해서 partition에 대해서도 많이 언급되고 있어서 다음 글로는 spark partition에 대해서 좀 더 공부를 해볼까 하는 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러나 무엇보다도 기본적으로 spark와 관련된 기본적인 개념을 조금은 알고 있어야 관련해서 필요한 여러 개념들을 더 깊이 이해할 수 있겠구나라는 생각이 들어서 spark 완벽 가이드를 빨리 완독해야겠다는 생각도 들었다..!&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;Reference&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://jjaesang.github.io/spark/2018/12/23/Spark-join.html&quot; data-token-index=&quot;0&quot;&gt;&lt;span&gt;spark join에 대한 정리&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://spark.apache.org/docs/latest/sql-ref-syntax-qry-select-join.html&quot; data-token-index=&quot;0&quot;&gt;&lt;span&gt;spark 공식문서&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://dhkdn9192.github.io/apache-spark/spark-join-strategy/&quot; data-token-index=&quot;0&quot;&gt;&lt;span&gt;효율적인 spark join 전략&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://towardsdatascience.com/strategies-of-spark-join-c0e7b4572bcf&quot; data-token-index=&quot;0&quot;&gt;&lt;span&gt;spark join strategy - how &amp;amp; what&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://dmtolpeko.com/2015/02/20/map-side-join-in-spark/&quot; data-token-index=&quot;0&quot;&gt;&lt;span&gt;Map-Side Join in Spark&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;</description>
      <category>DE/Skill</category>
      <category>join</category>
      <category>spark</category>
      <author>Hazel_song</author>
      <guid isPermaLink="true">https://hazel-developer.tistory.com/307</guid>
      <comments>https://hazel-developer.tistory.com/307#entry307comment</comments>
      <pubDate>Sat, 25 Feb 2023 16:47:23 +0900</pubDate>
    </item>
    <item>
      <title>[글또] 새로운 한 해의 시작, 그리고 글또8기!</title>
      <link>https://hazel-developer.tistory.com/306</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;글또를 또 한 번 시작하며&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;약 일년만에 글또 7기 다짐글을 보면서 한 번 시작해볼까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hazel-developer.tistory.com/270&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;글또 7기 다짐글&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;글또7기 다짐글을 읽으면서 가장 처음 드는 생각은 일년이란 시간이 참으로 짧은데 길다였다.&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;color: #ef6f53;&quot;&gt;&lt;b&gt;&lt;i&gt;이런 변화의 속도라면 내년의 나는 도대체 또 어떻게 살고 있을까 하는 설렘과 기대와 함께 약간의 걱정이..?&lt;/i&gt;&lt;/b&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2022년 글또 7기를 처음 시작했을 때의 나는, 개발자로 전향을 해서 일년간 일을 하긴 했는데 뭐가 뭔지 모르겠고&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;그리고 주변에 개발자 지인이 너무 없다보니 회사를 벗어나서 더 다양한 개발자들을 만나면 뭔가 내 길을 찾게 되지 않을까하는 생각 속에서 글또 7기를 시작했었다.&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;그렇게 시작한 글또와 약 6개월을 함께 하면서&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1) 한 번의 이직을 또 하게 되었고(글또 7기 시작했을때가 막 이직했을 때였다)&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) 새로운 글또 8기는 운영진까지 과감히 도전해보았다.&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;불안과 불확실 속에서 시작한 글또에서 단순히 &quot;글쓰기 습관&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;이번 8기는 7기와는 또 다른 기대감 속에서 시작하게 되었고, 어느정도 방향도 잡히고 감도 잡은 이상 더 멋있는 마무리를 하고싶다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #ef6f53;&quot;&gt;&lt;b&gt;&lt;i&gt;글또....은근 빡센 녀석인데, 중독성있다...그래서 더 '일'이라 생각하지 않고 즐겁게 하자고 계속 다짐하게 된다.&lt;/i&gt;&lt;/b&gt;&lt;/span&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;h4 data-ke-size=&quot;size20&quot;&gt;1) 활동의 성실성 여부&amp;nbsp;&amp;nbsp;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;상&lt;/span&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;h4 data-ke-size=&quot;size20&quot;&gt;2) 글의 퀄리티&amp;nbsp;&lt;span style=&quot;background-color: #9feec3;&quot;&gt;중&lt;/span&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;&amp;nbsp;&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;&lt;a href=&quot;https://hazel-developer.tistory.com/285&quot; data-token-index=&quot;0&quot;&gt;&lt;span&gt;Udemy [Hadoop] - Spark&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hazel-developer.tistory.com/275&quot; data-token-index=&quot;0&quot;&gt;&lt;span&gt;AWS EMR 세팅 실습&lt;/span&gt;&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;후반부로 갈수록 하나의 주제를 잡고 깊이 공부하고 내 생각을 정리하며 &quot;학습&quot;을 할 수 있게 되었다. 또한 누군가가 읽는다는 것까지 염두하고 글을 쓰게 되었다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hazel-developer.tistory.com/293&quot; data-token-index=&quot;0&quot;&gt;&lt;span&gt;Apache Sqoop의 등장과 은퇴&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hazel-developer.tistory.com/300&quot;&gt;[Index]검색엔진에서의 SS테이블/LSM트리인덱스 활용(1)&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://hazel-developer.tistory.com/302&quot;&gt;[Index]검색엔진에서의 SS테이블/LSM트리인덱스 활용(2) - ES의 세그먼트&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;대표적으로 후반부에 쓴 sqoop이야기는 짧지만 재밌었다(저 글을 쓰고 난 이후로 스터디할때마다 apache attic을 찾게 된다ㅋㅋ)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;index에 대한 짧은 시리즈는 글의 퀄리티 뿐 아니라 개인적인 지식 향상에도 큰 영향을 주었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;확실히 좋은 글을 쓰기 위해 저자는 열심히 고민하고 공부하게 되므로, 결국 좋은 글은 단순히 &quot;글&quot;이라는 결과물뿐 아니라&amp;nbsp;&lt;/b&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;h4 data-ke-size=&quot;size20&quot;&gt;3) 글쓰는 방식 및 습관&amp;nbsp; &lt;span style=&quot;background-color: #c1bef9;&quot;&gt;하&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 회고 소제목에 대한 설명이 필요할듯 한데, 풀어 쓰자면 &quot;&lt;b&gt;얼마나 깊이 고민한 주제이며, 그 주제가 정말 내가 현재 하고자 하는 학습방향과 일치하는가? 아니면 무작위로 급하게 정한 주제인가?&quot;&lt;/b&gt; 이다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런 의미에서 나는 격주마다 일단 글을 쓰는거에 쫓겨서 긴 흐름을 만들지 못하고 그때그때 내 호기심을 채우는 공부를 단타로 쳐냈던 것 같다. 이번 기수에서는 전체적인 방향과 목적을 정하고 시작하여, 이번 기수가 끝났을때 특정 주제에 대한 학습적 성장을 이루었으면 좋겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;이번 글또에 대한 새로운 다짐&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1) 성실하게 활동하자&lt;/h4&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;2) 나의 학습 메모가 아닌, 누군가의 학습에 도움이 되는 글임을 생각하자&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;u&gt;&lt;b&gt;주제 선정 부터 해서 목차 정리 그리고 학습&lt;/b&gt;&lt;/u&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;3) 이번 기수에서 나의 글감, 더 나아가 학습 목표를 정리해보자&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;Spark 공부&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;회사 내에서 무거운 데이터를 분석하고 모델링하는 업무를 주로 담당하게 되다보니 spark를 확실히 잘 써야겠다는 생각이 들었다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Data Governance&lt;/b&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;b&gt;기술적으로 데이터 거버넌스와 메타데이터 관리 프로세스를 자동화 할 수 있는 툴&lt;/b&gt;에 대한 고민/&lt;/li&gt;
&lt;li&gt;그리고 &lt;b&gt;기획적으로는 이러한 프로세스를 어떻게 가꾸어 나가고 데이터 거버넌스 자체&lt;/b&gt;에 대해서 많이 고민할 거 같다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b&gt;CDP(customer data platform)&lt;/b&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;그런데 데이터 분석이라기보다는 정확히는 데이터 분석가/마케터/PO들의 분석 작업 중 고객/유저와 관련된 업무가 더 효율적이고 깊게 이루어질 수 있도록 고객 데이터 플랫폼을 구성하는 것이고&lt;/li&gt;
&lt;li&gt;고객과 비즈니스에 대한 이해보다는 좀 더 1차원적인 데이터 구성/ database/ 가공방식에 대한 공부가 필요한 듯하다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;CDJ(고객 구매여정), 고객 프로파일링 등에 대한 업무를 회사에서 메인으로 진행하게 될텐데 이 과정에서 고민하는 부분과 관련된 학습을 많이 정리할 예정이다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4) 좋은 사람들과 즐거운 시간이 되자&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이건 지난 기수와 관련없는 new!다짐이다!&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;나의 글과 성장에 대한 고민을 넘어서서 많은 사람들의 글과 성장에 대해서 고민하는 시간이 되어보고&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 과정에서 운영진이라는 인연을 맺게된 좋은 사람들과 즐거운 시간이 될 수 있도록! 그러기 위해서는 항상 먼저 나서서 열심히 즐겁게 하는 시간이 되어보자!&lt;/p&gt;</description>
      <category>DIARY/2023</category>
      <author>Hazel_song</author>
      <guid isPermaLink="true">https://hazel-developer.tistory.com/306</guid>
      <comments>https://hazel-developer.tistory.com/306#entry306comment</comments>
      <pubDate>Wed, 1 Feb 2023 22:54:13 +0900</pubDate>
    </item>
    <item>
      <title>[하둡 완벽 가이드]Ch07. 맵리듀스 작동방법</title>
      <link>https://hazel-developer.tistory.com/305</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://www.kyobobook.co.kr/product/detailViewKor.laf?ejkGb=KOR&amp;amp;mallGb=KOR&amp;amp;barcode=9788968484599&amp;amp;orderClick=LEa&amp;amp;Kc=&quot;&gt;하둡 완벽가이드&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;를 읽으며 정리한 내용입니다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;7.1 맵리듀스 잡 실행 상세 분석&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;399&quot; data-origin-height=&quot;384&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AH573/btrPsBRHl1B/MPypK3nIkg94kVddLOgf80/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AH573/btrPsBRHl1B/MPypK3nIkg94kVddLOgf80/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AH573/btrPsBRHl1B/MPypK3nIkg94kVddLOgf80/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAH573%2FbtrPsBRHl1B%2FMPypK3nIkg94kVddLOgf80%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;564&quot; height=&quot;543&quot; data-origin-width=&quot;399&quot; data-origin-height=&quot;384&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;클라이언트 노드에서 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;맵리듀스 잡을 제출&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;YARN 리소스 매니저
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;클러스터 상에 계산 리소스의 할당을 제어&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;YARN 노드 매니저
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;각 머신에서 컨테이너를 시작하고 모니터링&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;MRAppMaster
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MR잡을 수행하는 각 태스크를 제어한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;AppMaster와 MR task는 컨테이너 내에서 실행되며 리소스 매니저는 잡을 할당하고 노드 매니저는 태스크를 관리하는 역할&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;공유 파일 시스템
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서로 다른 단계 간의 잡 리소스 파일들을 공유하는데 사용된다.(보통 HDFS)&lt;/li&gt;
&lt;/ul&gt;
&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;7.1.1 잡 제출&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클라이언트 노드에서 맵리듀스 잡을 제출할 때의 과정을 상세하게 정리&lt;/p&gt;
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내부의 JobSubmitter 인스턴스 생성&lt;/li&gt;
&lt;li&gt;&lt;u&gt;리소스 매니저&lt;/u&gt;에 MR 잡 ID로 사용될 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;새로운 애플리케이션 ID요청&lt;/span&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;/ul&gt;
&lt;/li&gt;
&lt;li&gt;입력 스플릿 계산. 즉 실질적으로 수행 될 작업 단위를 계산한다.&lt;/li&gt;
&lt;li&gt;잡 실행에 필요한 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;잡리소스에 대한 정보가 기재된 파일을 공유 파일시스템&lt;/span&gt;에 있는 해당 잡 ID이름의 디렉터리에 복사
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;복제인수는 mapreduce.client.submit.file.replication 속성에 정의하며 기본값은 10으로 설정&lt;/li&gt;
&lt;li&gt;클러스터 상에 복제본이 많으면 노드 매니저가 잡의 태스크를 실행할 때 접근성이 높아지는 장점&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;리소스 매니저의 submitApplication()을 호출하여 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;잡 제출&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;waitForCompletion() 메서드가 1초에 한번씩 잡의 진행상황을 조사하여 변경내역 보여줌.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7.1.2 잡 초기화(태스크 실행방식 결정)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 단계를 통해, 리소스 매니저가 호출을 받게되고 YARN 스케줄러에 요청을 전달한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스케줄러가 컨테이너를 할당하면, 컨테이너를 시작하고 노드 매니저의 애플리케이션 마스터 프로세스를 시작한다. (그림의 5a~5b 단계)&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;애플리케이션 마스터는 잡을 초기화 할 때, 잡의 진행상태를 추적하기 위한 다수의 bookkeeping 객체를 생성한다.&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잡 제출 당시에 복사한 입력 스플릿 리소스에 대한 정보를 공유파일 시스템으로부터 추출한다.(7단계)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;해당 파일 정보를 바탕으로 맵 태스크 객체를 생성하고 지정한 리듀서 수만큼 맵 태스크 객체를 생성하며, 각 태스크가 ID를 부여받게 된다.&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: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;잡의 크기가 작다면 본인의 JVM에서 실행한다. 즉 병렬처리를 위해 새로운 컨테이너에 태스크를 할당하는 등의 오버헤드가 발생하는 것보다 단일 노드에서의 순차처리가 더 유리하다고 판단한 것이다. (&lt;span style=&quot;background-color: #f6e199;&quot;&gt;우버 태스크&lt;/span&gt;로 실행했다고 한다)&lt;/li&gt;
&lt;li&gt;잡의 크기가 작다는 기준은? HDFS 블록 하나보다 작은 크기의 입력, 혹은 10개 미만의 매퍼&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;7.1.3 태스크 할당&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 잡을 우버 태스크로 실행하지 않는다면, 애플리케이션 마스터는 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;리소스 매니저에 추가 컨테이너를 요청&lt;/span&gt;한다.(8단계)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;이때 맵태스크 요청이 리듀스 태스크 요청보다 우선순위가 높은데, 이는 리듀스의 정렬단계가 시작되기 전에 모든 맵태스크가 완료되어야 하기 때분이다.&lt;/li&gt;
&lt;li&gt;참고로 모든 맵 태스크의 5%가 완료되기 전까지 리듀스 태스크의 요청은 처리되지 않는다(10.3.5 절의 느린 리듀스 시작 참고)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리듀스 태스크와 달리(클러스터 어느곳에서도 실행 가능) &lt;span style=&quot;background-color: #f6e199;&quot;&gt;맵 태스크는 데이터 지역성 제약&lt;/span&gt;이 있다. 최적의 상황은 입력 스플릿이 저장된 노드에서 맵 태스크가 실행되는 데이터 로컬일 때다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음 대안으로는 입력 스플릿과 동일한 랙이지만 노드는 다른 랙 로컬일때다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 이렇게 리소스를 요청할때 필요한 메모리 요구사항과 CPU 수를 명시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 기본적으로 1,024MB 메모리와 가상코어 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;7.1.4 태스크 실행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 과정을 통해 리소스를 태스크에 할당하면 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;애플리케이션 마스터는 노드 매니저와 통신하며 컨테이너를 시작&lt;/span&gt;한다(9a~9b 단계)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본격적으로 태스크 실행하기 전에 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;필요한 리소스(환경 설정, JAR파일, 분산 캐시와 관련된 파일 등)를 로컬&lt;/span&gt;로 가져오고(10단계),&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음에 맵과 리듀스 태스크를 실제 실행한다.(11단계)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;위의 그림을 참고하면 노드 매니저 노드 내에서 각 태스크가 실행되는 YarnChild는 자체 전용 JVM에서 실행되므로 이에 대한 버그는 노드 매니저에 영향을 주지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앞에서 임시 작업 공간에 저장한 태스크 출력을 최종 위치로 옮긴다. (7.4.3절 출력 커미터 참고)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커밋 프로토콜은 투기적 실행이 활성화 됐을때 중복 태스크 중 단 하나만 커밋하고 나머지는 버린다(7.4.2 투기적 실행 참고)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스트리밍 태스크&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2022-10-20-21-57-06.jpeg&quot; data-origin-width=&quot;685&quot; data-origin-height=&quot;1440&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b6C2ET/btrPgfBSOV9/FpBXthUEbI40904SclH9h1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b6C2ET/btrPgfBSOV9/FpBXthUEbI40904SclH9h1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b6C2ET/btrPgfBSOV9/FpBXthUEbI40904SclH9h1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb6C2ET%2FbtrPgfBSOV9%2FFpBXthUEbI40904SclH9h1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;293&quot; height=&quot;616&quot; data-filename=&quot;KakaoTalk_Photo_2022-10-20-21-57-06.jpeg&quot; data-origin-width=&quot;685&quot; data-origin-height=&quot;1440&quot;/&gt;&lt;/span&gt;&lt;/figure&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;h3 data-ke-size=&quot;size23&quot;&gt;7.1.5 진행상황과 상태갱신&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;span style=&quot;background-color: #f6e199;&quot;&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;리듀스 태스크는 리듀스가 처리한 입력 데이터의 비율을 시스템이 추정하는데, 이러한 추정을 위해서 전체 진행과정을 총 세부분으로 나누게된다. 예를 들어 리듀서에서 입력의 절반을 처리했다면 태스크의 진행상황은 5/6이 된다. 이는 복사와 정렬 단계가 각각 1/3씩 완료되었고 리듀스 단계의 절반(1/6)이 진행되엇기 때문이다.(셔플의 단계와 관련)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 즉 리듀스 태스크는 copy, sort, reduce&amp;nbsp; 세 단계로 태스크를 나누고 이 기준으로 진행 상황을 추적한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  진행 중임을 판단하는 동작&lt;br /&gt;- 입력 레코드 읽기&lt;br /&gt;- 출력 레코드 쓰기&lt;br /&gt;- 상태 명세의 설정&lt;br /&gt;- 카운터 증가&lt;/blockquote&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;7.1.6 잡 완료&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;h2 data-ke-size=&quot;size26&quot;&gt;7.2 실패&lt;/h2&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;7.2.1 태스크 실패&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;태스크가 아예 장애로 작업을 실행하지 못하는 경우는 &lt;span style=&quot;background-color: #f6e199;&quot;&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;u&gt;행이 걸린 즉 멈춘&lt;/u&gt; 태스크의 경우, 바로 실패로 처리되는게 아니라 특정 시간 동안 진행상황을 갱신 받지 못한 경우 애플리케이션 마스터에서 실패로 표시한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 이 특정 시간 즉 타임아웃은 사용자가 설정 가능한데 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;0으로 설정하면 아예 비활성화&lt;/span&gt;가 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 아무리 긴 시간동안 실행되지 않아도 멈추지 않을 것이고 이는 클러스터를 느리게 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 태스크가 실패하더라도 당장 잡이 중단되는 것은 원치않을 수 있으므로 잡이 실패하지 않는 태스크 실패 허용 비율을 잡에 설정할 수 있다. 이 때 태스크의 강제 종료는 실패에 포함하지 않는다.&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;7.2.2 애플리케이션 마스터 실패&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;span style=&quot;background-color: #f6e199;&quot;&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;u&gt;복구 작업 방식&lt;/u&gt;은 어떨까?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;애플리케이션 마스터는 &lt;u&gt;주기적으로 리소스 매니저에 하트비트&lt;/u&gt;를 보내고, &lt;u&gt;실패시에 리소스 매니저가 이를 감지해서 새로운 컨테이너에서 새로운 마스터 인스턴스를 시작&lt;/u&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 &lt;u&gt;잡 히스토리를 사용해서 실패한 애플리케이션에서 이미 실행된 모든 태스크의 상태를 복구&lt;/u&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;u&gt;리소스 매니저에 애플리케이션 마스터의 주소를 요청하고, 이를 캐시&lt;/u&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;7.2.3 노드매니저 실패&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리소스 매니저에서 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;특정 노드매니저가 (설정 가능한, 기본적으로 10분)특정 시간동안 하트비트를 전송하지 않음을 인지하면, 컨테이너를 스케줄링하는 노드 풀에서 해당 노드 매니저를 제거&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 즉 실패한 노드 매니저 위에 돌아가는 태스크 포함 애플리케이션 마스터 모두 recover한다.&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;h3 data-ke-size=&quot;size23&quot;&gt;7.2.4 리소스 매니저 실패&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;우선 &amp;ldquo;굉장히&amp;rdquo; 심각한 상황! 왜냐면 리소스 매니저 없이 잡이나 태스크 컨테이너가 실행될 수 없기 때문이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;u&gt;고가용성(HA)&lt;/u&gt;을 위해 설정이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 위해 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;두 개의 리소스 매니저를 활성대기 설정으로 실행&lt;/span&gt;한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행중인 &lt;u&gt;애플리케이션에 대한 정보는 고가용 상태 저장소(주키퍼 혹은 HDFS)에 보관&lt;/u&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리소스 매니저가 대기 &amp;rarr; 활성상태로 전환하는 것은 &lt;u&gt;장애극복 관리자&lt;/u&gt;가 담당한다.(failover controller)&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;rarr; 이러한 장애극복 관리자는 독립 프로세스일 필요는 없으므로 기본적으로 리소스 매니저에 포함된다.&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;gt; 노드 매니저가 각 리소스 매니저에 일정 시간 동안 계속해서 연결을 시도하면서 연결되는 리소스 매니저를 찾는다??&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;7.3 셔플과 정렬&lt;/h2&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;/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;7.3.1 맵 부분&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵 함수가 결과를 생산할 때 실질적으로는 단순히 디스크에 쓰는 것은 아니다. 효율적인 처리를 위해 메모리에 일정 크기만큼 쓴다음 사전 정렬을 수행한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;436&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cIT6cp/btrPrmmV80R/8E98K8GZQFnqY2Xy2uM4OK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cIT6cp/btrPrmmV80R/8E98K8GZQFnqY2Xy2uM4OK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cIT6cp/btrPrmmV80R/8E98K8GZQFnqY2Xy2uM4OK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcIT6cp%2FbtrPrmmV80R%2F8E98K8GZQFnqY2Xy2uM4OK%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;782&quot; height=&quot;436&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;436&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;&lt;u&gt;&lt;b&gt;메모리 버퍼&lt;/b&gt;&lt;/u&gt;는 환형 구조이며, 기본적으로 100MB인데 이 또한 변경 가능하다.&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버퍼의 내용이 특정 한계치(기본적으로 80%, 이 또한 설정 가능)에 도달하면 백그라운드 스레드가 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;디스크에 스필(각 파티션별 메모리 버퍼를 디스크에 한번에 쓰는 것)한다. 이 때 데이터를 최종적으로 전송할 리듀서 수에 맞게 파티션한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;-&amp;gt; 해당 파티션 기준으로 후에 merge 된다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서도 맵 결과는 계속 버퍼에 쓰이는데 버퍼가 가득차면 맵은 스필이 종료될때까지 블록 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;스필 한계치에 도달하면 새로운 스필파일이 생성&lt;/u&gt;된다. 따라서 맵 태스크가 최종결과 레코드를 쓰고나면 여러개의 스필 파일이 존재할 수 있다. 태스크가 종료되기전에 이러한 여러 스필 파일은 단일 출력 파일로 병합되고 정렬된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 한 번에 병합할 최대 스트림 수를 조절할 수 있으며 기본 값은 10이다.&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;i&gt;맵 출력 파일을 효율화 하기 위해 컴바이너가 실행되는데, 이 기준은 스필 파일이 세개를 넘느냐 아니냐이다.&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;즉 스필파일이 세개를 넘으면 출력을 축소하기 위해 컴바이너가 실행된다.&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;그러나 한개나 두개일 경우에는 컴바이너를 호출하는거 자체가 오버헤드이므로 실행하지 않는다.&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;맵출력을 디스크에 쓰려는 시점에 압축을 실행한다. 이를 통해 디스크 공간을 절약하며 리듀서로 전송할 데이터양을 줄일 수 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 기본적으로는 출력 당시에 압축이 실행되지 않지만, 압축하도록 설정할 수 있으며 이때 압축 라이브러리 또한 지정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵태스크를 진행하며 파티션을 나누었던 파티션 정보는 HTTP를 통해 리듀서에 전달된다.&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;7.3.2 리듀스 부분&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;span style=&quot;background-color: #f6e199;&quot;&gt;각각 병렬로 복사를 실행한다.&lt;/span&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;  맵 태스크가 성공적으로 종료되면 하트비트 전송 메커니즘을 통해 애플리케이션 마스터에 맵출력의 서버를 알려준다. &lt;br /&gt;그렇다면 애플리케이션 마스터는 맵출력과 호스트 사이의 매핑 정보를 알고 있게 된다. &lt;br /&gt;이에 대해 리듀스 내의 한 스레드가 맵 출력 호스트 정보를 주기적으로 마스터에 요청한다.&lt;br /&gt;&lt;br /&gt;이때 호스트는 첫 번째 리듀서가 맵 출력을 회수하더라도 디스크에서 그들을 삭제하지 않는데. 이는 리듀스가 회수에 실패할 수 있기 때문이다. 애플리케이션 마스터로부터 삭제 요청을 받을 때까지 기다린다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때 맵출력의 크기가 작다면 리듀스 태스크 JVM메모리에 복사된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 또한 맵태스크처럼 &lt;u&gt;인메모리 버퍼가 한계치에 다르면 병합되어 디스크에 스필&lt;/u&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;&amp;rarr; 이 작업은 라운드 단위로 이루어 지는데, 가령 맵출력이 50개이고 병합 계수가 10이라면 5개의 라운드로 구성된다. 이를 통해 5개의 중간 파일이 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;이렇게 만들어진 파일들은 하나의 정렬된 파일로 병합하는 최종 라운드를 가지는 대신 마지막 단계(리듀스 단계)의 리듀스 함수에 곧바로 전송하여 디스크 IO를 줄인다.&amp;nbsp;&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;i&gt;-&amp;gt; 이 부분이 이해가 잘 안된다&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;❓ 사실 각 라운드에서 병합되어 만들어지느 파일 수는 좀 더 미묘하다. &lt;u&gt;궁극적인 목적은 최종 라운드에서 병합 계수에 도달하기 위한 최소한의 파일을 병합하는 것이다&lt;/u&gt;. 따라서 예시처럼 40개의 파일일때 10개씩 병합하여 파일 4개를 얻는 것과는 실제는 다른다.&lt;br /&gt;&lt;br /&gt;처음에는 4개만 병합하고, 이어서 라운드 3개에서 10개씩 병합하면, 네개의 병합된 파일과, 병합되지 않은 여섯개의 파일을 합쳐서 10개의 파일이 생성된다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7.3.3 설정 조정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;맵리듀스 성능향상을 위해 셔플 튜닝에 대한 설정을 해줄 수 있는데 일반적으로는 &lt;u&gt;셔플에 가능한 한 많은 메모리를 할당하는 것&lt;/u&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;span style=&quot;background-color: #f6e199;&quot;&gt;맵 측면에서는 다수의 디스크 스필을 피하는 것&lt;/span&gt;이 최고의 성능을 내는 방법이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;리듀스 측면에서는 중간 데이터 전체가 메모리에 존재할 때 최고의 성능&lt;/span&gt;을 얻을 수 있다. 일반적으로는 모든 메모리를 리듀스 함수에 예약해두므로 중간 데이터를 보관할 메모리를 할당하는 것이 힘드나, 리듀스 함수가 조금의 메모리만 요구할때 이러한 설정이 가능하여 성능향상을 꾀할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;-&amp;gt; 이 부분이 이해가 잘 되지 않는다. 중간 데이터는 어떤 데이터이며, 맵태스크를 완료한 맵출력 데이터인건가? 그리고 데이터가 메모리에 존재하는데 어느 메모리에? 리듀스 내 JVM 메모리?&amp;nbsp;&lt;/u&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 위에서 언급된, &lt;b&gt;&quot;맵출력의 크기가 작다면 리듀스 태스크 JVM메모리에 복사된다. 이 또한 맵태스크처럼인메모리 버퍼가 한계치에 다르면 병합되어 디스크에 스필&quot;&lt;/b&gt; 이 부분과 관련된건가? 즉 디스크에 스필 되지 않고 최대한 많은 데이터가 메모리에 잔존하는 것이 최고의 성능을 얻는 방법이라는 건가?&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;7.4 태스크 실행&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;7.4.1 태스크 실행 환경&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하둡은 맵, 리듀스 태스크에 실행환경에 관한 정보를 전달해주며, 이 정보에서 처리할 파일명이나 태스크 시도 횟수등을 알 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7.4.2 투기적 실행&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;이때 하둡은 느린 태스크를 진단하거나 고치려하지 않고, 태스크 수행이 예상했던 것보다 더 느리게 수행된 상황을 감지하여 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;비슷한 설정을 지닌 동일한 태스크를 새로 실행한다. 이를 태스크의 투기적 실행&lt;/span&gt;이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 즉 이름때문에 두개의 복제 태스크를 경쟁하도록 하는 실행이 아니다.&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;u&gt;중복된 데이터를 복사하여 실행함으로써 더 많은 리소스를 사용하며, 결국 성공한 하나의 태스크가 아니면 버려지지만, 동일한 태스크를 여러 노드에서 실행함으로써 특정 노드가 느리더라도(장비노후나 특정 문제로) 다른 노드에서 먼저 끝나면 해당 결과를 사용할 수 있게 된다. 즉 특정 노드에서 발생할 수 있는 버그를 제거함으로써 전체적으로는 수행 시간이 단축 되는 것이다.&lt;/u&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;투기적 실행의 궁극적인 목적은 잡 실행시간을 줄이는 최적화지만, 사실 &lt;u&gt;클러스터 효율성 측면에서는 복제 태스크가 추가로 실행되는 것이므로 비용이 발생&lt;/u&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7.4.3 출력 커미터&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잡과 태스크가 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;깨끗하게 성공하거나 실패하도록 보장하기 위한 커밋 프로토콜&lt;/span&gt;을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 동작은 OutputCommitter로 구현했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;잡이 실행되면 최종 출력 디렉터리와 태스크 출력을 위한 임시 작업공간을 최종 출력 디렉터리의 &lt;u&gt;서브 디렉터리로 생성&lt;/u&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;특정 태스크에 대한 여러 태스크 시도 중에서 단 하나만을 커밋하며 나머지는 중단시킨다. 만약 두개의 태스크 시도가 투기적 중첩으로 인해 동시에 실행되면 먼저 완료된 태스크가 커밋되고 나머지는 중단된다.&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;태스크의 부차적인 파일&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;태스크의 출력을 작성하는 일반적인 방법은 키-값 쌍을 수집하는 OutputCollector를 사용하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;몇몇은 키-값 쌍 모델보다 더 유연한 모델을 필요로 하기 때문에 직접 HDFS와 같은 분산 파일 시스템에 출력 파일을 작성한다.&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;이때 동일 태스크의 다중 인스턴스가 동일한 파일에 쓰이지 않아야 하는데, OutputCommitter 프로토콜이 이를 해결해준다.&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;h2 data-ke-size=&quot;size26&quot;&gt;Reference&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://mambo-coding-note.tistory.com/20&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;하둡 : 맵리듀스 잡 실행&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.linkedin.com/pulse/map-reduce-shuffle-sort-shreyas-tapale/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;letter-spacing: -1px; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif;&quot;&gt;Map-Reduce Shuffle and Sort&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://sungwookkang.com/1469&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;&lt;span style=&quot;letter-spacing: -1px; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif;&quot;&gt;투기적 실행&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot;color: #000000; letter-spacing: -1px; font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'Apple SD Gothic Neo', Arial, sans-serif;&quot;&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>WIR(What I Read)</category>
      <category>맵리듀스 작동방식</category>
      <category>하둡완벽가이드</category>
      <author>Hazel_song</author>
      <guid isPermaLink="true">https://hazel-developer.tistory.com/305</guid>
      <comments>https://hazel-developer.tistory.com/305#entry305comment</comments>
      <pubDate>Sun, 23 Oct 2022 21:47:48 +0900</pubDate>
    </item>
    <item>
      <title>[하둡 완벽 가이드]Ch04. YARN</title>
      <link>https://hazel-developer.tistory.com/304</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;http://www.kyobobook.co.kr/product/detailViewKor.laf?ejkGb=KOR&amp;amp;mallGb=KOR&amp;amp;barcode=9788968484599&amp;amp;orderClick=LEa&amp;amp;Kc=&quot;&gt;하둡 완벽가이드&lt;/a&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000;&quot;&gt;를 읽으며 정리한 내용입니다.&lt;/span&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;YARN은 하둡의 클러스터 자원관리 시스템이다. 맵리듀스의 성능을 높이기 위해 하둡2에서 처음 도입되었으나 맵리듀스뿐만 아니라 다른 분산 컴퓨팅 도구도 지원한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-10-09 오후 12.30.47.png&quot; data-origin-width=&quot;595&quot; data-origin-height=&quot;223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/vlf7k/btrN67XZNfU/vk2uOFgr8Ln89k8h48jAW0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/vlf7k/btrN67XZNfU/vk2uOFgr8Ln89k8h48jAW0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/vlf7k/btrN67XZNfU/vk2uOFgr8Ln89k8h48jAW0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fvlf7k%2FbtrN67XZNfU%2Fvk2uOFgr8Ln89k8h48jAW0%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;595&quot; height=&quot;223&quot; data-filename=&quot;스크린샷 2022-10-09 오후 12.30.47.png&quot; data-origin-width=&quot;595&quot; data-origin-height=&quot;223&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분산 컴퓨팅 프레임 워크는 위의 그림과 같이 계산 계층(YARN)과 저장계층(HDFS, HBASE)위에서 YARN 애플리케이션을 실행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;피크, 하이브 같은 애플리케이션은 맵리듀스, 스파크에서 구동되는 처리 프레임 워크이며 YARN에 직접 접근하지 않는다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.1 YARN 애플리케이션 수행 해부해보기&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;[YARN의 구조]&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;573&quot; data-origin-height=&quot;396&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/VfWdw/btrOcidn3C3/lZvAU7qZO4qfpixzgguaRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/VfWdw/btrOcidn3C3/lZvAU7qZO4qfpixzgguaRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/VfWdw/btrOcidn3C3/lZvAU7qZO4qfpixzgguaRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FVfWdw%2FbtrOcidn3C3%2FlZvAU7qZO4qfpixzgguaRK%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;573&quot; height=&quot;396&quot; data-filename=&quot;Untitled.png&quot; data-origin-width=&quot;573&quot; data-origin-height=&quot;396&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;YARN은 기본적으로 Resource Management 기능과 Job Scheduling/Monitoring 기능을 분리된 데몬에서 처리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 구현하기 위해서 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;Global Resource Manager(RM)와 application 단위로 존재하는 Application Master(AM)&lt;/span&gt; 을 가지도록 하였다. 여기서 말하는 &lt;u&gt;Application은 하나의 작업 또는 작업&lt;/u&gt;의 DAG(Directed Acyclic Graph, 비순환 방향 그래프)을 의미한다.&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;Resource Manager: Master Daemon에서 구동되며, 클러스터들로의 자원 할당을 관리한다.&lt;/li&gt;
&lt;li&gt;Node Manager: Slave Daemon에서 구동되며, 각 단일 노드의 Task 실행을 담당한다.&lt;/li&gt;
&lt;li&gt;Application Master: 개별적인 응용에서 필요로 하는 자원과 Job에 대한 lifecycle을 관리한다. 이것은 Node Manager와 함께 작동하며, 작업의 실행을 모니터링한다.&lt;/li&gt;
&lt;li&gt;Container: 한 노드의 자원을 모아둔 패키지 (RAM, CPU, Network, HDD 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Resource Manager은 시스템의 모든 Application들 사이에서 자원을 관리/중재한다. Node Manager은 Machine 단위로 존재하는 Framework Agent로 Container를 관리하고, CPU와 Memory와 같은 자원의 사용량을 모니터링하며, Resource Manager로 정보를 전달한다. Application단위로 존재하는 Application Master는 Resource Manager와 함께 자원을 협상하고, Task를 모니터링하고 실행하기 위해 Node Manager와 함께 작동한다.&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;[YARN의 구동방식]&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2022-10-09 오후 12.32.34.png&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;475&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bZwGRB/btrN78vAkjU/0rHVpL6CCwkFkyueiTnbB1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bZwGRB/btrN78vAkjU/0rHVpL6CCwkFkyueiTnbB1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bZwGRB/btrN78vAkjU/0rHVpL6CCwkFkyueiTnbB1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbZwGRB%2FbtrN78vAkjU%2F0rHVpL6CCwkFkyueiTnbB1%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;598&quot; height=&quot;475&quot; data-filename=&quot;스크린샷 2022-10-09 오후 12.32.34.png&quot; data-origin-width=&quot;598&quot; data-origin-height=&quot;475&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;ol 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;ol 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;/ol&gt;
&lt;/li&gt;
&lt;li&gt;작업 수행 후, 리소스를 리소스 매니저에 더 요청하거나 아니면 작업 종료&lt;/li&gt;
&lt;li&gt;리소스를 더 할당 받은 경우 새로운 노드 매니저 찾아서 구동
&lt;ol style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;분산처리를 수행&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;YARN은 클라이언트, 마스터, 프로세스와 같은 애플리케이션이 서로 통신하는 기능은 제공하지 않고 하둡의 RPC와 같은 원격 호출 방식을 이용하여 상태변경을 전달하고 클라이언트로부터 결과를 받는다.&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;4.1.1 자원요청&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유연한 자원요청 모델을 갖고있다. 컨테이너에 필요한 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;자원의 용량뿐 아니라 요청에 대한 지역성 제약도 표현&lt;/span&gt;할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분산 데이터 처리 알고리즘에서 클러스터의 네트워크 대역폭을 효율적으로 활용하기 위해서는 지역성을 보장하는 것이 가장 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 지역성 제약은 보통 특정 노드나 랙 또는 클러스터의 다른 곳에서 컨테이너를 요청할때 사용된다.&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;가령 특정 노드를 요청햇는데 그 노드에서 다른 컨테이너가 실행되고 있는 등의 상황이면, YARN은 동일한 랙의 다른 노드에서 컨테이너를 시작하려 시도한다.&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;YARN 애플리케이션은 실행 중에는 아무때나 자원요청을 할 수 있다.&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;h3 data-ke-size=&quot;size23&quot;&gt;4.1.2 애플리케이션의 수명&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행시간은 천차만별이므로 사용자가 실행하는 잡의 방식에 따라 애플리케이션을 분류하는 것이 좋다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;가장 단순한 유형은 사용자의 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;잡 당 하나의 애플리케이션이 실행되는 방식&lt;/span&gt;으로 맵리듀스 잡이 속한다.&lt;/li&gt;
&lt;li&gt;두번째 유형은 워크플로나 사용자의 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;잡 세션당 하나의 애플리케이션이 실행&lt;/span&gt;되는 방식이다. 이 유형은 첫 유형보다 훨씬더 효율적이다. 순차적으로 실행되는 잡이 동일한 컨테이너를 재사용가능하며 잡 사이에 공유데이터를 캐싱할수 있는 장점이 있다. 대표적으로 스파&lt;/li&gt;
&lt;li&gt;세번째 유형은 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;서로 다른 사용자들이 공유할 수 있는 장기실행 애플리케이션&lt;/span&gt;이다. 이러한 유형은 일종의 코디네이션 역할을 수행하기도 한다. 임팔라가 대표적. 임팔라는 여러 임팔라 데몬이 클러스터 자원을 요청할 수 있도록 프록시 애플리케이션을 제공한다. &lt;span style=&quot;background-color: #f6e199;&quot;&gt;이러한 경우 애플리케이션마스터가 항상 켜져있고 그에 따라 요청에 따른 구동 오버헤드를 피할 수 있어서 사용자가 요청하면 매우 빠른 시간내에 응답할 수 있다.&lt;/span&gt;&lt;/li&gt;
&lt;/ol&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.1.3 YARN 애플리케이션 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아파치 슬라이더같이 YARN을 쉽게 만들 수 있도록 도와주는 프로젝트가 있다. 아파치 슬라이더는 기존의 분산 어플리케이션을 YARN위에서 실행하도록 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉 YARN애플리케이션을 작성하는 것은 매우 어려우므로 기존 프로젝트를 활용하는 것을 추천한다&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.2&amp;nbsp; YARN과 맵리듀스1의 차이점&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;맵리듀스 API와 맵리듀스 구현은 다른 의미이다. 맵리듀스 API는 사용자 측면의 클라이언트 기능이고, 구현은 맵리듀스 프로그램을 실행하는 다른 방식을 의미한다.&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;[맵리듀스 1]&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;span style=&quot;background-color: #f6e199;&quot;&gt;모든 잡을 조율&lt;/span&gt;한다. 잡트래커는 태스크의 실행에 대한 진행상황을 파악하며 태스크가 실패하면 다른 태스크 트래커에 그 태스크를 다시 스케줄링할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잡트래커는 잡 스케줄링(태스크와 태스크 트래커 연결)와 태스크 진행 모니터링(태스크를 추적하고, 실패하거나 느린 태스크를 다시 시작하고 전체 카운터를 유지하는 방법)을 맡고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 즉 잡트래커 하나가 모든 작업에 대한 관리, 스케줄링 등을 진행&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;[맵리듀스 2]&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;YARN 등장.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 역할을 분리된 객체인 리소스 매니저와 애플리케이션 마스터(맵리듀스 잡 당 하나)를 통해 처리한다.&lt;/p&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;style2&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;맵리듀스 1&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;YARN&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;잡 트래커&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;리소스매니저, 애플리케이션 마스터, 타임라인 서버&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;태스크 트래커&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;노드 매니저&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;슬롯&lt;/td&gt;
&lt;td style=&quot;width: 50%;&quot;&gt;컨테이너&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하둡 2.5.1에서 얀의 타임라인 서버는 아직 맵리듀스 잡 이력을 저장하지 못해서 잡 히스토리 서버 데몬은 여전히 필요하다&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;YARN 사용에 따른 이점&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;확장성
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;맵리듀스 1보다 더 큰 클러스터에서 실행 가능. 이는 맵리듀스1이 잡트래커가 잡과 태스크를 모두 관리하기 때문이다.&lt;/li&gt;
&lt;li&gt;YARN은 &lt;span style=&quot;background-color: #f6e199;&quot;&gt;리소스 매니저와 애플리케이션 마스터를 분리&lt;/span&gt;하는 구조&lt;/li&gt;
&lt;li&gt;이에 따라 애플리케이션 마스터는 해당 애플리케이션이 실행될때만 존재한다&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;가용성
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;고가용성은 서비스 데몬에 문제가 발생했을 때 서비스에 필요한 작업을 다른 데몬이 이어받을 수 있도록 상태 정보를 항상 복사해두는 방법으로 구현된다. 하지만 잡 트래커의 메모리에 있는 복잡한 상태 정보가 매우 빠르게 변경되는 상황에서 잡 트래커 서비스에 고가용성을 적용하는 것은 매우 어려운일이다.&lt;/li&gt;
&lt;li&gt;이러한 잡 트래커의 역할이 YARN에서는 분리되었기 때문에 고가용성 서비스가 &lt;u&gt;분할후 정복 문제&lt;/u&gt;로 바뀌었다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;먼저 리소스 매니저의 고가용성을 제공한 후 YARN 애플리케이션을 지원하면 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;하둡2는 리소스 매니저와 맵리듀스 잡을 위한 애플리케이션 마스터 모두에 고가용성을 제공한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;효율성
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;맵리듀스1에서 각 태스크 트래커는 맵슬롯과 리듀스 슬롯으로 구분된 고정크기 슬롯의 정적할당 설정을 가지고 있다. 각 슬롯은 정해진 태스크실행만 가능&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;YARN에서는 노드매니저는 정해진 개수의 슬롯대신 일종의 리소스 풀을 관리&lt;/span&gt;한다.
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;클러스터의 맵슬롯은 남아있지만 리듀스 슬롯이 없어서 리듀스 태스크가 마냥 대기하고 있는 상황은 발생하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;YARN의 자원은 잘게 쪼개져 있어서 애플리케이션은 필요한 만큼만 자원을 요청&lt;/span&gt;할 수 있다. 기존에는 개별 슬롯을 사용했기 때문에 특정 태스크를 위해 너무 많거나(자원 낭비), 너무 적게(실패원인) 자원을 할당했다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;멀티테넌시(다중 사용자)
&lt;ul style=&quot;list-style-type: circle;&quot; data-ke-list-type=&quot;circle&quot;&gt;
&lt;li&gt;하둡이 다양한 분산 애플리케이션을 수용할 수 있게 된것.&lt;/li&gt;
&lt;li&gt;사용자는 서로 다른 버전의 맵리듀스를동일한 얀 클러스터에서 수행하는 것도 가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4.3 YARN 스케줄링&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;YARN 스케줄러의 역할은 정해진 정책에 따라 애플리케이션에 자원을 할당하는 것이다.&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;4.3.1 스케줄러 옵션&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;FIFO 스케줄러
&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;대형 애플리케이션이 수행될때는 클러스터의 모든 자원을 점유해버릴수있기 때문에 무한 대기가 발생&lt;/li&gt;
&lt;/ul&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;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2022-10-09-13-32-23.jpeg&quot; data-origin-width=&quot;2605&quot; data-origin-height=&quot;3523&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cTB5gg/btrN5xijb14/C1KZMP3IAJM6EZ3qij0sD1/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cTB5gg/btrN5xijb14/C1KZMP3IAJM6EZ3qij0sD1/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cTB5gg/btrN5xijb14/C1KZMP3IAJM6EZ3qij0sD1/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcTB5gg%2FbtrN5xijb14%2FC1KZMP3IAJM6EZ3qij0sD1%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;509&quot; height=&quot;688&quot; data-filename=&quot;KakaoTalk_Photo_2022-10-09-13-32-23.jpeg&quot; data-origin-width=&quot;2605&quot; data-origin-height=&quot;3523&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ii) 캐퍼시티 스케줄러는 작은 잡을 제출되는 즉시 분리된 전용 큐에서 처리. 해당 큐는 잡을 위한 자원을 미리 예약해두기 때문에 효율성은 떨어진다. 그리고 대형잡은 FIFO보다 늦게 끝난다.&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;iii) 페어스케줄러는 모든 잡의 자원을 동적으로 분배하기 때문에 미리 자원의 가용량을 예약할 필요가 없다. 대형잡이 실행되는 도중에 작은 잡이 추가로 시작되면 페어 스케줄러는 클러스터 자원의 절반을 이 잡에 할당한다.&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;4.3.2 캐퍼시티 스케줄러 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 조직은 전체 클러스터의 지정된 가용량을 미리 할당받는다. 분리된 전용 큐를 가지며 클러스터 가용량의 지정된 부분을 사용하도록 설정할 수 있다. 그리고 조직에 속한 서로 다른 사용자 그룹사이에도 클러스터의 가용량을 공유하도록 할 수 있다. 단일 큐 내부에 있는 애플리케이션들은 FIFO방식으로 스케줄링 된다.&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;span style=&quot;background-color: #f6e199;&quot;&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;이때 다른 큐의 컨테이너가 완료되어 자원이 해제된 경우에만 해당 큐에 가용량을 돌려준다&lt;/span&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 다른 큐의 가용량을 너무 많이 잡아먹지 않도록 큐에 최대 가용량을 설정하는 방법으로 문제를 해결할 수 있는데 이것이 큐 탄력성의 단점이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;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;u&gt;capacity-scheduler.xml파일&lt;/u&gt;에서 설정가능하다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각 큐는 &lt;u&gt;yarn.scheduler.capacity.&amp;lt;queue-path&amp;gt;.&amp;lt;sub-property&amp;gt;&lt;/u&gt;와 같은 형식의 설정 속성을 정의한 후 설정할 수 있다,.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;lt;queue-path&amp;gt;에는 큐의 계층구조를 정의하면 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1665294494997&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;property&amp;gt;
  &amp;lt;name&amp;gt;yarn.scheduler.capacity.root.queues&amp;lt;/name&amp;gt;
  &amp;lt;value&amp;gt;a,b,c&amp;lt;/value&amp;gt;
  &amp;lt;description&amp;gt;The queues at the this level (root is the root queue).
  &amp;lt;/description&amp;gt;
&amp;lt;/property&amp;gt;

&amp;lt;property&amp;gt;
  &amp;lt;name&amp;gt;yarn.scheduler.capacity.root.a.queues&amp;lt;/name&amp;gt;
  &amp;lt;value&amp;gt;a1,a2&amp;lt;/value&amp;gt;
  &amp;lt;description&amp;gt;The queues at the this level (root is the root queue).
  &amp;lt;/description&amp;gt;
&amp;lt;/property&amp;gt;

&amp;lt;property&amp;gt;
  &amp;lt;name&amp;gt;yarn.scheduler.capacity.root.b.queues&amp;lt;/name&amp;gt;
  &amp;lt;value&amp;gt;b1,b2,b3&amp;lt;/value&amp;gt;
  &amp;lt;description&amp;gt;The queues at the this level (root is the root queue).
  &amp;lt;/description&amp;gt;
&amp;lt;/property&amp;gt;&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;애플리케이션을 큐에 배치하는 방법은 애플리케이션의 종류에 따라 달라진다. 큐를 지정하지 않으면 기본큐인 default에 배치된다.&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;4.3.3 페어 스케줄러 설정&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;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;KakaoTalk_Photo_2022-10-09-13-45-49.jpeg&quot; data-origin-width=&quot;3536&quot; data-origin-height=&quot;1821&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ZSQDC/btrN4xpPykn/P2vW9hxDQwbVuDNawTZJRk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ZSQDC/btrN4xpPykn/P2vW9hxDQwbVuDNawTZJRk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ZSQDC/btrN4xpPykn/P2vW9hxDQwbVuDNawTZJRk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZSQDC%2FbtrN4xpPykn%2FP2vW9hxDQwbVuDNawTZJRk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;540&quot; height=&quot;278&quot; data-filename=&quot;KakaoTalk_Photo_2022-10-09-13-45-49.jpeg&quot; data-origin-width=&quot;3536&quot; data-origin-height=&quot;1821&quot;/&gt;&lt;/span&gt;&lt;/figure&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;u&gt;yarn.resourcemanager.scheduler.class&lt;/u&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;u&gt;yarnsite.xml파일의 yarn.resourcemanager.scheduler.class&lt;/u&gt; 속성에 스케줄러의 전체 클래스 이름을 &lt;u&gt;org.apache.hadoop.yarn.server.resourcemanager.scheduler.fair.FairScheduler&lt;/u&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;u&gt;fair-scheduler.xml&lt;/u&gt; 이라는 할당 파일에 원하는 속성을 설정한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;yarn.scheduler.fair.allocation.file&lt;/u&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;기본 정책은 최상단의 defaultQueueSechedulingPolicy 항목에 설정가능. 페어 스케줄러는 큐에 FIFO정책도 지원하고 우성자원 공평성정책도 사용할 수 있다.&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;특정 큐의 정책은 schedulingPolicy 항목으로 재정의할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;할당 파일의 더 자세한 예제는 &lt;a href=&quot;https://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/FairScheduler.html&quot;&gt;레퍼런스 참고&lt;/a&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1665294721755&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;?xml version=&quot;1.0&quot;?&amp;gt;
&amp;lt;allocations&amp;gt;
	&amp;lt;defaultQueueSchedulingPolicy&amp;gt;fair&amp;lt;/defaultQueueSchedulingPolicy&amp;gt;

	&amp;lt;queue name=&quot;prod&quot;&amp;gt;
		&amp;lt;weight&amp;gt;40&amp;lt;/weight&amp;gt;
		&amp;lt;schedulingPolicy&amp;gt;fifo&amp;lt;/schedulingPolicy&amp;gt;
	&amp;lt;/queue&amp;gt;

	&amp;lt;queue name=&quot;dev&quot;&amp;gt;
		&amp;lt;weight&amp;gt;60&amp;lt;/weight&amp;gt;
		&amp;lt;queue name=&quot;eng&quot; /&amp;gt;
		&amp;lt;queue name=&quot;science&quot; /&amp;gt;
	&amp;lt;/queue&amp;gt;
	
	&amp;lt;queuePlacementPolicy&amp;gt;
		&amp;lt;rule name=&quot;specified&quot; create=&quot;false&quot; /&amp;gt;
		&amp;lt;rule name=&quot;primaryGroup&quot; create=&quot;false&quot; /&amp;gt;
		&amp;lt;rule name=&quot;default&quot; create=&quot;dev.eng&quot; /&amp;gt;
	&amp;lt;/queuePlacementPolicy&amp;gt;
&amp;lt;/allocations&amp;gt;&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;/p&gt;
&lt;pre id=&quot;code_1665294751760&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;&amp;lt;queuePlacementPolicy&amp;gt;
		&amp;lt;rule name=&quot;specified&quot; create=&quot;false&quot; /&amp;gt;
		&amp;lt;rule name=&quot;primaryGroup&quot; create=&quot;false&quot; /&amp;gt;
		&amp;lt;rule name=&quot;default&quot; create=&quot;dev.eng&quot; /&amp;gt;
&amp;lt;/queuePlacementPolicy&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;queuePlacementPolicy&lt;/u&gt; 해당 항목은 규칙목록을 포함. 맞는 규칙이 나올때까지 순서대로 시도한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;yarn.scheduler.fair.user-as-default-queue&lt;/u&gt; 속성을 false로 설정하면 할당 파일을 사용하지 않고도 정책을 지정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;yarn.scheduler.fair.allow-underclared-pools&lt;/u&gt; 속성을 false로 설정하면 사용자는 동적으로 큐를 생성할 수 없게된다.&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;span style=&quot;background-color: #f6e199;&quot;&gt;빈 큐에 잡이 제출되더라도 클러스터에서 이미 실행되고 있는 다른 잡이 자원을 해제해주기전까지 잡을 시작할 수 없다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잡의 시작시간을 어느정도 예측가능하게 만들기 위해 페어스케줄러는 선점이라는 기능을 제공한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;선점은 스케줄러가 자원의 균등공유에 위배되는 큐에서 실행되는 컨테이너를 죽일수 있도록 허용&lt;/span&gt;하는 기능으로, 큐에 할당된 자원은 균등공유 기준을 반드시 따라야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;yarn.scheduler.fair.preemption&lt;/u&gt; 속성을 true로 설정하여 전체적으로 활성화&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;u&gt;최소보장 자원을 받지못한채 지정된 최소 공유 선점 타임아웃이 지나면 스케줄러는 다른 컨테이너를 선취&lt;/u&gt;할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 큐의 기본 타임아웃은 할당파일의 최상위 항목인 &lt;u&gt;defaultMinSharedPreemptionTimeout&lt;/u&gt;으로 설정할 수 있으며, 큐를 기준으로 지정하고 싶으면 각 큐의 &lt;u&gt;minSharePreemptionTimeout&lt;/u&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;4.3.4 지연 스케줄링&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;span style=&quot;background-color: #f6e199;&quot;&gt;지연스케줄링&lt;/span&gt;이라고 부른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;rarr; 캐퍼시티와 페어 스케줄러는 모두 이러한 기능을 제공하고 있다.&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;YARN의 모든 노드매니저는 주기적으로 리소스 매니저에 하트비트 요청을 보낸다. 하트비트를 통해 노드 매니저가 실행 중인 컨테이너의 정보와 새로운 컨테이너를 위한 가용한 자원에 대한 정보를 주고 받는다. &lt;span style=&quot;background-color: #f6e199;&quot;&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;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;u&gt;yarn.scheduler.capacity.node-locality-delay&lt;/u&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;4.3.5 우성 자원 공평성&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;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어, 클러스터 전체 CPU는 100개이고, 10TB의 메모리를 가지고 있다. A는 CPU2개와 300GB메모리의 컨테이너를, B는 CPU 6개와, 100GB 메모리의 컨테이너를 각각 요청했다. A의 요청은 전체 클러스터의 2%와 3%이므로, 메모리의 비율이 더 높다. B의 요청은 CPU(6%)가 더 우세하다. B의 요청을 우세자원을 기준으로 비교해보면 A보다 두배나 높다. 따라서 A는 B의 절반에 해당하는 자원을 할당 받는다.&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;기본적으로 해당 기능은 비활성화되어있고 자원을 계산할때는 CPU는 무시되고 메모리만 고려된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 기능을 활성화하기 위해서는 &lt;u&gt;capacityscheduler.xml&lt;/u&gt; 파일에 있는 &lt;u&gt;yarn.scheduler.capacity.resource-calculator&lt;/u&gt; 속성에 &lt;u&gt;org.apache.hadoop.yarn.uril.resource.DominateResourceCalculator&lt;/u&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;Reference&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://mangkyu.tistory.com/127&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;yarn의 구조와 구동 방식&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1665294583226&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;[Hadoop] YARN의 구조와 동작 방식&quot; data-og-description=&quot;1. Hadoop 1.0의 구조와 YARN의 등장 이유 [ Hadoop 1.0의 구조와 YARN의 등장&amp;nbsp;] Hadoop 1.0의 MRV1(MapReduce Version1)는 작업의 처리와 자원의 관리를 한번에 관리하였다. 즉, Single-Master 노드에 해당하는..&quot; data-og-host=&quot;mangkyu.tistory.com&quot; data-og-source-url=&quot;https://mangkyu.tistory.com/127&quot; data-og-url=&quot;https://mangkyu.tistory.com/127&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/motFX/hyP4DtidQH/78I17tKeuK7gZA6USmQZCk/img.png?width=800&amp;amp;height=418&amp;amp;face=0_0_800_418,https://scrap.kakaocdn.net/dn/FHGgc/hyP4Mjs128/IQL7Ng4sFA2fuxqrs070U0/img.png?width=800&amp;amp;height=418&amp;amp;face=0_0_800_418,https://scrap.kakaocdn.net/dn/caDL56/hyP4Lx5QUW/wRrvbv2ayzXqisz81mYxuk/img.png?width=1174&amp;amp;height=614&amp;amp;face=0_0_1174_614&quot;&gt;&lt;a href=&quot;https://mangkyu.tistory.com/127&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://mangkyu.tistory.com/127&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/motFX/hyP4DtidQH/78I17tKeuK7gZA6USmQZCk/img.png?width=800&amp;amp;height=418&amp;amp;face=0_0_800_418,https://scrap.kakaocdn.net/dn/FHGgc/hyP4Mjs128/IQL7Ng4sFA2fuxqrs070U0/img.png?width=800&amp;amp;height=418&amp;amp;face=0_0_800_418,https://scrap.kakaocdn.net/dn/caDL56/hyP4Lx5QUW/wRrvbv2ayzXqisz81mYxuk/img.png?width=1174&amp;amp;height=614&amp;amp;face=0_0_1174_614');&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;[Hadoop] YARN의 구조와 동작 방식&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;1. Hadoop 1.0의 구조와 YARN의 등장 이유 [ Hadoop 1.0의 구조와 YARN의 등장&amp;nbsp;] Hadoop 1.0의 MRV1(MapReduce Version1)는 작업의 처리와 자원의 관리를 한번에 관리하였다. 즉, Single-Master 노드에 해당하는..&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;mangkyu.tistory.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/FairScheduler.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;하둡 공식 문서&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;figure id=&quot;og_1665294630043&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Apache Hadoop 3.3.4 &amp;ndash; Hadoop: Fair Scheduler&quot; data-og-description=&quot;&amp;lt;!--- Licensed under the Apache License, Version 2.0 (the &amp;quot;License&amp;quot;); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or a&quot; data-og-host=&quot;hadoop.apache.org&quot; data-og-source-url=&quot;https://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/FairScheduler.html&quot; data-og-url=&quot;https://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/FairScheduler.html&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/FairScheduler.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hadoop.apache.org/docs/current/hadoop-yarn/hadoop-yarn-site/FairScheduler.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&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;Apache Hadoop 3.3.4 &amp;ndash; Hadoop: Fair Scheduler&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;&amp;lt;!--- Licensed under the Apache License, Version 2.0 (the &quot;License&quot;); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or a&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;hadoop.apache.org&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;</description>
      <category>WIR(What I Read)</category>
      <category>Yarn</category>
      <category>하둡완벽가이드</category>
      <author>Hazel_song</author>
      <guid isPermaLink="true">https://hazel-developer.tistory.com/304</guid>
      <comments>https://hazel-developer.tistory.com/304#entry304comment</comments>
      <pubDate>Sun, 9 Oct 2022 14:56:44 +0900</pubDate>
    </item>
  </channel>
</rss>