<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>2JINISHAPPY</title>
    <link>https://2jinishappy.tistory.com/</link>
    <description>Binary Coding Blog</description>
    <language>ko</language>
    <pubDate>Wed, 8 Apr 2026 23:37:29 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>이진2</managingEditor>
    <image>
      <title>2JINISHAPPY</title>
      <url>https://tistory1.daumcdn.net/tistory/3043840/attach/a3fb4fcb18c14ffa8a01639dd7f65299</url>
      <link>https://2jinishappy.tistory.com</link>
    </image>
    <item>
      <title>분산 시스템의 재앙, Split Brain: 원인 분석부터 글로벌 기업의 대응 전략까지</title>
      <link>https://2jinishappy.tistory.com/358</link>
      <description>&lt;p&gt;오늘은 고가용성(HA, High Availability) 시스템을 설계하는 엔지니어라면 반드시 정복해야 할 숙제, &lt;strong&gt;Split Brain(스플릿 브레인)&lt;/strong&gt; 현상을 심층적으로 다루어 보겠습니다. &lt;/p&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 &quot;서버가 두 개가 된다&quot;는 수준을 넘어, 왜 이 현상이 데이터 정합성을 파괴하는지, 그리고 글로벌 기업들은 어떤 엔지니어링 기법으로 이를 방어하고 있는지 정리했습니다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Split Brain: 왜 분산 시스템의 '악몽'인가?&lt;/h3&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.1 개념 정의&lt;/h3&gt;&lt;p&gt;분산 시스템에서 &lt;strong&gt;Split Brain&lt;/strong&gt;은 네트워크 단절(Network Partition)로 인해 클러스터가 두 개 이상의 독립적인 그룹으로 쪼개지고, 각 그룹이 서로의 존재를 알지 못한 채 &lt;strong&gt;스스로를 Master(Leader)로 승격시키는 현상&lt;/strong&gt;을 말합니다.&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;1.2 CAP 정리와 Partition Tolerance&lt;/h3&gt;&lt;p&gt;분산 시스템의 근간인 &lt;strong&gt;CAP 정리&lt;/strong&gt;에 따르면, 네트워크 분절(P)이 발생했을 때 시스템은 일관성(C)과 가용성(A) 중 하나를 선택해야 합니다.&lt;br&gt;Split Brain은 가용성을 억지로 유지하려다 일관성이 완전히 깨져버리는 시나리오입니다. 두 개의 Master가 각각 다른 쓰기 요청을 수락하면, 네트워크가 복구된 후 데이터는 복구 불가능한 &lt;strong&gt;'뇌 분리'&lt;/strong&gt; 상태에 빠지게 됩니다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 글로벌 사례 분석: GitHub의 2018년 24시간 서비스 중단&lt;/h3&gt;&lt;p&gt;Split Brain이 실제 비즈니스에 어떤 타격을 주는지 보여주는 가장 유명한 사례는 &lt;strong&gt;2018년 10월 발생한 GitHub의 데이터베이스 장애&lt;/strong&gt;입니다.&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;  장애 전개 (Timeline)&lt;/h3&gt;&lt;ol&gt; 
 &lt;li&gt;&lt;strong&gt;발단:&lt;/strong&gt; US 동부와 US 서부 데이터센터 간의 광케이블 문제로 약 43초간 네트워크가 단절되었습니다.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;현상:&lt;/strong&gt; GitHub이 사용하는 MySQL HA 솔루션인 &lt;code&gt;Orchestrator&lt;/code&gt;가 서부 노드와 통신이 끊기자, 동부 노드를 새로운 Master로 승격시켰습니다.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;문제:&lt;/strong&gt; 하지만 서부 노드(기존 Master)는 여전히 살아있었고, 일부 애플리케이션 서버는 여전히 서부 노드에 쓰기 요청을 보내고 있었습니다.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;결과:&lt;/strong&gt; 양쪽 데이터센터에 서로 다른 데이터가 쌓이는 &lt;strong&gt;데이터 정합성 오류&lt;/strong&gt;가 발생했고, GitHub은 이를 수동으로 대조하여 복구하느라 24시간 넘게 서비스 장애를 겪었습니다.&lt;/li&gt; 
&lt;/ol&gt;&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
 &lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&lt;/span&gt;&lt;/p&gt;
 &lt;p&gt;&lt;strong&gt;Key Insight:&lt;/strong&gt; &quot;Failover(장애 조치)는 빠를수록 좋지만, '정확하지 않은' Failover는 안 하느니만 못하다.&quot;&lt;/p&gt; 
 &lt;p&gt;&lt;/p&gt;
&lt;/blockquote&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 해결을 위한 엔지니어링 메커니즘&lt;/h3&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.1 분산 합의 알고리즘 (Raft &amp;amp; Paxos)&lt;/h3&gt;&lt;p&gt;현대 분산 시스템(etcd, Kubernetes, Consul 등)은 &lt;strong&gt;Raft 알고리즘&lt;/strong&gt;을 통해 Split Brain을 방어합니다. 핵심은 &lt;strong&gt;Quorum(의사정족수)&lt;/strong&gt;입니다.&lt;/p&gt;&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;공식:&lt;/strong&gt; $N/2 + 1$ (전체 노드의 과반수 동의 필요)&lt;/li&gt; 
 &lt;li&gt;노드가 3대라면, 최소 2대의 동의를 얻어야 Leader가 됩니다. 네트워크가 1:2로 쪼개지면 1대인 그룹은 절대 Leader가 될 수 없으므로 쓰기 작업을 중단합니다.&lt;/li&gt; 
&lt;/ul&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;3.2 I/O Fencing: &quot;상대방의 손발을 묶어라&quot;&lt;/h3&gt;&lt;p&gt;합의만으로는 부족할 때가 있습니다. 확실한 격리를 위해 &lt;strong&gt;Fencing(울타리 치기)&lt;/strong&gt; 기법을 사용합니다.&lt;/p&gt;&lt;ol&gt; 
 &lt;li&gt;&lt;strong&gt;STONITH (Shoot The Other Node In The Head):&lt;/strong&gt; 네트워크 응답이 없는 노드의 전원을 원격(IPMI 등)에서 강제로 꺼버립니다.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Resource Fencing:&lt;/strong&gt; 공유 스토리지 레벨에서 특정 노드의 접근 권한을 즉시 차단합니다.&lt;/li&gt; 
&lt;/ol&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 실전 코드 및 설정 가이드&lt;/h3&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.1 Corosync/Pacemaker를 이용한 Quorum 설정&lt;/h3&gt;&lt;p&gt;리눅스 클러스터링 도구인 &lt;code&gt;Corosync&lt;/code&gt;에서 Split Brain을 방지하기 위한 핵심 설정 예시입니다.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# /etc/corosync/corosync.conf 핵심 설정

totem {
    version: 2
    cluster_name: my_cluster
    transport: udpu
}

quorum {
    # 쿼럼 방식 설정
    provider: corosync_votequorum
    # 노드 수가 2개일 때 특별 처리 (가급적 3개 이상 권장)
    two_node: 0 
    # 과반수 미달 시 서비스 중단 여부 (중요!)
    last_man_standing: 1
    # 쿼럼을 잃었을 때 동작: 'stop'으로 설정하여 데이터 오염 방지
    wait_for_all: 1
}&lt;/code&gt;&lt;/pre&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;4.2 Python으로 이해하는 Quorum 로직 (Pseudo-code)&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 코드로 Leader 선출 시 Quorum을 체크하는 로직을 시뮬레이션해 보겠습니다.&lt;/p&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot;&gt;import time

class Node:
    def __init__(self, node_id, total_nodes):
        self.node_id = node_id
        self.total_nodes = total_nodes
        self.votes = 0
        self.is_leader = False

    def request_vote(self):
        &quot;&quot;&quot;
        다른 노드들에게 투표를 요청하는 메서드
        실제 환경에서는 RPC(Remote Procedure Call)로 구현됩니다.
        &quot;&quot;&quot;
        # 네트워크 단절 상황을 가정한 투표 획득 로직
        self.votes = self.get_network_votes() 

        # 쿼럼 체크: 과반수(N/2 + 1)를 넘었는가?
        quorum_size = (self.total_nodes // 2) + 1

        if self.votes &amp;gt;= quorum_size:
            self.is_leader = True
            print(f&quot;[Node {self.node_id}] Quorum 확보 성공! Leader로 승격합니다.&quot;)
        else:
            self.is_leader = False
            print(f&quot;[Node {self.node_id}] Quorum 확보 실패 (표수: {self.votes}/{quorum_size}). Standby 유지.&quot;)

    def get_network_votes(self):
        # 네트워크 상황에 따라 연결 가능한 노드 수 반환
        pass

# 3개의 노드 중 1개만 연결된 상황 시뮬레이션
node_1 = Node(node_id=1, total_nodes=3)
node_1.votes = 1 # 자신만 연결됨
node_1.request_vote() # 결과: Standby 유지&lt;/code&gt;&lt;/pre&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 엔지니어를 위한 Split Brain 방어 설계 원칙 ✅&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;단순히 &quot;서버를 여러 대 둔다&quot;는 것만으로는 부족합니다. 실제 운영 환경에서 Split Brain을 원천 차단하거나 피해를 최소화하기 위한 3가지 핵심 설계 원칙을 제안합니다.&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.1 홀수 노드 구성과 Quorum 알고리즘의 수학적 근거&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;분산 시스템에서 노드 수를 홀수로 구성하는 것은 선택이 아닌 필수입니다. &lt;/p&gt;&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;2개 노드 구성 시:&lt;/strong&gt; 네트워크가 1:1로 단절되면 양쪽 모두 과반수(2/2=1)를 점유할 수 없어 전체 서비스가 중단되거나, 혹은 양쪽 모두 Master가 되는 Split Brain이 발생하기 쉽습니다.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;3개 노드 구성 시:&lt;/strong&gt; 1:2로 단절될 경우, 2대인 그룹이 과반수($3/2 + 1 = 2$)를 확보하여 서비스를 이어가고, 1대인 노드는 스스로 격리됩니다.&lt;/li&gt; 
&lt;/ul&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.2 &quot;안전하게 죽기(Fail-fast)&quot; 전략&lt;/h3&gt;&lt;p&gt;시스템이 스스로의 상태를 확신할 수 없을 때는 &lt;strong&gt;과감하게 프로세스를 종료하는 것&lt;/strong&gt;이 데이터를 오염시키는 것보다 훨씬 낫습니다.&lt;/p&gt;&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Watchdog 활용:&lt;/strong&gt; 하드웨어 또는 소프트웨어 Watchdog을 설정하여, 클러스터 엔진(Corosync 등)이 일정 시간 응답하지 않으면 커널 패닉(Kernel Panic)을 유발해 강제로 시스템을 리부트시킵니다.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Self-Fencing:&lt;/strong&gt; 노드가 쿼럼을 잃었다고 판단되는 즉시 모든 I/O를 중단하고 Read-only 모드로 전환하거나 프로세스를 Kill 하는 로직을 애플리케이션 레벨에서 구현해야 합니다.&lt;/li&gt; 
&lt;/ul&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;5.3 지연 시간(Latency)과 타임아웃의 상관관계&lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;글로벌 서비스에서는 네트워크 지연이 일시적으로 발생할 수 있습니다. 이때 타임아웃 설정이 너무 짧으면 빈번한 Failover로 인해 오히려 Split Brain 위험이 커집니다.&lt;/p&gt;&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;Heartbeat Timeout &amp;gt; Network Jitter:&lt;/strong&gt; 네트워크 미세 떨림보다 하트비트 임계치를 길게 설정하되,&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Detection Time &amp;lt; Data Inconsistency Time:&lt;/strong&gt; 데이터가 꼬이기 시작하는 시간보다는 빠르게 감지해야 합니다.&lt;/li&gt; 
&lt;/ul&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;6. [Advanced] Cloud Native 시대의 Split Brain&lt;/h3&gt;&lt;p&gt;최근 쿠버네티스(Kubernetes) 환경에서도 &lt;code&gt;etcd&lt;/code&gt; 클러스터의 Split Brain은 매우 중요한 이슈입니다.&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;etcd의 대응 방식&lt;/h3&gt;&lt;p&gt;쿠버네티스의 두뇌인 &lt;code&gt;etcd&lt;/code&gt;는 &lt;strong&gt;Raft 알고리즘&lt;/strong&gt;을 극단적으로 활용합니다. 만약 &lt;code&gt;etcd&lt;/code&gt;가 Split Brain 상태에 빠지면, 쿠버네티스 API 서버는 상태를 업데이트할 수 없게 되어 클러스터 전체의 컨트롤 플레인이 마비됩니다. &lt;/p&gt;&lt;p&gt;&lt;strong&gt;실제 클라우드 환경의 예 (AWS Multi-AZ):&lt;/strong&gt;&lt;br&gt;가용 영역(Availability Zone) 간의 네트워크 단절이 발생했을 때, AWS는 &lt;code&gt;Elastic IP&lt;/code&gt;와 &lt;code&gt;Route53&lt;/code&gt;의 헬스 체크를 통해 트래픽을 한쪽으로 강제 유도하지만, DB 레벨에서는 여전히 Split Brain 위험이 존재합니다. 이때는 &lt;strong&gt;'Cloud-native Fencing'&lt;/strong&gt;인 AWS API를 호출하여 반대편 노드의 인스턴스를 강제로 터미네이트(Terminate)하는 방식을 사용하기도 합니다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot;&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. 결론 및 인사이트: &quot;정합성은 엔지니어의 자존심이다&quot;  &lt;/h3&gt;&lt;p data-ke-size=&quot;size16&quot;&gt;Split Brain은 분산 시스템을 다루는 엔지니어에게 피할 수 없는 숙명과도 같습니다. 우리는 '절대 끊기지 않는 네트워크'를 만들 수 없기 때문입니다.&lt;/p&gt;&lt;h3 data-ke-size=&quot;size23&quot;&gt;  핵심 요약 (TL;DR)&lt;/h3&gt;&lt;ol&gt; 
 &lt;li&gt;&lt;strong&gt;현상:&lt;/strong&gt; 네트워크 단절로 인해 다수의 Master가 발생하여 데이터 정합성이 깨지는 현상.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;원인:&lt;/strong&gt; 물리적 네트워크 장애, 과도한 시스템 부하로 인한 하트비트 응답 지연.&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;해결책:&lt;/strong&gt; * &lt;strong&gt;소프트웨어:&lt;/strong&gt; Raft/Paxos 알고리즘을 통한 &lt;strong&gt;Quorum(다수결)&lt;/strong&gt; 확보.
  &lt;ul&gt; 
   &lt;li&gt;&lt;strong&gt;하드웨어:&lt;/strong&gt; STONITH 등 &lt;strong&gt;Fencing(강제 격리)&lt;/strong&gt; 기법 적용.&lt;/li&gt; 
   &lt;li&gt;&lt;strong&gt;설계:&lt;/strong&gt; 반드시 &lt;strong&gt;홀수 노드&lt;/strong&gt;로 구성하고, 모호할 땐 &lt;strong&gt;Fail-fast&lt;/strong&gt; 할 것.&lt;/li&gt; 
  &lt;/ul&gt; &lt;/li&gt; 
&lt;/ol&gt;&lt;p&gt;&lt;strong&gt;정리&lt;/strong&gt;&lt;br&gt;고가용성(HA)의 목적은 서비스의 영속성이지만, 그 기반에는 &lt;strong&gt;데이터의 무결성&lt;/strong&gt;이 있어야 합니다. 잘못된 Failover로 데이터가 꼬이는 것보다, 차라리 잠시 서비스를 멈추고 데이터를 보호하는 것이 더 성숙한 엔지니어링 의사결정일 수 있습니다.&lt;/p&gt;&lt;hr data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style1&quot;&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;[SRE 관점에서 본 장애 회고(Post-mortem) 작성법]&lt;/li&gt;&lt;li&gt;[etcd와 Raft 알고리즘: 쿠버네티스는 어떻게 합의하는가?]&lt;/li&gt;&lt;li&gt;[MySQL Group Replication으로 구현하는 무결성 클러스터]&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;strong&gt;참조 및 출처&lt;/strong&gt;&lt;/p&gt;&lt;ul&gt; 
 &lt;li&gt;&lt;strong&gt;GitHub Engineering Blog:&lt;/strong&gt; &lt;a href=&quot;https://github.blog/2018-10-30-october-21-post-mortem/&quot;&gt;October 21st post-mortem&lt;/a&gt;&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Martin Kleppmann:&lt;/strong&gt; &lt;a href=&quot;https://dataintensive.net/&quot;&gt;Designing Data-Intensive Applications (O'Reilly)&lt;/a&gt;&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;AWS Documentation:&lt;/strong&gt; &lt;a href=&quot;https://docs.aws.amazon.com/whitepapers/latest/real-time-communication-on-aws/high-availability-guidelines.html&quot;&gt;Guidelines for Implementing High Availability&lt;/a&gt;&lt;/li&gt; 
 &lt;li&gt;&lt;strong&gt;Linux Foundation:&lt;/strong&gt; &lt;a href=&quot;https://clusterlabs.org/pacemaker/doc/&quot;&gt;Cluster Labs - Pacemaker/Corosync Administration&lt;/a&gt;&lt;/li&gt; 
&lt;/ul&gt;</description>
      <category>Computer Science/Software Engineering</category>
      <author>이진2</author>
      <guid isPermaLink="true">https://2jinishappy.tistory.com/358</guid>
      <comments>https://2jinishappy.tistory.com/358#entry358comment</comments>
      <pubDate>Thu, 22 Jan 2026 18:30:53 +0900</pubDate>
    </item>
    <item>
      <title>이벤트 응모 시스템으로 배우는 Redis 분산 락(Distributed Lock) 해결 가이드  </title>
      <link>https://2jinishappy.tistory.com/357</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요! 오늘은 백엔드 개발자라면 한 번쯤은 마주하게 되는, 혹은 면접 단골 질문이기도 한 '동시성 제어'에 대해 이야기해보려 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선착순 이벤트나 한정판 상품 판매 시스템을 구축할 때, 수만 명의 사용자가 동시에 '구매' 버튼을 누르면 어떤 일이 벌어질까요? 단순히 DB의 값을 업데이트하는 방식으로는 데이터의 원자성(Atomicity)을 보장하기 어렵습니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 그 해결책 중 하나인 &lt;b&gt;Redis 분산 락&lt;/b&gt;을 활용해 문제를 해결하는 방법을 알아보겠습니다!  &amp;zwj; &lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 왜 DB 락(Lock)만으로는 부족할까?  &lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일반적으로 관계형 데이터베이스(RDBMS)가 제공하는 Pessimistic Lock(비관적 락)이나 Optimistic Lock(낙관적 락)을 떠올릴 수 있습니다. 하지만 다음과 같은 상황에서는 한계가 명확하죠.&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; DB 자체에 락을 걸면 트랜잭션이 길어질수록 DB 커넥션 점유 시간이 늘어나 전체 처리량이 떨어집니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;분산 환경:&lt;/b&gt; 여러 대의 서버 인스턴스가 동작하는 MSA 환경에서는 각 서버의 로컬 락(e.g. Java의 synchronized)이 무용지물이 됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 활용하기 좋은 도구가 바로 &lt;b&gt;Redis&lt;/b&gt;입니다. 메모리 기반이라 빠르고, 모든 명령어가 싱글 스레드로 동작하여 원자성을 보장하기 때문이죠.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Redisson 라이브러리를 이용한 분산 락 구현  ️&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Redis를 이용해 직접 SETNX 명령어로 락을 구현할 수도 있지만, 타임아웃 처리나 재시도 로직을 직접 짜기엔 위험 요소가 많습니다. 그래서 Java 진영에서는 &lt;b&gt;Redisson&lt;/b&gt; 라이브러리를 주로 사용합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  핵심 코드 예제 (Spring Boot)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재고를 감소시키는 로직에 분산 락을 적용해 보겠습니다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot;&gt;&lt;code&gt;@Service
@RequiredArgsConstructor
public class StockService {

    private final RedissonClient redissonClient;
    private final StockRepository stockRepository;

    public void decreaseStock(Long itemId, Long quantity) {
        // 1. Lock 객체 가져오기 (고유한 키 값 설정)
        RLock lock = redissonClient.getLock(&quot;ITEM_LOCK:&quot; + itemId);

        try {
            // 2. 락 획득 시도 (waitTime: 락 획득 대기 시간, leaseTime: 락 점유 시간)
            boolean available = lock.tryLock(10, 1, TimeUnit.SECONDS);

            if (!available) {
                throw new RuntimeException(&quot;락 획득에 실패했습니다. 잠시 후 다시 시도해주세요.&quot;);
            }

            // 3. 비즈니스 로직 수행 (재고 감소)
            Stock stock = stockRepository.findById(itemId).orElseThrow();
            stock.decrease(quantity);
            stockRepository.save(stock);

        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            // 4. 반드시 락 해제 (획득한 스레드인지 확인 필수)
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;  코드 포인트 설명&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;tryLock&lt;/b&gt;: 무한정 기다리지 않고 지정된 시간 동안만 락 획득을 시도합니다. 시스템 전체의 행(Hang) 걸림을 방지하죠.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;isHeldByCurrentThread&lt;/b&gt;: 락을 획득하지 못한 스레드가 다른 스레드의 락을 해제하는 불상사를 막아줍니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pub/Sub 방식&lt;/b&gt;: Redisson은 락이 해제될 때 대기 중인 스레드에게 알림을 주는 방식을 사용하여 Redis에 주는 부하를 줄입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 주의할 점: 트랜잭션의 커밋 시점 ⚠️&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분산 락을 사용할 때 가장 많이 하는 실수 중 하나는 &lt;b&gt;@Transactional과 분산 락의 순서&lt;/b&gt;입니다.&lt;/p&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;b&gt;락 획득&lt;/b&gt; (Lock Acquire)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;트랜잭션 시작&lt;/b&gt; &amp;amp; 비즈니스 로직 수행&lt;/li&gt;
&lt;li&gt;&lt;b&gt;트랜잭션 커밋&lt;/b&gt; (Transaction Commit)&lt;/li&gt;
&lt;li&gt;&lt;b&gt;락 해제&lt;/b&gt; (Lock Release)&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;왜 그럴까요?&lt;/b&gt; 만약 트랜잭션이 커밋되기 전에 락이 해제되어 버리면, 대기하던 다른 스레드가 즉시 락을 획득하고 데이터를 읽어갑니다. 하지만 이전 스레드의 변경 사항이 아직 DB에 최종 반영(Commit)되지 않았기 때문에, 다른 스레드는 업데이트 전의 데이터를 읽어 '재고 초과'와 같은 정합성 오류를 일으키게 됩니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 실무에서는 보통 서비스 레이어 외부에서 락을 잡고 내부 서비스를 호출하는 &lt;b&gt;Facade 패턴&lt;/b&gt;을 주로 사용합니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 요약 및 인사이트  &lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드 개발자로서 기술을 선택할 때는 항상 트레이드오프(Trade-off)를 고민해야 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비교 항목 DB Lock (Pessimistic) Redis (Redisson)&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;속도&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;상대적으로 느림&lt;/td&gt;
&lt;td&gt;매우 빠름 (In-memory)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;복잡도&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;단순함 (SQL 수준)&lt;/td&gt;
&lt;td&gt;별도 인프라(Redis) 필요&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;효율성&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;커넥션 점유로 부하 발생 가능&lt;/td&gt;
&lt;td&gt;Pub/Sub 방식으로 부하 적음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;b&gt;권장 상황&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;충돌이 적고 단일 DB일 때&lt;/td&gt;
&lt;td&gt;초당 요청이 많고 분산 환경일 때&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 한 줄 요약&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분산 환경에서 데이터 정합성을 확실히 지키고 싶다면, 트랜잭션 범위보다 넓게 설계된 Redis 분산 락을 적극 검토해보자!  &lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&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;[Redis Pub/Sub 구조 깊이 알아보기]&lt;/li&gt;
&lt;li&gt;[Spring Boot에서 Facade 패턴으로 분산 락 깔끔하게 관리하기]&lt;/li&gt;
&lt;li&gt;[DB 격리 수준(Isolation Level)과 동시성 제어의 관계]&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&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;Redisson 공식 가이드: &lt;a href=&quot;https://redisson.org/&quot;&gt;https://redisson.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;마틴 클레프만 저, &amp;lt;데이터 중심 애플리케이션 설계&amp;gt; (동시성 파트)&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Project</category>
      <author>이진2</author>
      <guid isPermaLink="true">https://2jinishappy.tistory.com/357</guid>
      <comments>https://2jinishappy.tistory.com/357#entry357comment</comments>
      <pubDate>Wed, 21 Jan 2026 23:14:57 +0900</pubDate>
    </item>
    <item>
      <title>서버 장애의 80%는 여기서 시작된다? 커넥션 풀(Connection Pool) 최적화 가이드  </title>
      <link>https://2jinishappy.tistory.com/356</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;안녕하세요! 5년 차 주니어 개발자입니다. 오늘은 백엔드 성능의 핵심 보루, **커넥션 풀(Connection Pool, CP)**에 대해 깊이 있게 다뤄보려 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;사용자가 많아지면 무조건 풀 크기를 늘리면 될까?&quot; &quot;언제 풀이 부족하다는 걸 알 수 있을까?&quot; 실무에서 마주하는 이 질문들에 대해 명쾌한 해답과 모니터링 노하우를 공유합니다!  &lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 커넥션 풀(Connection Pool)이란?  &amp;zwj;♂️&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB 커넥션 풀은 미리 일정한 수의 **데이터베이스 연결(Connection)**을 만들어 '풀(Pool)' 속에 보관해 두었다가, 요청이 올 때마다 빌려주고 반납받는 방식입니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;왜 직접 연결하지 않고 '풀'을 사용하나요? (Why?)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB와 매번 새로 연결하는 작업(TCP 3-way handshake 등)은 비용이 매우 비쌉니다.&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;b&gt;자원 보호&lt;/b&gt;: 무한정 연결이 생기는 것을 방지하여 DB 서버의 부하를 조절합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 실전! 커넥션 풀을 언제 늘리고 줄여야 할까?  &lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정값보다 중요한 것이 &lt;b&gt;현재 상태를 확인하는 방법&lt;/b&gt;입니다. 서비스 모니터링(Grafana, CloudWatch 등) 시 다음 지표를 유심히 보세요.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 이런 상황엔 &quot;늘려야 합니다&quot; (Scale-Up)&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;Connection Wait Time 증가&lt;/b&gt;: 클라이언트가 커넥션을 빌리기 위해 대기하는 시간이 길어진다면, 현재 풀이 모든 요청을 소화하지 못하고 있다는 뜻입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Pool Usage(사용률)가 90% 이상&lt;/b&gt;: 피크 타임에 사용률이 지속적으로 높다면 여유분을 확보해야 합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Active Connections vs Pending Threads&lt;/b&gt;: 활성 커넥션은 꽉 찼는데, 커넥션을 기다리는 스레드(Pending)가 쌓이고 있다면 증설이 필요합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 이런 상황엔 &quot;줄여야 합니다&quot; (Scale-Down)&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;DB 서버 CPU/Memory 부하&lt;/b&gt;: WAS는 멀쩡한데 DB 서버의 CPU 사용률이 너무 높다면, 너무 많은 커넥션이 Context Switching을 일으키고 있을 확률이 큽니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Idle Connections 과다&lt;/b&gt;: 평소에 사용되지 않고 노는 커넥션이 너무 많다면 메모리 낭비입니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Deadlock 발생 빈도 증가&lt;/b&gt;: 커넥션이 너무 많으면 트랜잭션 간 경합이 심해져 데드락이 더 자주 발생할 수 있습니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 주니어 개발자가 흔히 하는 착각: &quot;많을수록 좋다?&quot; ❌&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;차선이 많으면 무조건 차가 안 밀리겠지?&quot;라고 생각하기 쉽지만, DB는 그렇지 않습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;  HikariCP 공식 가이드의 권장 공식&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 널리 쓰이는 커넥션 풀 라이브러리인 HikariCP는 아래와 같은 공식을 제안합니다.&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;$$connections = ((core\\_count * 2) + effective\\_spindle\\_count)$$
&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;핵심&lt;/b&gt;: DB 서버의 CPU 코어 수에 맞춰 적절한 제한을 두는 것이, 무작정 늘리는 것보다 훨씬 빠릅니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 실무에서 바로 써먹는 설정 튜닝 포인트  ️&lt;/h2&gt;
&lt;pre class=&quot;yaml&quot;&gt;&lt;code&gt;# Spring Boot application.yml 예시
spring:
  datasource:
    hikari:
      maximum-pool-size: 20 # 부하 테스트를 통해 결정 (보통 10~50 사이)
      minimum-idle: 20      # maximum-pool-size와 동일하게 설정 권장
      connection-timeout: 3000 # 3초 이상 대기 시 에러 (사용자 경험 보호)
      max-lifetime: 1800000    # DB의 wait_timeout보다 무조건 2~3분 짧게!

&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로젝트 설정 파일(application.yml)에 적용할 핵심 파라미터입니다.&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;maximum-pool-size&lt;/b&gt;: 최대 풀 크기. 보통 10~50 사이에서 부하 테스트 후 결정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;minimum-idle&lt;/b&gt;: 최소 유휴 연결 수. 성능을 위해 maximum-pool-size와 동일하게 맞추는 것을 권장합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;connection-timeout&lt;/b&gt;: 커넥션 획득 대기 시간. 보통 3000ms(3초) 내외로 설정합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;max-lifetime&lt;/b&gt;: 커넥션 최대 수명. &lt;b&gt;DB의 wait_timeout보다 무조건 2~3분 짧게&lt;/b&gt; 설정하는 것이 장애 방지의 핵심입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 요약 및 마무리  &lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;커넥션 풀은 서버의 '호흡기'와 같습니다. 단순히 값을 크게 잡는 것이 아니라, 모니터링 지표를 바탕으로 우리 서비스에 맞는 최적의 값을 찾아가야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;한 줄 요약&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;커넥션 풀 튜닝의 핵심은 모니터링 지표를 바탕으로 DB 서버가 감당할 수 있는 최적의 '한계치'를 찾는 것이다.&quot;  &lt;/p&gt;
&lt;/blockquote&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;[Database] 인덱스 설정 하나로 쿼리 속도 10배 높이기&lt;/li&gt;
&lt;li&gt;[Java] HikariCP 내부 동작 원리 깊이 파헤치기&lt;/li&gt;
&lt;li&gt;[Infra] nGrinder를 이용한 API 부하 테스트 실습&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  출처 및 참고 자료&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;HikariCP Wiki&lt;/b&gt;: &lt;a href=&quot;https://github.com/brettwooldridge/HikariCP/wiki/About-Pool-Sizing&quot;&gt;About Pool Sizing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Baeldung&lt;/b&gt;: &lt;a href=&quot;https://www.baeldung.com/hikaricp&quot;&gt;A Guide to HikariCP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;MySQL Docs&lt;/b&gt;: &lt;a href=&quot;https://dev.mysql.com/doc/refman/8.0/en/connection-threads.html&quot;&gt;How MySQL Handles Client Connections&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Oracle Blog&lt;/b&gt;: &lt;a href=&quot;https://blogs.oracle.com/database/post/real-world-performance-video-series-connection-pooling&quot;&gt;Real World Connection Pool Sizing&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Computer Science/Database</category>
      <author>이진2</author>
      <guid isPermaLink="true">https://2jinishappy.tistory.com/356</guid>
      <comments>https://2jinishappy.tistory.com/356#entry356comment</comments>
      <pubDate>Tue, 20 Jan 2026 18:00:47 +0900</pubDate>
    </item>
    <item>
      <title>네트워크 대역폭(Bandwidth)이란?</title>
      <link>https://2jinishappy.tistory.com/355</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오늘은 백엔드 서버를 설계하고 운영할 때 절대 간과해서는 안 될 요소, 바로 &lt;b&gt;네트워크 대역폭(Bandwidth)&lt;/b&gt;에 대해 이야기해보려 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버 로직 최적화에는 공을 들이면서, 정작 데이터가 오가는 '길'에 대해서는 무관심하지 않으셨나요? 대역폭을 이해하면 성능 개선은 물론 인프라 비용까지 획기적으로 줄일 수 있습니다.  &lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. 대역폭(Bandwidth)이란 무엇인가?&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 대역폭은 &lt;b&gt;단위 시간 내에 전송할 수 있는 데이터의 최대 용량&lt;/b&gt;을 의미합니다. 흔히 속도와 혼동하지만, 정확히는 '통로의 넓이'에 가깝습니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;핵심 개념 비교&lt;/h3&gt;
&lt;table data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th align=&quot;left&quot;&gt;용어&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;개념&lt;/th&gt;
&lt;th align=&quot;left&quot;&gt;비유&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;대역폭 (Bandwidth)&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;이론상 최대 전송 가능량&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;고속도로의 차선 수 (8차선 vs 2차선)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;처리량 (Throughput)&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;실제로 전송된 데이터 양&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;1시간 동안 실제 통과한 차량 수&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td align=&quot;left&quot;&gt;&lt;b&gt;지연 시간 (Latency)&lt;/b&gt;&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;데이터가 도달하는 데 걸리는 시간&lt;/td&gt;
&lt;td align=&quot;left&quot;&gt;출발지에서 목적지까지 도달하는 시간&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 서버 개발자가 대역폭에 민감해야 하는 이유 (Why?)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;① 클라우드 비용의 주범: Data Transfer Out&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대부분의 클라우드 서비스(AWS, Azure 등)는 서버로 들어오는 트래픽은 무료지만, &lt;b&gt;서버에서 외부로 나가는 트래픽(Egress)&lt;/b&gt;에 대해서는 과금을 합니다. 대역폭 최적화가 곧 기술적인 비용 절감(Cost Optimization)인 셈이죠.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;② 시스템의 병목 현상 (Bottleneck)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아무리 DB 인덱싱을 잘하고 서버가 0.1초 만에 응답을 생성해도, 결과 데이터가 너무 커서 대역폭 제한에 걸리면 사용자는 결국 '느린 서비스'라고 느끼게 됩니다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 대역폭 최적화를 위한 실전 전략 (How?)&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 1. 응답 데이터 압축 (Gzip / Brotli)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 기본적이면서 효과적인 방법입니다. JSON이나 HTML 같은 텍스트 데이터는 압축 효율이 매우 좋습니다.&lt;/p&gt;
&lt;pre id=&quot;code_1768797836725&quot; class=&quot;javascript&quot; data-ke-language=&quot;javascript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// Node.js Express 기준 압축 적용 예시
const compression = require('compression');
const express = require('express');
const app = express();

// 모든 응답에 Gzip 압축 적용
app.use(compression());

app.get('/api/users', (req, res) =&amp;gt; {
// 거대한 JSON 데이터도 압축되어 전송됨
res.json({ users: Array(1000).fill({ id: 1, name: 'Gemini' }) });
});&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 2. 효율적인 데이터 포맷 선택&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;JSON은 사람이 읽기 좋고 디버깅이 편하지만, 데이터 크기 면에서는 불리할 수 있습니다. 특히 내부 서버 간 통신(MSA)이나 트래픽이 막대한 구간에서는 &lt;b&gt;Protocol Buffers(protobuf)&lt;/b&gt;나 &lt;b&gt;MessagePack&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;JSON&lt;/b&gt;: &lt;code&gt;{&quot;id&quot;: 123, &quot;name&quot;: &quot;John&quot;}&lt;/code&gt; -&amp;gt; 텍스트 기반이라 필드명 중복이 발생하고 용량이 큼.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Protobuf&lt;/b&gt;: 이진(Binary) 데이터로 직렬화되어 데이터 크기가 JSON 대비 최대 1/10까지 줄어듭니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 3. CDN 및 캐싱 활용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정적 파일(이미지, JS, CSS)은 API 서버의 대역폭을 소모하지 않도록 &lt;b&gt;CDN(Content Delivery Network)&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;Origin 서버 보호&lt;/b&gt;: 사용자의 요청이 서버로 직접 오지 않고 전 세계 곳곳의 Edge Server에서 처리됩니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;캐시 헤더 활용&lt;/b&gt;: &lt;code&gt;Cache-Control&lt;/code&gt;을 적절히 설정하여 동일한 리소스를 중복해서 전송하지 않도록 합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;✅ 4. 지연 로딩(Lazy Loading) 및 페이지네이션&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;&lt;b&gt;Pagination&lt;/b&gt;: 10,000개의 목록을 한 번에 주지 않고 20개씩 끊어서 응답합니다.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Partial Response&lt;/b&gt;: 클라이언트가 필요한 필드만 선택해서 가져갈 수 있도록 설계합니다. (예: GraphQL)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 요약 및 인사이트  &lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;네트워크 대역폭은 무한하지 않은 &lt;b&gt;비싼 자원&lt;/b&gt;입니다. 서버 개발자는 단순히 기능 구현을 넘어, 내가 설계한 데이터 구조가 네트워크 환경에 어떤 부하를 주는지 항상 고민해야 합니다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;한 줄 요약&lt;/h3&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&quot;데이터를 보내기 전에 압축하고, 적절한 포맷을 선택하며, 정적 자원은 CDN에 맡겨 대역폭을 효율적으로 사수하자!&quot;  &lt;/p&gt;
&lt;/blockquote&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;[Backend] API 응답 속도를 높이는 5가지 최적화 기법&lt;/li&gt;
&lt;li&gt;[Infra] 클라우드 Egress 비용을 30% 절감하는 아키텍처&lt;/li&gt;
&lt;li&gt;[Protocol] HTTP/3와 QUIC: 웹 전송의 미래&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;  출처 및 참고 자료&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;MDN Web Docs&lt;/b&gt;: &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Glossary/Bandwidth&quot;&gt;Bandwidth - 네트워크 용어 사전&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;AWS Architecture Blog&lt;/b&gt;: &lt;a href=&quot;https://aws.amazon.com/ko/blogs/aws-cost-management/top-10-tips-for-optimizing-costs-on-aws/&quot;&gt;Top 10 tips for optimizing network costs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;High Performance Browser Networking&lt;/b&gt; (Ilya Grigorik 저): 네트워크 성능 최적화의 필독서&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Google Developers&lt;/b&gt;: &lt;a href=&quot;https://developers.google.com/protocol-buffers&quot;&gt;Protocol Buffers Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Cloudflare Learning&lt;/b&gt;: &lt;a href=&quot;https://www.cloudflare.com/learning/network-layer/what-is-bandwidth/&quot;&gt;What is Bandwidth? | Bandwidth vs Speed&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Computer Science/Network</category>
      <author>이진2</author>
      <guid isPermaLink="true">https://2jinishappy.tistory.com/355</guid>
      <comments>https://2jinishappy.tistory.com/355#entry355comment</comments>
      <pubDate>Mon, 19 Jan 2026 13:41:57 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] Rotate Array: 배열 회전 마스터하기 (Kotlin 풀이 및 최적화 전략)</title>
      <link>https://2jinishappy.tistory.com/354</link>
      <description>&lt;p&gt;오늘은 기술 면접 단골 문제인 LeetCode의 &lt;strong&gt;189. Rotate Array&lt;/strong&gt; 문제를 함께 살펴보겠습니다. 배열을 다루는 기본적인 감각은 물론, &lt;strong&gt;공간 복잡도 최적화&lt;/strong&gt; 능력을 어필하기 아주 좋은 문제입니다.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;  문제 개요&lt;/h2&gt;
&lt;p&gt;주어진 정수 배열 &lt;code&gt;nums&lt;/code&gt;를 오른쪽으로 &lt;code&gt;k&lt;/code&gt;단계만큼 회전시키는 문제입니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Input&lt;/strong&gt;: &lt;code&gt;nums = [1,2,3,4,5,6,7]&lt;/code&gt;, &lt;code&gt;k = 3&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Output&lt;/strong&gt;: &lt;code&gt;[5,6,7,1,2,3,4]&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;핵심 조건&lt;/strong&gt;: &lt;code&gt;k&lt;/code&gt;는 음수가 아닐 수 있으며, 배열의 길이보다 클 수 있습니다. 또한, 가능하다면 &lt;strong&gt;O(1) 공간 복잡도&lt;/strong&gt;(In-place)로 해결하는 것이 권장됩니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;  첫 번째 접근: 임시 배열 활용하기 (Space O(n))&lt;/h2&gt;
&lt;h3&gt;코드 구현 (Kotlin)&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-kotlin&quot;&gt;class Solution {
    fun rotate(nums: IntArray, k: Int): Unit {
        // 1. 원본 배열과 같은 크기의 임시 리스트 생성
        val temp = MutableList(nums.size) { 0 }

        // 2. 각 인덱스에 들어갈 위치 계산하여 할당
        for (index in nums.indices) {
            // (index + nums.size - (k % nums.size)) % nums.size 공식을 사용해
            // 회전 후 해당 자리에 와야 할 원본 데이터를 찾아옵니다.
            temp[index] = nums[(index + nums.size - (k % nums.size)) % nums.size]
        }

        // 3. 원본 배열에 결과 복사
        for (index in temp.indices) {
            nums[index] = temp[index]
        }
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;  분석 및 특징장점: 로직이 직관적입니다. 각 칸이 어디서 데이터를 가져올지 수식으로 명확히 정의됩니다.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;시간 복잡도: $O(n)$ - 배열을 두 번 순회합니다.&lt;/li&gt;
&lt;li&gt;공간 복잡도: $O(n)$ - 원본 배열과 동일한 크기의 temp 리스트가 필요합니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;  더 나은 방법은 없을까? (공간 최적화: In-place)&lt;/h2&gt;
&lt;p&gt;실제 개발 인터뷰에서는 &lt;strong&gt;&amp;quot;추가 메모리를 사용하지 않고(O(1)) 해결할 수 있나요?&amp;quot;&lt;/strong&gt;라는 후속 질문이 들어올 확률이 매우 높습니다.&lt;br&gt;이때 사용할 수 있는 마법 같은 방법이 바로 배열 뒤집기(Reverse) 전략입니다.&lt;/p&gt;
&lt;p&gt;뒤집기 전략 3단계&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;전체 배열을 뒤집는다.&lt;/li&gt;
&lt;li&gt;앞부분 k개를 뒤집는다.&lt;/li&gt;
&lt;li&gt;나머지 뒷부분을 뒤집는다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;이 방식을 사용하면 별도의 메모리 할당 없이 배열 안에서 요소의 위치만 바꿔 문제를 해결할 수 있습니다.&lt;/p&gt;
&lt;h3&gt;최적화 코드 (Kotlin)&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-Kotlin&quot;&gt;    class Solution {
      fun rotate(nums: IntArray, k: Int): Unit {
          val n = nums.size
          // k가 n보다 클 수 있으므로 나머지 연산 필수!
          val realK = k % n 

          // 1. 전체 뒤집기: [7, 6, 5, 4, 3, 2, 1]
          reverse(nums, 0, n - 1)
          // 2. 앞부분 k개 뒤집기: [5, 6, 7, 4, 3, 2, 1]
          reverse(nums, 0, realK - 1)
          // 3. 뒷부분 뒤집기: [5, 6, 7, 1, 2, 3, 4] -&amp;gt; 완성!
          reverse(nums, realK, n - 1)
      }

      private fun reverse(nums: IntArray, start: Int, end: Int) {
          var s = start
          var e = end
          while (s &amp;lt; e) {
              val temp = nums[s]
              nums[s] = nums[e]
              nums[e] = temp
              s++
              e--
          }
      }
  }&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h2&gt; ‍  시니어 엔지니어의 인사이트&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;k % nums.size를 잊지 마세요&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;면접관은 경계 조건(Edge Case) 처리를 유심히 봅니다.&lt;br&gt;만약 배열 길이가 3인데 k가 10이라면, 실제로는 1번 회전한 것(10 % 3 = 1)과 같습니다.&lt;br&gt;이 처리가 누락되면 성능 저하나 에러가 발생할 수 있습니다.&lt;/p&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;공간 복잡도와 트레이드 오프처음 작성하신 O(n) 공간 방식은 코드가 명확하고 이해하기 쉽다는 장점이 있습니다. &lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;반면 O(1) 방식은 메모리 효율은 극대화되지만 &amp;quot;배열을 세 번 뒤집는다&amp;quot;는 논리를 미리 알고 있어야 구현이 쉽습니다.&lt;br&gt;상황에 맞는 최적의 해법을 제시하고 이유를 설명하는 능력이 중요합니다.&lt;/p&gt;
&lt;p&gt;✅ 한 줄 요약배열 회전 문제는 전체 뒤집기 후 부분 뒤집기 전략을 사용하면 추가 메모리 없이 $O(n)$ 시간 안에 해결 가능하다!&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;함께 보면 좋은 글&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;[배열(Array)과 리스트(List)의 메모리 구조 차이][자주 나오는 투 포인터(Two Pointers) 알고리즘 정리]&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;출처&lt;/h2&gt;
&lt;p&gt;LeetCode 189. Rotate Array: &lt;a href=&quot;https://leetcode.com/problems/rotate-array/&quot;&gt;https://leetcode.com/problems/rotate-array/&lt;/a&gt;&lt;/p&gt;</description>
      <category>Problem Solving/LeetCode</category>
      <author>이진2</author>
      <guid isPermaLink="true">https://2jinishappy.tistory.com/354</guid>
      <comments>https://2jinishappy.tistory.com/354#entry354comment</comments>
      <pubDate>Fri, 16 Jan 2026 23:18:30 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] 27. Remove Element</title>
      <link>https://2jinishappy.tistory.com/353</link>
      <description>&lt;pre id=&quot;code_1740892803019&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;class Solution(object):
    def removeElement(self, nums, val):
        result = []
        k = 0

        for i in nums:
            if val != i:
                k+=1
                result+=[i]
        
        nums[:] = result[:]
        return k&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/remove-element/?envType=study-plan-v2&amp;amp;envId=top-interview-150&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leetcode.com/problems/remove-element/?envType=study-plan-v2&amp;amp;envId=top-interview-150&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;주어진 배열 nums에서 val 이라는 값을 전부 제거한 뒤, val이 아닌 원소의 개수를 반환하는 문제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 배열을 만들어서 val이 아닌 값을 push한 뒤 k의 값을 카운트 하는 방식으로 풀었다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 배열을 만들어서 교체하는 방식이 아니라 실제 배열에서 교체하는 방식으로도 풀 수 있을까?&lt;/p&gt;</description>
      <category>Problem Solving/LeetCode</category>
      <author>이진2</author>
      <guid isPermaLink="true">https://2jinishappy.tistory.com/353</guid>
      <comments>https://2jinishappy.tistory.com/353#entry353comment</comments>
      <pubDate>Sun, 2 Mar 2025 14:22:31 +0900</pubDate>
    </item>
    <item>
      <title>[LeetCode] 88. Merge Sorted Array</title>
      <link>https://2jinishappy.tistory.com/352</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;오랜만에 리트코드를 풀어봤다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://leetcode.com/problems/merge-sorted-array/?envType=study-plan-v2&amp;amp;envId=top-interview-150&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://leetcode.com/problems/merge-sorted-array/?envType=study-plan-v2&amp;amp;envId=top-interview-150&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오름차순으로 정렬된 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;두 개의&lt;span&gt; 배열을 합치는 것&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;포인터 두 개를 사용했고, 마지막 인덱스 처리하기 번거로워서 각각의 배열 양 끝에 max number 값을 넣어주었다&lt;/p&gt;
&lt;pre id=&quot;code_1740294397577&quot; class=&quot;python&quot; data-ke-language=&quot;python&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;import sys

class Solution(object):
    def merge(self, nums1, m, nums2, n):
        i = 0
        j = 0
        result = []
        nums1+=[sys.maxsize]
        nums2+=[sys.maxsize]

        while i&amp;lt;m or j&amp;lt;n :
            if nums1[i]&amp;lt;nums2[j] and i&amp;lt;m:
                result+=[nums1[i]]
                i+=1
            else:
                result+=[nums2[j]]
                j+=1
        
        nums1[:]=result&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;오랜만에 푸니까 파이썬도 c도 문법이 하나도 생각 안 난다 ㅎㅎ&lt;/p&gt;</description>
      <category>Problem Solving/LeetCode</category>
      <author>이진2</author>
      <guid isPermaLink="true">https://2jinishappy.tistory.com/352</guid>
      <comments>https://2jinishappy.tistory.com/352#entry352comment</comments>
      <pubDate>Sun, 23 Feb 2025 16:08:30 +0900</pubDate>
    </item>
    <item>
      <title>kubernetes components</title>
      <link>https://2jinishappy.tistory.com/351</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1252&quot; data-origin-height=&quot;585&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rjgCw/btsAc5ygSZW/kc6Mxnc6MopB5ze1Az3341/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rjgCw/btsAc5ygSZW/kc6Mxnc6MopB5ze1Az3341/img.jpg&quot; data-alt=&quot;https://kubernetes.io/images/docs/components-of-kubernetes.svg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rjgCw/btsAc5ygSZW/kc6Mxnc6MopB5ze1Az3341/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrjgCw%2FbtsAc5ygSZW%2Fkc6Mxnc6MopB5ze1Az3341%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;1252&quot; height=&quot;585&quot; data-origin-width=&quot;1252&quot; data-origin-height=&quot;585&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://kubernetes.io/images/docs/components-of-kubernetes.svg&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;662&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1DTq9/btsAbm1qmKb/QyXbpxQKxFmmkT3kajwuxk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1DTq9/btsAbm1qmKb/QyXbpxQKxFmmkT3kajwuxk/img.jpg&quot; data-alt=&quot;https://kubernetes.io/images/docs/kubernetes-cluster-architecture.svg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1DTq9/btsAbm1qmKb/QyXbpxQKxFmmkT3kajwuxk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1DTq9%2FbtsAbm1qmKb%2FQyXbpxQKxFmmkT3kajwuxk%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;1052&quot; height=&quot;662&quot; data-origin-width=&quot;1052&quot; data-origin-height=&quot;662&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://kubernetes.io/images/docs/kubernetes-cluster-architecture.svg&lt;/figcaption&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;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Node&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클러스터 내 설치되는 가상 혹은 물리 머신을 의미.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거에는 미니언즈라는 용어로 불렀다. 용어는 현재도 혼용해서 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어플리케이션이 실행중이다가 노드가 죽을 수도 있으니, 일반적으로 클러스터에는 여러 개의 노드를 둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;br /&gt; Cluster&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노드(워커 머신)의 집합.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 클러스터는 최소 한 개 이상의 &lt;b&gt;워커 노드&lt;/b&gt;를 가진다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클러스터 외부에서 관점에서는 하나의 노드가 죽어도 다른 노드로 접근 가능하기 때문에, 부하 분산 및 고가용성을 보장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;고가용성을 보장하기 위해서는 쿠버네티스 자체적으로 다음의 일을 지속적으로 수행해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 클러스터를 관리하고, 관리중인 자원의 정보를 저장하고, 노드를 모니터링 하고, 상태를 감지해서 다른 노드로 트래픽을 옮기는 작업이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 수행하는 컴포넌트를 마스터 노드라고 하고, 그 외의 노드는 모두 워커 노드라고 칭한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마스터 노드는 쿠버네티스 자체에 내장되있는 하나의 노드이며, 워커 노드들 사이에서 실제 컨테이너 오케스트레이션을 관장한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&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;Control Plain Component&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;kube-apiserver&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스 API를 외부에 노출하는 컴포넌트.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유저, 매니징 디바이스, CLI를 포함한 외부에서 내부 클러스터로 접근하기 위해 거치는 프론트 엔드다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;etcd&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;distributed, reliable key value store&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿠버네티스가 클러스터를 관리하는 데 필요한 모든 데이터를 저장하는 곳.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여러 클러스터에서 마스터와 여러 워커 노드를 각각의 방식으로 구동중이라면, etcd는 락을 이용해서 각각의 마스터가 컨플릭트가 발생하지 않게끔 관리한다.&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;kube-scheduler&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(노드가 배정되지 않은) 새로 생성된 파드를 감지하고, 실행할 노드를 선택하는 스케줄러.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스케줄링 고려 요소에는 리소스에 대한 요구사항, affinity &amp;amp; anti-affinity, 데이터 지역성 등을 포함한다.&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;kube-controller-manager&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;h3 data-ke-size=&quot;size23&quot;&gt;Node Component&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행중인 파드를 유지하고, 쿠버네티스 런타임 환경을 제공하며 &lt;b&gt;모든 노드 상에서 동작&lt;/b&gt;한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;kubelet&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클러스터의 각 노드 내에서 실행되는 에이전트. 파드 내의 컨테이너들이 기대하는 대로 돌아가도록 관리한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;br /&gt;container runtime&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너를 실행하는 데 사용되는 기본 소프트웨어&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커를 많이 사용하지만, 정식 CRI 구현체는 아니다. containerd, CRI-O가 이에 속한다.&lt;/p&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;a href=&quot;https://seongjin.me/kubernetes-cluster-components/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://seongjin.me/kubernetes-cluster-components/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://kubernetes.io/ko/docs/concepts/overview/components/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://kubernetes.io/ko/docs/concepts/overview/components/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Etc</category>
      <author>이진2</author>
      <guid isPermaLink="true">https://2jinishappy.tistory.com/351</guid>
      <comments>https://2jinishappy.tistory.com/351#entry351comment</comments>
      <pubDate>Sun, 12 Nov 2023 16:05:34 +0900</pubDate>
    </item>
    <item>
      <title>(kotlin)gradle spring boot application dockerize</title>
      <link>https://2jinishappy.tistory.com/350</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://spring.io/guides/topicals/spring-boot-docker/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://spring.io/guides/topicals/spring-boot-docker/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1695891142429&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;Getting Started | Spring Boot Docker&quot; data-og-description=&quot;The Spring Boot Maven and Gradle plugins use buildpacks in exactly the same way that the pack CLI does in the following examples. The resulting images are identical, given the same inputs. Cloud Foundry has used containers internally for many years now, an&quot; data-og-host=&quot;spring.io&quot; data-og-source-url=&quot;https://spring.io/guides/topicals/spring-boot-docker/&quot; data-og-url=&quot;https://spring.io/guides/topicals/spring-boot-docker/&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bgawrZ/hyT2AeSTuO/KpCzyz0aUbKzFDxeGPtoLK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/enJgwE/hyT2vEDxWW/OkFV5mwbofG9U2CTpacaA1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://spring.io/guides/topicals/spring-boot-docker/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://spring.io/guides/topicals/spring-boot-docker/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bgawrZ/hyT2AeSTuO/KpCzyz0aUbKzFDxeGPtoLK/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/enJgwE/hyT2vEDxWW/OkFV5mwbofG9U2CTpacaA1/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;Getting Started | Spring Boot Docker&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;The Spring Boot Maven and Gradle plugins use buildpacks in exactly the same way that the pack CLI does in the following examples. The resulting images are identical, given the same inputs. Cloud Foundry has used containers internally for many years now, an&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;spring.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. spring boot application initialize&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://start.spring.io/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://start.spring.io/&lt;/a&gt; 에서 생성했다&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱 특성에 따라 아무거나 골라도 상관 없지만 kotlin+gradle 설정이 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Dockerfile 작성&lt;/p&gt;
&lt;pre id=&quot;code_1695891464675&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM eclipse-temurin:17-jdk
VOLUME /tmp
ARG JAR_FILE
COPY ${JAR_FILE} app.jar
ENTRYPOINT [&quot;java&quot;,&quot;-jar&quot;,&quot;/app.jar&quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단한 도커파일을 작성했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;앱이 무거워져서 이미지 빌드 시간이 길어질 경우 layered jar를 활용한 멀티 스테이지 빌드로 시간 단축이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. docker build&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1770&quot; data-origin-height=&quot;724&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bYAI4x/btswcKdJjRa/PUdqYUvwZlWDO1t2hjDqVK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bYAI4x/btswcKdJjRa/PUdqYUvwZlWDO1t2hjDqVK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bYAI4x/btswcKdJjRa/PUdqYUvwZlWDO1t2hjDqVK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbYAI4x%2FbtswcKdJjRa%2FPUdqYUvwZlWDO1t2hjDqVK%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;1770&quot; height=&quot;724&quot; data-origin-width=&quot;1770&quot; data-origin-height=&quot;724&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4. 실행&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3044&quot; data-origin-height=&quot;908&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/nahKE/btsv5ls8338/ssvKoL0MXTMX02Y8Ha6D71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/nahKE/btsv5ls8338/ssvKoL0MXTMX02Y8Ha6D71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/nahKE/btsv5ls8338/ssvKoL0MXTMX02Y8Ha6D71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FnahKE%2Fbtsv5ls8338%2FssvKoL0MXTMX02Y8Ha6D71%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;3044&quot; height=&quot;908&quot; data-origin-width=&quot;3044&quot; data-origin-height=&quot;908&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 방식이 있고, palantir라는 라이브러리를 사용해서 gradle task로 추가해 사용하는 방법이 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://plugins.gradle.org/plugin/com.palantir.docker&quot;&gt;https://plugins.gradle.org/plugin/com.palantir.docker&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1695894728591&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;Gradle - Plugin: com.palantir.docker&quot; data-og-description=&quot;Version 0.35.0 (latest) Created 03 April 2023. Gradle Docker is a simple docker orchestrator for executing docker build and push from within Gradle. plugins { id(&amp;quot;com.palantir.docker&amp;quot;) version &amp;quot;0.35.0&amp;quot; } buildscript { repositories { maven { url = uri(&amp;quot;http&quot; data-og-host=&quot;plugins.gradle.org&quot; data-og-source-url=&quot;https://plugins.gradle.org/plugin/com.palantir.docker&quot; data-og-url=&quot;https://plugins.gradle.org/plugin/com.palantir.docker&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://plugins.gradle.org/plugin/com.palantir.docker&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://plugins.gradle.org/plugin/com.palantir.docker&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;Gradle - Plugin: com.palantir.docker&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Version 0.35.0 (latest) Created 03 April 2023. Gradle Docker is a simple docker orchestrator for executing docker build and push from within Gradle. plugins { id(&quot;com.palantir.docker&quot;) version &quot;0.35.0&quot; } buildscript { repositories { maven { url = uri(&quot;http&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;plugins.gradle.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;
&lt;p data-ke-size=&quot;size16&quot;&gt;또, spring boot 프로젝트라면 2.3 이후 버전부터는 maven, gradle 빌드팩 지원이 포함되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://spring.io/blog/2020/01/27/creating-docker-images-with-spring-boot-2-3-0-m1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://spring.io/blog/2020/01/27/creating-docker-images-with-spring-boot-2-3-0-m1&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1695894710531&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;Creating Docker images with Spring Boot 2.3.0.M1&quot; data-og-description=&quot;Spring Boot 2.3.0.M1 has just been released and it brings with it some interesting new features that can help you package up your Spring Boot application into Docker images. In this blog post we&amp;rsquo;ll take a look at the typical ways developers create Docker&quot; data-og-host=&quot;spring.io&quot; data-og-source-url=&quot;https://spring.io/blog/2020/01/27/creating-docker-images-with-spring-boot-2-3-0-m1&quot; data-og-url=&quot;https://spring.io/blog/2020/01/27/creating-docker-images-with-spring-boot-2-3-0-m1&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/cvtTcO/hyT2pYKr9K/ft0wVEKZap4FyMyat7Np2k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/piYur/hyT2BSowSA/W4HzNmkaxno6iYF4CeWXuk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://spring.io/blog/2020/01/27/creating-docker-images-with-spring-boot-2-3-0-m1&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://spring.io/blog/2020/01/27/creating-docker-images-with-spring-boot-2-3-0-m1&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/cvtTcO/hyT2pYKr9K/ft0wVEKZap4FyMyat7Np2k/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600,https://scrap.kakaocdn.net/dn/piYur/hyT2BSowSA/W4HzNmkaxno6iYF4CeWXuk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&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;Creating Docker images with Spring Boot 2.3.0.M1&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Spring Boot 2.3.0.M1 has just been released and it brings with it some interesting new features that can help you package up your Spring Boot application into Docker images. In this blog post we&amp;rsquo;ll take a look at the typical ways developers create Docker&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;spring.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별다른 설정 없이 bootBuildImage gradle task를 실행하면 이미지가 빌드되고, 위와 똑같이 실행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빌드 파이프라이닝이 복잡하지 않은 초기 애플리케이션이라면 이렇게 구성해도 괜찮을 것 같다.&lt;/p&gt;</description>
      <category>Spring</category>
      <author>이진2</author>
      <guid isPermaLink="true">https://2jinishappy.tistory.com/350</guid>
      <comments>https://2jinishappy.tistory.com/350#entry350comment</comments>
      <pubDate>Thu, 28 Sep 2023 19:07:59 +0900</pubDate>
    </item>
    <item>
      <title>Engine=InnoDB는 뭘 지정해주는 걸까 - MySQL 엔진과 스토리지 엔진</title>
      <link>https://2jinishappy.tistory.com/349</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;MySQL 아키텍처&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MySQL 서버는 &lt;b&gt;MySQL 엔진&lt;/b&gt;, &lt;b&gt;스토리지 엔진&lt;/b&gt;으로 구분&lt;/li&gt;
&lt;li&gt;스토리지 엔진은 핸들로 API를 만족하면 누구든 구현해서 서버에 추가 가능&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;MySQL 엔진 아키텍처&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;533&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kD3vp/btsm1UXl8Cs/JA1MLXnckSmt8XmpmpsJl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kD3vp/btsm1UXl8Cs/JA1MLXnckSmt8XmpmpsJl1/img.png&quot; data-alt=&quot;출처:&amp;amp;amp;nbsp;https://dev.mysql.com/doc/refman/8.0/en/images/mysql-architecture.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kD3vp/btsm1UXl8Cs/JA1MLXnckSmt8XmpmpsJl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkD3vp%2Fbtsm1UXl8Cs%2FJA1MLXnckSmt8XmpmpsJl1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;500&quot; height=&quot;533&quot; data-origin-width=&quot;500&quot; data-origin-height=&quot;533&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처:&amp;amp;nbsp;https://dev.mysql.com/doc/refman/8.0/en/images/mysql-architecture.png&lt;/figcaption&gt;
&lt;/figure&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;MySQL 엔진&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL 엔진은 아래의 역할들을 수행하며 요청된 SQL 문장을 분석, 최적화하는 핵심 처리를 함&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;SQL 파서&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;h4 data-ke-size=&quot;size20&quot;&gt;스토리지 엔진&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 데이터를 디스크 스토리지에 저장하거나 데이터를 읽어오는 부분을 전담&lt;/li&gt;
&lt;li&gt;스토리지 엔진은 여러 개를 동시에 사용할 수 있다&lt;/li&gt;
&lt;li&gt;각 스토리지 엔진은 성능 향상을 위해 키 캐시나 InnoDB 버퍼 풀과 같은 기능을 내장&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;mysql&amp;gt; CREATE TABLE test_table (fb1 INT, fb2 INT) ENGINE=INNODB;
&lt;/code&gt;&lt;/pre&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&amp;nbsp;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;핸들러 API&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;핸들러 API: MySQL 엔진의 쿼리 실행기에서 데이터를 쓰거나 읽을 때 스토리지 엔진에 요청하는 API&lt;/li&gt;
&lt;li&gt;InnoDB 스토리지 엔진 또한 이 핸들러 API를 이용해 MySQL 엔진과 데이터를 주고받음&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;MySQL 스레딩 구조&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;598&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yLUVd/btsm1Tc4xEq/JYLeN3HEY1wNV6knGiHF10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yLUVd/btsm1Tc4xEq/JYLeN3HEY1wNV6knGiHF10/img.png&quot; data-alt=&quot;출처: Real MySQL 1권 - 그림 4.2&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yLUVd/btsm1Tc4xEq/JYLeN3HEY1wNV6knGiHF10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyLUVd%2Fbtsm1Tc4xEq%2FJYLeN3HEY1wNV6knGiHF10%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;910&quot; height=&quot;598&quot; data-origin-width=&quot;910&quot; data-origin-height=&quot;598&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: Real MySQL 1권 - 그림 4.2&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MySQL 서버는 프로세스 기반이 아니라 스레드 기반으로 작동하며, 크게 포그라운드 스레드와 백그라운드 스레드로 구분한다&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;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;최소 MySQL 서버에 접속된 클라이언트의 수만큼 존재&lt;/li&gt;
&lt;li&gt;커넥션이 종료되면 스레드는 다시 스레드 캐시로 되돌아간다. 스레드 캐시에 일정 개수 이상이 대기 중이면 캐시에 넣지 않고 종료시켜 일정 개수의 스레드만 캐시에 존재하게 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;백그라운드 스레드&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인서트 버퍼를 병합하는 스레드&lt;/li&gt;
&lt;li&gt;&lt;b&gt;로그를 디스크로 기록하는 스레드&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;InnoDB 버퍼 풀의 데이터를 디스크에 기록하는 스레드&lt;/b&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;InnoDB에서도 데이터를 읽는 작업은 주로 클라이언트 스레드에서 처리되기 때문에 읽기 스레드는 많이 설정할 필요 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자의 요청을 처리하는 도중 데이터의 쓰기 작업은 지연(버퍼링)되어 처리될 수 있지만 데이터의 읽기 작업은 절대 지연될 수 없다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메모리 할당 및 사용 구조&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;423&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/sTbDG/btsmQprynVt/5LOz4D2Ql11QdeSQo12xa1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/sTbDG/btsmQprynVt/5LOz4D2Ql11QdeSQo12xa1/img.png&quot; data-alt=&quot;출처: Real MySQL 1권 - 그림 4.3&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/sTbDG/btsmQprynVt/5LOz4D2Ql11QdeSQo12xa1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FsTbDG%2FbtsmQprynVt%2F5LOz4D2Ql11QdeSQo12xa1%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;640&quot; height=&quot;423&quot; data-origin-width=&quot;640&quot; data-origin-height=&quot;423&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: Real MySQL 1권 - 그림 4.3&lt;/figcaption&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;/li&gt;
&lt;li&gt;글로벌 메모리 영역의 모든 메모리 공간은 MySQL 서버가 시작되면서 운영체제로부터 할당.&lt;/li&gt;
&lt;/ul&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;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;필요에 따라 2개 이상의 메모리 공간을 할당받을 수 있지만 클라이언트의 스레드 수와는 무관하다.&lt;/li&gt;
&lt;li&gt;영역
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;테이블 캐시&lt;/li&gt;
&lt;li&gt;InnoDB 버퍼 풀&lt;/li&gt;
&lt;li&gt;InnoDB 어댑티브 해시 인덱스&lt;/li&gt;
&lt;li&gt;InnoDB 리두 로그 버퍼&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;로컬 메모리 영역(세션 메모리 영역)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MySQL 서버의 클라이언트 스레드가 쿼리를 처리하는 데 사용하는 메모리 영역&lt;/li&gt;
&lt;li&gt;로컬 메모리는 각 클라이언트 스레드별로 독립적으로 할당되며 절대 공유되어 사용되지 않는다는 특징이 있다.&lt;/li&gt;
&lt;li&gt;영역
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;커넥션 버퍼&lt;/li&gt;
&lt;li&gt;정렬 버퍼&lt;/li&gt;
&lt;li&gt;조인 버퍼&lt;/li&gt;
&lt;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;세션이 너무 많아지면 &quot;세션별 버퍼 크기 * 세션 수&quot;만큼의 메모리 비용이 추가로 든다.&lt;/p&gt;
&lt;hr data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;플러그인 스토리지 엔진 모델&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;전문 검색 엔진을 위한 검색어 파서도 플러그인 형태로 개발해서 사용 가능&lt;/li&gt;
&lt;li&gt;MySQL에서 쿼리가 실행되는 과정 중 대부분의 작업은 MySQL엔진에서 처리되고, 마지막 데이터 읽기/쓰기 작업만 스토리지 엔진에 의해 처리.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실질적인 GROUP BY나 ORDER BY 등 복잡한 처리는 스토리지 엔진 영역이 아닌, MySQL 엔진의 처리 영역인 쿼리 실행기에서 처리.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;하나의 쿼리 작업이 여러 하위 작업으로 나뉠 때, 각 작업이 MySQL 엔진 영역에서 처리되는지 아니면 스토리지 엔진 영역에서 처리되는지 구분할 줄 알아야 한다.&lt;/li&gt;
&lt;li&gt;MySQL 서버에서는 스토리지 엔진뿐만 아니라 다양한 기능을 플러그인 형태로 지원한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;쿼리 실행 구조&lt;/h2&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;785&quot; data-origin-height=&quot;557&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bUoQL5/btsmSuMcgha/N5qT6wpyLEgEM04fZcl2W1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bUoQL5/btsmSuMcgha/N5qT6wpyLEgEM04fZcl2W1/img.png&quot; data-alt=&quot;출처: Real MySQL 1권 - 그림 4.6&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bUoQL5/btsmSuMcgha/N5qT6wpyLEgEM04fZcl2W1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbUoQL5%2FbtsmSuMcgha%2FN5qT6wpyLEgEM04fZcl2W1%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;785&quot; height=&quot;557&quot; data-origin-width=&quot;785&quot; data-origin-height=&quot;557&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;출처: Real MySQL 1권 - 그림 4.6&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;쿼리 파서&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자 요청으로 들어온 쿼리 문장을 토큰으로 분리해 트리 형태의 구조로 만들어내는 작업&lt;/li&gt;
&lt;li&gt;쿼리 문장의 기본 문법 오류는 이 과정에서 발견되어 사용자에게 오류 메시지를 전달한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;전처리기&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파서 과정에서 만들어진 파서 트리를 기반으로 쿼리 문장에 구조적인 문제점이 있는지 확인&lt;/li&gt;
&lt;li&gt;내장 함수와 같은 개체를 매핑해 해당 객체 존재 여부와 객체의 접근 권한 등을 확인&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;옵티마이저&lt;/h4&gt;
&lt;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;h4 data-ke-size=&quot;size20&quot;&gt;실행 엔진&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만들어진 계획대로 각 핸들러에게 요청해서 받은 결과를 또 다른 핸들러 요청의 입력으로 연결하는 역할을 수행&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;핸들러(스토리지 엔진)&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;MySQL 서버 가장 밑단에서 실행 엔진의 요청에 따라 데이터를 디스크로 저장하고 디스크로부터 읽어 오는 역할을 담당.&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;내부적으로 사용자의 요청을 처리하는 스레드 개수를 제한해서, 동시 처리되는 요청이 많더라도 MySQL 서버의 CPU가 제한된 개수의 처리에만 집중할 수 있게 하는 목적&lt;/li&gt;
&lt;li&gt;제한된 수의 스레드만으로 CPU가 처리하도록 적절히 유도하면 불필요한 컨텍스트 스위치를 줄여 오버헤드를 낮출 수 있다.&lt;/li&gt;
&lt;li&gt;일반적으로 CPU 코어의 개수와 스레드 그룹의 개수를 맞추는 것이 CPU 프로세서 친화도를 높이는데 좋다.&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;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기존에는 파일 기반의 메타데이터는 생성 및 변경 작업이 트랜잭션을 지원하지 않기 때문에 테이블의 생성 또는 변경 도중에 MySQL 서버가 비정성적으로 종료되면 일관되지 않은 상태로 남는 문제가 발생&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;8.0부터는 테이블의 구조 정보나 스토어드 프로그램의 코드 관련 정보를 모두 InnoDB의 테이블에 저장하도록 개선&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Computer Science/Database</category>
      <author>이진2</author>
      <guid isPermaLink="true">https://2jinishappy.tistory.com/349</guid>
      <comments>https://2jinishappy.tistory.com/349#entry349comment</comments>
      <pubDate>Sun, 9 Jul 2023 23:05:28 +0900</pubDate>
    </item>
  </channel>
</rss>