<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>VIRGIL ABLOH</title>
    <link>https://virgil-abloh.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Tue, 23 Jun 2026 08:24:21 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>VIRGIL ABLOH</managingEditor>
    <item>
      <title>PostgreSQL Shared Buffer 영역(작성중)</title>
      <link>https://virgil-abloh.tistory.com/154</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;일단 shared buffer 영역이 존재하는 이유는 매번 쿼리를 날릴때마다 DB는 디스크에 접근해서 데이터를 가져오기엔 비용과 부하가 발생한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸 해결하려면 메모리에 데이터를 미리 캐싱해서 올려두면 매번 디스크 IO를 발생시키지않고 응답속도와 비용절감을 가능케할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;일단 책을 읽어보면서 buffer descrioptor는 무슨 역할을 하는지 궁금하게 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순수하게 텍스트로 보면 그냥 버퍼 설명하는 주체?라고 생각하게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;책에서는 Lock이라는 설명이 들어있었다 그래서 내가 아는 비관적/낙관적 락을 관리하는 주체인가? 해서 제미나이한테 물어보니 내가 아는 락이 아니라 shared pin이라고 shared buffer영역에서 사용되는 개념이였다. 말그대로 쿼리로 인해서 해당 테이블 데이터를 사용중이라는 lock 즉 pin을 건다는 개념인것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 예시를 들어서 개념을 확고하게 해보았다.&lt;/p&gt;
&lt;pre id=&quot;code_1775271051228&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;PostgreSQL은 보통 Clock Sweep이라는 알고리즘을 사용합니다. 시계 바늘이 돌면서 &quot;너 요즘 자주 쓰이니?&quot;라고 물어보고 다니는 방식이죠.

상황 가정
1번 버퍼 (1번 테이블): Usage Count = 11, Pin = 0 (아무도 안 쓰는 중)
2번 버퍼 (2번 테이블): Usage Count = 7, Pin = 1 (현재 질문자님이 읽는 중)
새로운 3번 테이블: 메모리로 들어오고 싶어 함 (자리 없음)
진행 과정
희생양 찾기: 시계 바늘이 돌면서 어떤 버퍼를 비울지 검사합니다.
2번 버퍼 검사: Usage Count가 7로 낮지만, Pin이 1인 것을 확인합니다.
결과: &quot;오, 이건 누군가 지금 쓰고 있네? 건드리면 큰일 나겠다!&quot; 하고 무조건 건너뜁니다. (Pin의 위력)
1번 버퍼 검사: Usage Count가 11로 높습니다. 시계 바늘은 이 카운트를 하나 깎습니다 ($11 -&amp;gt; 10$).
만약 모든 버퍼가 다 바쁘다면 바늘이 여러 바퀴 돌겠죠? 그러다 Usage Count가 0이 되고 Pin이 0인 녀석을 발견하면 그게 바로 희생양이 됩니다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;해시함수&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간단히 이야기하면 어느 buffer descriptor에 있는지 태그해놓은 정보를 모아놓은 공간이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게하면 모든 buffer descriptor에 있는 정보들을 확인하지 않아도 찾으려는 테이블의 페이지를 빨리 찾을 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1776570371116&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;Hash(key) -&amp;gt; Mod(key, length) -&amp;gt; HashValue(result)&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;key는 가변길이의 입력값, length는 해시 테이블의 길이라고 하자.&lt;/p&gt;
&lt;pre id=&quot;code_1776571007022&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;1. 유저가 abc라는 값을 저장하고 싶다. 그리고 해시 테이블의 길이는 100

2. 내부에서는 컴퓨터가 알아들을수 있도록 abc값을 hash함수를 이용해서 정수값으로 변환

3. 반환받은 정수값을 이용해서 Mod(-785388649, 100)로 계산하면 나머지값을 반환, 결과값: -49

4. 결과값이 음수일 경우라면 내부적으로 양수로 변환하여 49, 결국 해당 위치에 실제 데이터가 있다고 길을 알려줌&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;해시 테이블&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시 테이블의 내부 구조에 대해서 백화점의 보관함을 예시로 들어보자&lt;/p&gt;
&lt;pre id=&quot;code_1776572040253&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;#   도서관 수장고 비유로 보는 PostgreSQL 데이터 검색

### 1. 입력 키 (Key): &quot;조선왕조실록 태조편&quot;
* **상황:** 내가 찾고 싶은 책 제목입니다.
* **동작:** 이 제목을 '해시 함수'라는 자동 분류기에 넣으면 **256**이라는 숫자가 튀어나옵니다.

---

### 2. 해시 테이블 (Bucket [256])
* **상황:** 수장고에 있는 수만 개의 보관함 중 **256번 보관함**을 가리킵니다.
* **동작:** &quot;태조편은 무조건 256번 함에 있다&quot;라는 규칙에 따라 해당 위치로 바로 이동합니다.

---

### 3. 버퍼 파티션 (Buffer Partition)
* **상황:** 256번 보관함이 있는 **'제1구역'의 관리자**입니다.
* **동작:** 관리자에게 &quot;256번 함을 열겠습니다&quot;라고 허락을 구합니다(Lock). 
    * *덕분에 다른 구역(파티션)에서 책을 찾는 사람들과 서로 동선이 겹치지 않습니다.*

---

### 4. 디렉토리 및 해시 세그먼트
* **상황:** '제1구역' 안에 나열된 **보관함들의 복도**입니다.
* **동작:** 관리자의 허락 하에 복도를 따라 256번 보관함 앞에 도착합니다.

---

### 5. 해시 엘리먼트 (Buffer Tag)
* **상황:** 256번 보관함을 열었을 때 들어있는 **실제 책들과 이름표**입니다.
* **동작:** 보관함 안에 여러 권의 책이 있을 수 있으므로(해시 충돌), 책에 붙은 태그를 하나씩 확인합니다.
    * 1번 책 태그: '고려사' (Pass)
    * 2번 책 태그: **'조선왕조실록 태조편' (Match!)**
* **최종 결과:** 태그에 적힌 **Buffer ID**를 보고 메모리상의 실제 데이터 위치를 찾아냅니다.

---&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시에서 3번 동작에서 Lock을 건다라고 하는데 해당 락은 Light Weight Lock으로 내부 병목현상을 발생하지 않게하기 위해 경량화 락을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 버퍼 파티션마다 LW Lock을 부여하여 동시에 접근요청이 오더라도 분산하여 관리하기 때문에 오랜 대기 및 부하를 줄이도록 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;근데 지금까지보면 데이터를 금방 찾아오는 방법에 대해서 내부구현을 설명했다.&lt;/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;인덱스는 영구적인 데이터 위치 지도이고, 해시 테이블은 postgresql의 메모리에 있는 임시적인 데이터 위치 지도인것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 만약 메모리에 없으면 직접 디스크 IO를 발생시켜서 데이터를 가져오기때문에 비용문제와 함께 메모리에서 가져오는 방식보다는 시간이 걸릴것이다. 그래서 이런 구멍을 메우기 위해 인덱스 방식이 존재하는 것이다.&lt;/p&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;b&gt;chaining&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;해시 엘리먼트&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시 엘리먼트는 버킷에 들어있는 실제 값을 의미하며 해시 엔트리라고도 불린다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내부에는 hash value, 버퍼 태그, 버퍼 ID 구성되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hash value는 해시함수를 이용하여 생성된 값을 의미한다. 내부에서 고유한 값&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버퍼 ID는 버퍼 풀이라는 거대한 보관함 전체를 의미한다고 하면 버퍼는 그 중의 칸 하나하나를 의미하고 버퍼 ID는 보관함 칸 한개의 ID를 생각하면 될것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버퍼 태그는 메모리에 올라와있는 데이터를 식별할수 있는 메타데이터라고 보면 될것같다. 즉, 실제 데이터가 어느페이지에 있는지 알 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1777610198253&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT
    b.bufferid AS &quot;버퍼 ID&quot;,
    b.reltablespace AS &quot;SpcNode(OID)&quot;,
    t.spcname AS &quot;테이블스페이스명&quot;,
    b.reldatabase AS &quot;DbNode(OID)&quot;,
    d.datname AS &quot;데이터베이스명&quot;,
    b.relfilenode AS &quot;RelNode(OID)&quot;,
    c.relname AS &quot;테이블/인덱스명&quot;,
    b.relforknumber AS &quot;포크 번호&quot;,
    b.relblocknumber AS &quot;블록 번호&quot;
FROM
    pg_buffercache b
        LEFT JOIN pg_database d ON b.reldatabase = d.oid
        LEFT JOIN pg_class c ON b.relfilenode = c.relfilenode
        LEFT JOIN pg_tablespace t ON b.reltablespace = t.oid
WHERE
    b.reldatabase = (SELECT oid FROM pg_database WHERE datname = current_database())
  AND c.relname IS NOT NULL and c.relname = '테이블명'
ORDER BY
    b.bufferid&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 쿼리를 이용해서 해당 테이블 데이터가 메모리 어디에 할당받고 있는지 알수 있다.&lt;/p&gt;
&lt;table style=&quot;background-color: #000000; color: #1f1f1f; border-collapse: collapse; width: 100%; height: 424px;&quot; border=&quot;1&quot; data-path-to-node=&quot;5&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;background-color: #000000; color: #1f1f1f; height: 38px;&quot;&gt;
&lt;td style=&quot;background-color: #efefef; color: #1f1f1f; height: 38px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;값&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #efefef; color: #1f1f1f; height: 38px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;항목명 (이전 그림 기준)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #efefef; color: #1f1f1f; height: 38px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;의미와 해석 (비유)&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody style=&quot;background-color: #000000; color: #1f1f1f;&quot;&gt;
&lt;tr style=&quot;background-color: #000000; color: #1f1f1f; height: 55px;&quot;&gt;
&lt;td style=&quot;background-color: #000000; color: #1f1f1f; height: 55px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,0,0&quot;&gt;1286&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #000000; color: #1f1f1f; height: 55px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;버퍼 ID (Buffer Id)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #000000; color: #1f1f1f; height: 55px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,1,2,0&quot;&gt;&quot;현재 이 데이터는 메모리(공유 버퍼)의 1286번 칸에 들어있습니다.&quot;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;가장 핵심이 되는 보관함 번호입니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #000000; color: #1f1f1f; height: 73px;&quot;&gt;
&lt;td style=&quot;background-color: #000000; color: #1f1f1f; height: 73px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,2,0,0&quot;&gt;1663&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,2,0,2&quot;&gt;pg_default&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #000000; color: #1f1f1f; height: 73px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테이블스페이스 OID&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테이블스페이스명&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #000000; color: #1f1f1f; height: 73px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,2,2,0&quot;&gt;&quot;이 데이터의 하드디스크 상 저장 구역은 기본 구역(pg_default)입니다.&quot;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;PostgreSQL이 설치될 때 기본적으로 만들어지는 디스크 저장 위치를 뜻합니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #000000; color: #1f1f1f; height: 55px;&quot;&gt;
&lt;td style=&quot;background-color: #000000; color: #1f1f1f; height: 55px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,3,0,0&quot;&gt;5&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,3,0,2&quot;&gt;postgres&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #000000; color: #1f1f1f; height: 55px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;데이터베이스 OID&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;데이터베이스명&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #000000; color: #1f1f1f; height: 55px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,3,2,0&quot;&gt;&quot;이 데이터는 postgres라는 데이터베이스 소속입니다.&quot;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;건물로 치면 'postgres'라는 회사의 사무실(5번 방) 물건입니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #000000; color: #1f1f1f; height: 55px;&quot;&gt;
&lt;td style=&quot;background-color: #000000; color: #1f1f1f; height: 55px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,4,0,0&quot;&gt;16512&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,4,0,2&quot;&gt;users&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #000000; color: #1f1f1f; height: 55px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;릴레이션 OID&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;테이블/인덱스명&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #000000; color: #1f1f1f; height: 55px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,4,2,0&quot;&gt;&quot;이 데이터는 users라는 테이블의 데이터입니다.&quot;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;사무실 안의 16512번 서랍장(users 테이블)에서 꺼내온 물건입니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #000000; color: #1f1f1f; height: 74px;&quot;&gt;
&lt;td style=&quot;background-color: #000000; color: #1f1f1f; height: 74px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,5,0,0&quot;&gt;0&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #000000; color: #1f1f1f; height: 74px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;포크 번호 (ForkNum)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #000000; color: #1f1f1f; height: 74px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,5,2,0&quot;&gt;&quot;이것은 테이블의 '진짜 본문 데이터(Main)'입니다.&quot;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;PostgreSQL에서 0번은 실제 데이터 본문을 의미합니다. (참고로 1번은 빈 공간 정보, 2번은 데이터 표시 여부 등 관리용 정보입니다.)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;background-color: #000000; color: #1f1f1f; height: 74px;&quot;&gt;
&lt;td style=&quot;background-color: #000000; color: #1f1f1f; height: 74px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,6,0,0&quot;&gt;0&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #000000; color: #1f1f1f; height: 74px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;블록 번호 (BlockNum)&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;background-color: #000000; color: #1f1f1f; height: 74px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;5,6,2,0&quot;&gt;&quot;이것은 users 테이블 파일의 가장 '첫 번째 조각(블록)'입니다.&quot;&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;데이터베이스는 데이터를 8KB 크기의 블록으로 쪼개서 저장하는데, 그중 0번째(맨 처음) 페이지라는 뜻입니다.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DB</category>
      <author>VIRGIL ABLOH</author>
      <guid isPermaLink="true">https://virgil-abloh.tistory.com/154</guid>
      <comments>https://virgil-abloh.tistory.com/154#entry154comment</comments>
      <pubDate>Sun, 19 Apr 2026 13:43:08 +0900</pubDate>
    </item>
    <item>
      <title>PostgreSQL FORK 영역</title>
      <link>https://virgil-abloh.tistory.com/153</link>
      <description>&lt;div style=&quot;background-color: #fbfbfb; color: #403f53;&quot;&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;create table tab_OID(id integer); --테이블생성
-- 위에서 생성한 테이블의 oid 조회
select OID, relname from pg_class where relname ='tab_oid'; --16426,tab_oid
select 'tab_oid'::regclass::oid; --16426&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fbfbfb; color: #403f53; text-align: start;&quot;&gt;생성한 테이블의 OID 조회 가능&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fbfbfb; color: #403f53; text-align: start;&quot;&gt;main fork에 대해서&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fbfbfb; color: #403f53; text-align: start;&quot;&gt;데이터를 저장하는 파일의 용량이 1GB를 넘기게 되면 DB는 해당 파일을 세그먼트로 나눠서 관리하게 된다 이때 relfilenode이라고 명칭한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fbfbfb; color: #403f53; text-align: start;&quot;&gt;예들을어 oid=111인 객체 즉 테이블(&lt;span style=&quot;background-color: #fbfbfb; color: #403f53; text-align: start;&quot;&gt;relfilenode&lt;/span&gt;&lt;span style=&quot;background-color: #fbfbfb; color: #403f53; text-align: start;&quot;&gt;=&lt;/span&gt;&lt;span style=&quot;background-color: #fbfbfb; color: #403f53; text-align: start;&quot;&gt;16000으로 테이블 생성시 선언되는 값&lt;/span&gt;)이 1GB를 넘기게 되면 새로운 파일의 relfilenode&lt;span style=&quot;background-color: #fbfbfb; color: #403f53; text-align: start;&quot;&gt;은 16000.1, &lt;span style=&quot;background-color: #fbfbfb; color: #403f53; text-align: start;&quot;&gt;16000.2, &lt;span style=&quot;background-color: #fbfbfb; color: #403f53; text-align: start;&quot;&gt;16000.3 ...&lt;/span&gt;&lt;/span&gt; 이라고 세그먼트 파일이 생성된다. 만약 VACUUM FULL이 동작해서 delete된 파일들을 정리해서 새로운 relfilenode&lt;span style=&quot;background-color: #fbfbfb; color: #403f53; text-align: start;&quot;&gt;이 생기면 17000로 생성하여 해당 파일에 남은 데이터들을 이동한다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #fbfbfb; color: #403f53; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #fbfbfb; color: #403f53; text-align: start;&quot;&gt;&lt;span style=&quot;background-color: #fbfbfb; color: #403f53; text-align: start;&quot;&gt;위의 내용을 정리한 표이다.&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 114px;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot; data-ke-style=&quot;style13&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 14.3023%; height: 19px;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 85.6977%; height: 19px;&quot;&gt;&lt;b&gt;상세 내용&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 14.3023%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;기본&lt;/span&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt; 파일명&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 85.6977%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;테이블 생성 시 pg_class의 relfilenode 값을 이름으로 가짐 (예: 16000)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 14.3023%; height: 19px;&quot;&gt;&lt;span style=&quot;text-align: start;&quot;&gt;세그먼트 분할&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 85.6977%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;파일이 1GB에 도달하면 16000.1, 16000.2 식으로 확장됨&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 14.3023%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;일반 VACUUM&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 85.6977%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;기존 파일(16000) 내부의 쓰레기만 치움. 파일명 유지.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 14.3023%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;VACUUM FULL&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 85.6977%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #000000;&quot; data-path-to-node=&quot;12,4,1,0&quot;&gt;새 파일(17000)을 만들어 유효 데이터만 이사 시킴. 파일명&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot; data-path-to-node=&quot;12,4,1,0&quot;&gt;&amp;nbsp;변경.&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 14.3023%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;연결 고리&lt;/span&gt;&lt;/td&gt;
&lt;td style=&quot;width: 85.6977%; height: 19px;&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;파일명이 바뀌면 pg_class 장부의 relfilenode 컬럼 값도 새 번호로 갱신됨&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;free space map(fsm)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 변경되면 빈공간이 생기게 된다. 그이후에 추가되는 값은 어느 relfilenode에 저장할지 판단해야된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 fsm을 확인해서 해당 테이블의 relfilenode의 어느 페이지(page)로 갈지 판단한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;relfilenode를 조회했을때 16000이면 fsm 파일명은 16000_fsm으로 생성된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 새로 생성한 테이블이면 oid값과 relfilenode값은 서로 같을수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 내용을 정리한 표이다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%; height: 129px;&quot; border=&quot;1&quot; data-ke-style=&quot;style13&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 14.3023%; height: 19px;&quot;&gt;&lt;b&gt;구분&lt;/b&gt;&lt;/td&gt;
&lt;td style=&quot;width: 85.6977%; height: 19px;&quot;&gt;&lt;b&gt;상세 내용&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;width: 14.3023%; height: 36px;&quot;&gt;데이터 변경(Update/Delete)&lt;/td&gt;
&lt;td style=&quot;width: 85.6977%; height: 36px;&quot;&gt;기존 자리에 구멍(빈 공간)이 생김&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 14.3023%; height: 19px;&quot;&gt;FSM 업데이트&lt;/td&gt;
&lt;td style=&quot;width: 85.6977%; height: 19px;&quot;&gt;VACUUM이나 일반적인 작업 중에 &quot;이 페이지에 자리가 났다&quot;고 _fsm 파일에 기록함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 36px;&quot;&gt;
&lt;td style=&quot;width: 14.3023%; height: 36px;&quot;&gt;새 데이터 입력(Insert)&lt;/td&gt;
&lt;td style=&quot;width: 85.6977%; height: 36px;&quot;&gt;_fsm 지도를 보고 빈 구석을 찾아 들어가서 공간을 알뜰하게 재활용함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr style=&quot;height: 19px;&quot;&gt;
&lt;td style=&quot;width: 14.3023%; height: 19px;&quot;&gt;연결&lt;/td&gt;
&lt;td style=&quot;width: 85.6977%; height: 19px;&quot;&gt;relfilenode(실제 파일) + _fsm(빈자리 지도) + _vm(청소 지도)은 항상 세트로 움직임&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fsm 확인방법&lt;/p&gt;
&lt;div style=&quot;background-color: #fbfbfb; color: #403f53;&quot;&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;CREATE EXTENSION pg_freespacemap;
create table t1
(
    id   integer,
    name char(2000)
);
alter table t1 set(autovacuum_enabled=false);

insert into t1
select i as id, 'aaa' as name
from generate_series(1, 20) a(i);

select * from pg_freespace('t1');&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;696&quot; data-origin-height=&quot;423&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/y05FS/dJMcaadAdIu/CvfjGFQke8WmGt7AYxaUak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/y05FS/dJMcaadAdIu/CvfjGFQke8WmGt7AYxaUak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/y05FS/dJMcaadAdIu/CvfjGFQke8WmGt7AYxaUak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fy05FS%2FdJMcaadAdIu%2FCvfjGFQke8WmGt7AYxaUak%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;317&quot; height=&quot;193&quot; data-origin-width=&quot;696&quot; data-origin-height=&quot;423&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;div style=&quot;background-color: #fbfbfb; color: #403f53;&quot;&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;update t1 set name='bbb';
select * from pg_freespace('t1');&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 업데이트를 하면 데드 튜플이 증가되어 페이지 갯수는 2배로 증가함을 확인 가능&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;713&quot; data-origin-height=&quot;710&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b24Nc0/dJMcafzccMh/ClGJK5OXBjB5LNuHKzxq3k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b24Nc0/dJMcafzccMh/ClGJK5OXBjB5LNuHKzxq3k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b24Nc0/dJMcafzccMh/ClGJK5OXBjB5LNuHKzxq3k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb24Nc0%2FdJMcafzccMh%2FClGJK5OXBjB5LNuHKzxq3k%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;261&quot; height=&quot;260&quot; data-origin-width=&quot;713&quot; data-origin-height=&quot;710&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vaccum을 동작시켜서 dead tuple을 정리&lt;/p&gt;
&lt;div style=&quot;background-color: #fbfbfb; color: #403f53;&quot;&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;vacuum t1;

select *, round(100 * avail / 8192, 2) as freespace_ratio
from pg_freespace('t1');&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;683&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/byoVDn/dJMcaduC0tH/GNPuLPI4PV21X9wsgsHBXk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/byoVDn/dJMcaduC0tH/GNPuLPI4PV21X9wsgsHBXk/img.png&quot; data-alt=&quot;업데이트된 tuple은 유지되고 dead 처리된 tuple들은 비활성화되어서 freespace가 초기화됨을 확인&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/byoVDn/dJMcaduC0tH/GNPuLPI4PV21X9wsgsHBXk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbyoVDn%2FdJMcaduC0tH%2FGNPuLPI4PV21X9wsgsHBXk%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;330&quot; height=&quot;209&quot; data-origin-width=&quot;1080&quot; data-origin-height=&quot;683&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;업데이트된 tuple은 유지되고 dead 처리된 tuple들은 비활성화되어서 freespace가 초기화됨을 확인&lt;/figcaption&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;b&gt;VM(Visibility Map)&lt;/b&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&gt;VM(&lt;b&gt;Visibility Map&lt;/b&gt;)은 테이블의 페이지 단위로&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;&lt;b&gt;해당 페이지의 모든 tuple이 visible한지(all-visible)&lt;/b&gt;&lt;span&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;를 관리한다.&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;Vacuum은 VM을 참고하여 이미 all-visible인 페이지는 스캔을 건너뛰어 성능을 높인다.&lt;/li&gt;
&lt;li&gt;즉 all-visible하지 않은 페이지라면 vaccum에게 해당 페이지 확인해서 vaccum처리하라고 알려주는 파일이다.&lt;/li&gt;
&lt;li&gt;또한 VM의 all-visible 정보 덕분에 Index Only Scan이 가능하며, 이 경우 heap(실제 테이블)을 직접 조회하지 않아도 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;div style=&quot;background-color: #fbfbfb; color: #403f53;&quot;&gt;
&lt;pre class=&quot;pgsql&quot; style=&quot;background-color: #f8f8f8; color: #383a42;&quot;&gt;&lt;code&gt;SHOW data_directory; --/var/lib/postgresql/data
select pg_relation_filepath('테이블명'); --base/5/16412&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 path들을 검색해서 postgresql에서 관리하는 fork 영역을 관리하는 파일들을 직접 확인 가능하다.&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;select 쿼리 동작&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;먼저 해당 테이터 index에서 id를 조회하여 어느 페이지에 저장되었는지 찾는다.&lt;/li&gt;
&lt;li&gt;해당 페이지의 vm을 확인해서 all-visible하면 index에 저장된 값을 바로 반환한다.(비용절감됨)&lt;/li&gt;
&lt;li&gt;만약 all-visible하지 않은 데이터라면 실제 데이터가 저장된 디스크에서 읽어서 값을 반환한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;b&gt;all-visible과 all-frozen&lt;/b&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;all-visible은 해당 페이지는 인덱스에 저장된 값을 바로 반환해도 된다는 의미&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vaccum에게도 skip해도된다는 의미지만 낮은 확률로 vaccum 동작할수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;all-frozen은 해당 페이지는 절대 값이 변하지 않는다는 의미&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비트값이 1 즉 true면 해당 페이지는 vaccum이 아예 동작하지 않아도 된다는 의미&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;all-visible/all-frozen&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테이블 생성되고 데이터가 입력되면 vm의 값은 0,0 조합&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vaccum이 동작되면 vm의 값은 1,0 또는 1,1 상태가 된다.(&lt;i&gt;vaccum이 동작했기때문에 all-visible=true가 된다.)&lt;/i&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;vaccum freeze 옵션으로 동작시키면 무조건 1,1이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;delete,update 쿼리가 수행하면 항상 0,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;vm 정보 확인 테스트&lt;/p&gt;
&lt;div style=&quot;background-color: #fbfbfb; color: #403f53;&quot;&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;drop table t1; -- 기존 테이블 삭제

create table t1
(
    id   integer,
    name char(2000)
); -- 테이블 생성

insert into t1
select i as id, 'aaa' as name
from generate_series(1, 10) a(i); -- 데이터 입력&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;div style=&quot;background-color: #fbfbfb; color: #403f53;&quot;&gt;
&lt;pre class=&quot;pgsql&quot;&gt;&lt;code&gt;select blkno, all_visible, all_frozen
from pg_visibility('t1');&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빈 페이지에 insert가 발생하면 all-visible,all-frozen은 0 즉 false로 반환된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;267&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/clhG0D/dJMcafF3jMH/44WI36MvFfyO3apr1qIIO1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/clhG0D/dJMcafF3jMH/44WI36MvFfyO3apr1qIIO1/img.png&quot; data-alt=&quot;빈 페이지에 데이터가 저장되면 모든 값은 false&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/clhG0D/dJMcafF3jMH/44WI36MvFfyO3apr1qIIO1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FclhG0D%2FdJMcafF3jMH%2F44WI36MvFfyO3apr1qIIO1%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;472&quot; height=&quot;107&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;267&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;빈 페이지에 데이터가 저장되면 모든 값은 false&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div style=&quot;background-color: #fbfbfb; color: #403f53;&quot;&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;vacuum t1; -- vaccum 동작

select blkno, all_visible, all_frozen
from pg_visibility('t1'); -- 조회&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1171&quot; data-origin-height=&quot;258&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/YGEEo/dJMcaiCKoq1/XrtbWU0ZyLdHebaAeoSFQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/YGEEo/dJMcaiCKoq1/XrtbWU0ZyLdHebaAeoSFQk/img.png&quot; data-alt=&quot;vaccum을 동작시키면 all-visible은 true&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/YGEEo/dJMcaiCKoq1/XrtbWU0ZyLdHebaAeoSFQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FYGEEo%2FdJMcaiCKoq1%2FXrtbWU0ZyLdHebaAeoSFQk%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;464&quot; height=&quot;102&quot; data-origin-width=&quot;1171&quot; data-origin-height=&quot;258&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;vaccum을 동작시키면 all-visible은 true&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div style=&quot;background-color: #fbfbfb; color: #403f53;&quot;&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;update t1
set name = 'ccc'
where id = 1; -- update로 인해 dead tuple 발생&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;251&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxJGwd/dJMcaivX01f/ykdLfegKquDj5hOQiLll21/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxJGwd/dJMcaivX01f/ykdLfegKquDj5hOQiLll21/img.png&quot; data-alt=&quot;dead tuple이 발생하여 all-visible이 false&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxJGwd/dJMcaivX01f/ykdLfegKquDj5hOQiLll21/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbxJGwd%2FdJMcaivX01f%2FykdLfegKquDj5hOQiLll21%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;103&quot; data-origin-width=&quot;1172&quot; data-origin-height=&quot;251&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;dead tuple이 발생하여 all-visible이 false&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잠깐! 왜 where절로 한개의 로우만 업데이트 했는데 두개의 페이지가 all-visible=false처리되었냐면 update쿼리는 변경된 값을 insert 처리하기 때문에 dead tuple이 발생한 페이지와 업데이트되어 insert된 페이지 총 2개의 페이지가 all-visible=false처리가 된다.&lt;/p&gt;
&lt;div style=&quot;background-color: #fbfbfb; color: #403f53;&quot;&gt;
&lt;pre class=&quot;sql&quot; data-ke-language=&quot;sql&quot;&gt;&lt;code&gt;vacuum freeze t1; --vaccum freeze 수행

select blkno, all_visible, all_frozen
from pg_visibility('t1'); -- 확인&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;257&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/CXQk9/dJMcacWPXOn/NFZuEMAEjKw1XWvOpUpb10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/CXQk9/dJMcacWPXOn/NFZuEMAEjKw1XWvOpUpb10/img.png&quot; data-alt=&quot;모든 페이지의 all-visible과 all-freeze가 true&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/CXQk9/dJMcacWPXOn/NFZuEMAEjKw1XWvOpUpb10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCXQk9%2FdJMcacWPXOn%2FNFZuEMAEjKw1XWvOpUpb10%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;487&quot; height=&quot;106&quot; data-origin-width=&quot;1179&quot; data-origin-height=&quot;257&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;모든 페이지의 all-visible과 all-freeze가 true&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 되면 '현재 노출할 수 있는 데이터는 최신화 데이터라서 더이상 확인할 필요가 없다' 라는 의미가 된다.&lt;/p&gt;</description>
      <category>DB</category>
      <author>VIRGIL ABLOH</author>
      <guid isPermaLink="true">https://virgil-abloh.tistory.com/153</guid>
      <comments>https://virgil-abloh.tistory.com/153#entry153comment</comments>
      <pubDate>Sun, 22 Mar 2026 13:56:22 +0900</pubDate>
    </item>
    <item>
      <title>PostgreSQL 대해</title>
      <link>https://virgil-abloh.tistory.com/152</link>
      <description>&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;PostgreSQL은 시스템 메모리가 1GB 이상일 경우 전체 메모리의 약 25%를 shared_buffers로 설정하는 것이 일반적이다.&lt;/li&gt;
&lt;li&gt;PostgreSQL은 DB 내부 버퍼(shared_buffers)와 OS의 페이지 캐시를 함께 활용하는 구조이기 때문에, shared buffer를 과도하게 크게 설정하지 않는다.&lt;/li&gt;
&lt;li&gt;데이터 조회 시 ① shared buffer &amp;rarr; ② OS page cache &amp;rarr; ③ 디스크 순으로 접근하여 최대한 디스크 I/O를 줄인다.&lt;/li&gt;
&lt;li&gt;반면 Oracle은 DB 내부 메모리를 크게 사용하는 구조이며, PostgreSQL은 OS 캐시와 협력하는 방식으로 전체 시스템 메모리 효율을 높이는 전략을 사용한다.&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;공유 메모리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;shared buffer: 일반적인 데이터베이스의 버퍼영역과 동일하며 디스크 IO를 줄여주는 역할. 128KB를 기본값&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WAL(write ahead log): 변경사항들을 바로 디스크에 반영하지않고 메모리에 기록해두는 영역이며 일정시간 혹은 조건을 만족하면 디스크에 반영을 하는데 이때 WAL 세그먼트에 반영&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Clog(comiit log): 트랜잭션별로 결과를 저장하는 영역이며 트랜잭션이 진행중이면 in-progress, 정상적인 commit이되면 &quot;commited&quot;, rollback되면 &quot;aborted&quot; 결과를 반환한다.&lt;/p&gt;
&lt;pre id=&quot;code_1771312899977&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT txid_current();
-- 751
SELECT txid_status(751);&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;락 스페이스: 데이터베이스에서 락을 관리하는 장부이며 누가(PID), 무엇을(table/row), 어떤 종류의 락(exclusive/shared)을 점유하고 있는지 알수 있음.&lt;/p&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;backend 프로세스가 관리하는 영역이며 쿼리를 실행후 결과를 전송&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세션/트랜잭션별로 단위 메모리를 설정 가능&lt;/p&gt;</description>
      <category>DB</category>
      <author>VIRGIL ABLOH</author>
      <guid isPermaLink="true">https://virgil-abloh.tistory.com/152</guid>
      <comments>https://virgil-abloh.tistory.com/152#entry152comment</comments>
      <pubDate>Tue, 17 Feb 2026 16:56:11 +0900</pubDate>
    </item>
    <item>
      <title>CNI 개념</title>
      <link>https://virgil-abloh.tistory.com/151</link>
      <description>&lt;h1 data-path-to-node=&quot;3&quot;&gt;[Kubernetes] CNI, Flannel, 그리고 Service의 차이 완벽 정리&lt;/h1&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;쿠버네티스를 공부하다 보면 네트워크 부분에서 머리가 아파옵니다. &lt;b data-index-in-node=&quot;36&quot; data-path-to-node=&quot;4&quot;&gt;CNI, Flannel, Service...&lt;/b&gt; 다 통신을 하게 해주는 것 같은데, 도대체 무슨 차이가 있을까요?&lt;/p&gt;
&lt;p data-path-to-node=&quot;5&quot; data-ke-size=&quot;size16&quot;&gt;이번 글에서는 쿠버네티스 네트워크의 핵심 개념들을 **&quot;도로&quot;와 &quot;내비게이션&quot;**에 비유하여 아주 쉽게 정리해 보겠습니다.&lt;/p&gt;
&lt;hr data-path-to-node=&quot;6&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;7&quot; data-ke-size=&quot;size26&quot;&gt;1. CNI (Container Network Interface)란?&lt;/h2&gt;
&lt;blockquote data-path-to-node=&quot;8&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-path-to-node=&quot;8,0&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;8,0&quot;&gt;한 줄 요약:&lt;/b&gt; 쿠버네티스 네트워크의 &quot;표준 규격(인터페이스)&quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;쿠버네티스는 직접 네트워크 기능을 만들지 않았습니다. 대신 **&quot;IP는 이렇게 주고, 연결은 이렇게 하라&quot;**는 규칙(Interface)만 정해두었습니다. 이 규칙이 바로 &lt;b data-index-in-node=&quot;96&quot; data-path-to-node=&quot;9&quot;&gt;CNI&lt;/b&gt;입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot;&gt;CNI의 역할:&lt;/b&gt; 파드(Pod)에게 IP 주소를 할당하고, 네트워크 인터페이스를 관리합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;플러그인:&lt;/b&gt; 이 규칙을 지켜서 만든 실제 제품들이 &lt;b data-index-in-node=&quot;27&quot; data-path-to-node=&quot;10,1,0&quot;&gt;Flannel, Calico, AWS VPC CNI&lt;/b&gt; 등입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;11&quot; data-ke-size=&quot;size23&quot;&gt;  비유: USB 포트&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;12&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,0,0&quot;&gt;CNI = USB 포트:&lt;/b&gt; 삼성 마우스를 꽂든, 로지텍 키보드를 꽂든 작동하게 해주는 표준.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;12,1,0&quot;&gt;Flannel = 특정 마우스 제품:&lt;/b&gt; 그 포트에 꽂아서 실제로 동작하는 도구.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;13&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;14&quot; data-ke-size=&quot;size26&quot;&gt;2. Flannel과 Cross-Node 통신&lt;/h2&gt;
&lt;blockquote data-path-to-node=&quot;15&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-path-to-node=&quot;15,0&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15,0&quot;&gt;핵심 질문:&lt;/b&gt; 서로 다른 컴퓨터(Node)에 있는 파드끼리는 어떻게 통신할까?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p data-path-to-node=&quot;16&quot; data-ke-size=&quot;size16&quot;&gt;쿠버네티스 클러스터는 여러 대의 컴퓨터(Node)로 이루어져 있습니다. 문제는 &lt;b data-index-in-node=&quot;44&quot; data-path-to-node=&quot;16&quot;&gt;Node A&lt;/b&gt;에 있는 파드가 &lt;b data-index-in-node=&quot;59&quot; data-path-to-node=&quot;16&quot;&gt;Node B&lt;/b&gt;에 있는 파드에게 데이터를 보낼 때 발생합니다.&lt;/p&gt;
&lt;h3 data-path-to-node=&quot;17&quot; data-ke-size=&quot;size23&quot;&gt;1) CNI(Flannel)가 없을 때&lt;/h3&gt;
&lt;p data-path-to-node=&quot;18&quot; data-ke-size=&quot;size16&quot;&gt;각 노드는 **&quot;고립된 섬&quot;**입니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;19&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내 컴퓨터(Node A) 안에 있는 파드끼리는 통신이 됩니다.&lt;/li&gt;
&lt;li&gt;하지만 **다른 컴퓨터(Node B)**로 데이터를 보내려고 하면 길을 잃습니다. 물리적인 라우터나 공유기는 파드의 가상 IP(예: 10.244.1.5)를 모르기 때문입니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-path-to-node=&quot;20&quot; data-ke-size=&quot;size23&quot;&gt;2) CNI(Flannel)가 있을 때&lt;/h3&gt;
&lt;p data-path-to-node=&quot;21&quot; data-ke-size=&quot;size16&quot;&gt;Flannel은 노드들 사이에 **&quot;가상의 터널(Overlay Network)&quot;**을 뚫습니다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;22&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;22,0,0&quot;&gt;포장(Encapsulation):&lt;/b&gt; 파드의 데이터가 노드 밖으로 나갈 때, Flannel이 겉포장을 한 번 더 쌉니다. (&quot;이거 사실 Node B로 가는 거야&quot;라고 표시)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;22,1,0&quot;&gt;전송:&lt;/b&gt; 물리 네트워크를 타고 Node B로 이동합니다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;22,2,0&quot;&gt;개봉(Decapsulation):&lt;/b&gt; Node B의 Flannel이 포장을 뜯고, 안에 있는 파드에게 데이터를 전달합니다.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-path-to-node=&quot;23&quot; data-ke-style=&quot;style1&quot;&gt;
&lt;p data-path-to-node=&quot;23,0&quot; data-ke-size=&quot;size16&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;23,0&quot;&gt;참고 (Flannel ID / VNI):&lt;/b&gt; 이때 Flannel은 패킷을 구분하기 위해 VNI(VXLAN Network Identifier)라는 식별자(보통 1번)를 붙여서 전송합니다.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr data-path-to-node=&quot;24&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;25&quot; data-ke-size=&quot;size26&quot;&gt;3. Service와 CNI의 차이점&lt;/h2&gt;
&lt;p data-path-to-node=&quot;26&quot; 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-path-to-node=&quot;27&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;b&gt;CNI (Flannel 등)&lt;/b&gt;&lt;/td&gt;
&lt;td&gt;&lt;b&gt;Service (ClusterIP 등)&lt;/b&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;27,1,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;27,1,0,0&quot;&gt;비유&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;27,1,1,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;27,1,1,0&quot;&gt;도로 &amp;amp; 터널&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;27,1,2,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;27,1,2,0&quot;&gt;내비게이션 &amp;amp; 목적지&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;27,2,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;27,2,0,0&quot;&gt;역할&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;27,2,1,0&quot;&gt;물리적으로 **&quot;갈 수 있는 길&quot;**을 만듦&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;27,2,2,0&quot;&gt;논리적으로 &lt;b data-index-in-node=&quot;6&quot; data-path-to-node=&quot;27,2,2,0&quot;&gt;&quot;누구에게 갈지&quot;&lt;/b&gt; 안내함&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;27,3,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;27,3,0,0&quot;&gt;문제 해결&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;27,3,1,0&quot;&gt;&quot;다른 컴퓨터까지 어떻게 가지?&quot;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;27,3,2,0&quot;&gt;&quot;파드 IP가 자꾸 바뀌는데 누굴 찾지?&quot;&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;27,4,0,0&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;27,4,0,0&quot;&gt;작동 계층&lt;/b&gt;&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;27,4,1,0&quot;&gt;L2/L3 (네트워크 연결)&lt;/span&gt;&lt;/td&gt;
&lt;td&gt;&lt;span data-path-to-node=&quot;27,4,2,0&quot;&gt;L4 (부하분산 및 포워딩)&lt;/span&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-path-to-node=&quot;28&quot; data-ke-size=&quot;size23&quot;&gt;  상황극으로 이해하기&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;29&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;29,0,0&quot;&gt;Service:&lt;/b&gt; &quot;고객님, 목적지는 백엔드 파드입니다. 주소는 10.244.1.5로 찍고 가세요.&quot; (안내)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;29,1,0&quot;&gt;CNI:&lt;/b&gt; &quot;네, 그 주소(10.244.1.5)로 가시려면 제가 뚫어놓은 이 터널을 통과해서 옆 동네 컴퓨터로 건너가시면 됩니다.&quot; (이동)&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;30&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;31&quot; data-ke-size=&quot;size26&quot;&gt;4. (심화) AWS EKS는 다르다?&lt;/h2&gt;
&lt;p data-path-to-node=&quot;32&quot; data-ke-size=&quot;size16&quot;&gt;일반적인 쿠버네티스는 Flannel 같은 오버레이(Overlay) 방식을 쓰지만, &lt;b data-index-in-node=&quot;46&quot; data-path-to-node=&quot;32&quot;&gt;AWS EKS&lt;/b&gt;는 기본적으로 &lt;b data-index-in-node=&quot;61&quot; data-path-to-node=&quot;32&quot;&gt;AWS VPC CNI&lt;/b&gt;를 사용합니다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;33&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;33,0,0&quot;&gt;Flannel:&lt;/b&gt; 파드에게 &lt;b data-index-in-node=&quot;14&quot; data-path-to-node=&quot;33,0,0&quot;&gt;가짜(가상) IP&lt;/b&gt;를 주고 포장해서 보냄. (조금 느림)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;33,1,0&quot;&gt;AWS VPC CNI:&lt;/b&gt; 파드에게 **진짜 AWS IP(VPC IP)**를 줌.
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;33,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;포장할 필요 없이 EC2 통신하듯이 바로 쏠 수 있음. (빠르고 성능이 좋음)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr data-path-to-node=&quot;34&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h2 data-path-to-node=&quot;35&quot; data-ke-size=&quot;size26&quot;&gt;5. 요약&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;36&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;36,0,0&quot;&gt;CNI&lt;/b&gt;는 쿠버네티스 네트워크의 &lt;b data-index-in-node=&quot;17&quot; data-path-to-node=&quot;36,0,0&quot;&gt;도로 공사&lt;/b&gt; 담당이다. 파드 간의 물리적인 연결 길을 뚫어준다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;36,1,0&quot;&gt;Flannel&lt;/b&gt;은 CNI의 구현체 중 하나로, **패킷을 포장(캡슐화)**해서 다른 노드로 배달해 주는 역할을 한다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;36,2,0&quot;&gt;Service&lt;/b&gt;는 &lt;b data-index-in-node=&quot;9&quot; data-path-to-node=&quot;36,2,0&quot;&gt;내비게이션&lt;/b&gt;이다. IP가 자주 바뀌는 파드들을 대신해 &lt;b data-index-in-node=&quot;38&quot; data-path-to-node=&quot;36,2,0&quot;&gt;고정된 주소&lt;/b&gt;를 제공하고 올바른 파드로 안내한다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;36,3,0&quot;&gt;CNI가 없으면?&lt;/b&gt; 도로가 끊긴 것과 같다. 같은 노드 내에서만 통신되고, 다른 노드로 나가는 순간 통신이 두절된다.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr data-path-to-node=&quot;34&quot; data-ke-style=&quot;style1&quot; /&gt;
&lt;h4 data-path-to-node=&quot;13&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13&quot;&gt;1. CNI (Container Network Interface)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;14&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,0,0&quot;&gt;역할:&lt;/b&gt; &lt;b data-index-in-node=&quot;4&quot; data-path-to-node=&quot;14,0,0&quot;&gt;&quot;IP 할당 및 네트워크 연결 담당&quot;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;14,1,0&quot;&gt;동작:&lt;/b&gt; 쿠버네티스(Kubelet)가 파드를 만들 때 CNI를 호출합니다. CNI는 &lt;b data-index-in-node=&quot;46&quot; data-path-to-node=&quot;14,1,0&quot;&gt;사용 가능한 IPv4 주소를 할당&lt;/b&gt;하고, 파드 내부에 **네트워크 인터페이스(eth0)**를 생성하여 통신이 가능한 상태로 만들어줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-path-to-node=&quot;15&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;15&quot;&gt;2. Service (서비스)&lt;/b&gt;&lt;/h4&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;16&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,0,0&quot;&gt;역할:&lt;/b&gt; &lt;b data-index-in-node=&quot;4&quot; data-path-to-node=&quot;16,0,0&quot;&gt;&quot;부하 분산 및 트래픽 포워딩 담당&quot;&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,1,0&quot;&gt;동작:&lt;/b&gt; 파드는 생성과 삭제가 반복되면서 IP가 계속 바뀝니다. 서비스는 이 &lt;b data-index-in-node=&quot;42&quot; data-path-to-node=&quot;16,1,0&quot;&gt;변동되는 파드 IP 목록(Endpoints)을 실시간으로 관리&lt;/b&gt;합니다. 외부나 내부에서 요청(Request)이 오면, 서비스는 관리 중인 IP 목록 중 하나로 트래픽을 안전하게 **연결(Forwarding)**해 줍니다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Container</category>
      <author>VIRGIL ABLOH</author>
      <guid isPermaLink="true">https://virgil-abloh.tistory.com/151</guid>
      <comments>https://virgil-abloh.tistory.com/151#entry151comment</comments>
      <pubDate>Sat, 17 Jan 2026 11:32:43 +0900</pubDate>
    </item>
    <item>
      <title>Kubernetes 개념정리</title>
      <link>https://virgil-abloh.tistory.com/150</link>
      <description>&lt;h1 data-path-to-node=&quot;3&quot;&gt;[Kubernetes] Kind로 실습하며 정리한 핵심 개념 요약 (노드, 컨트롤러, 서비스)&lt;/h1&gt;
&lt;p data-path-to-node=&quot;4&quot; data-ke-size=&quot;size16&quot;&gt;로컬 쿠버네티스 학습 도구인 **Kind(Kubernetes in Docker)**를 사용하여 실습하면서 알게 된, 초심자가 헷갈리기 쉬운 핵심 개념들을 정리해 보았다.&lt;/p&gt;
&lt;h2 data-path-to-node=&quot;5&quot; data-ke-size=&quot;size26&quot;&gt;1. 인프라: Kind 노드의 정체 (가면무도회)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;6&quot; data-ke-size=&quot;size16&quot;&gt;kind create cluster로 노드 3개를 띄웠을 때, 내 컴퓨터에서는 무슨 일이 일어난 걸까?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;7&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,0,0&quot;&gt;실체:&lt;/b&gt; VM(가상머신) 3대가 아니라, &lt;b data-index-in-node=&quot;22&quot; data-path-to-node=&quot;7,0,0&quot;&gt;도커 컨테이너 3개&lt;/b&gt;가 생성된 것이다. (docker ps로 확인 가능)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,1,0&quot;&gt;속임수:&lt;/b&gt; 이 컨테이너들은 내부에서 systemd 등이 돌아가며 실제 OS처럼 행동한다. 쿠버네티스 마스터는 이들을 &quot;진짜 컴퓨터(Node)&quot;라고 인식한다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;7,2,0&quot;&gt;장점:&lt;/b&gt; 진짜 VM 3대를 띄우는 것보다 리소스(RAM, CPU)를 훨씬 적게 쓰면서 멀티 노드 환경을 완벽하게 흉내 낼 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;8&quot; data-ke-size=&quot;size26&quot;&gt;2. 워크로드: 누가 누구를 관리하나?&lt;/h2&gt;
&lt;p data-path-to-node=&quot;9&quot; data-ke-size=&quot;size16&quot;&gt;우리는 파드(Pod)를 직접 관리하지 않는다. 상위 개념들이 계층적으로 관리한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;10&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,0,0&quot;&gt;Deployment (채용 담당 매니저):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&quot;Nginx 파드 3개 유지해!&quot; 같은 **희망 상태(Desired State)**를 정의한다.&lt;/li&gt;
&lt;li&gt;파드 템플릿의 내용이 점 하나라도 바뀌면(업데이트), 해시(Hash)값이 바뀌면서 새로운 레플리카셋을 생성해 갈아치운다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,1,0&quot;&gt;ReplicaSet (반장):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 파드의 **개수(Replicas)**를 맞추는 역할을 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,2,0&quot;&gt;Pod (직원):&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;10,2,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;실제 컨테이너가 돌아가는 곳.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;10,2,1,1,0&quot;&gt;중요:&lt;/b&gt; 파드는 언제든 죽고 새로 태어나는 **'일회용품(Ephemeral)'**이다. 죽었다 살아나면 IP가 바뀐다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-path-to-node=&quot;11&quot; data-ke-size=&quot;size26&quot;&gt;3. 핵심 원리: 컨트롤러 (The Brain)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;12&quot; data-ke-size=&quot;size16&quot;&gt;쿠버네티스가 사람의 개입 없이 자동으로 시스템을 유지하는 비결은 **'컨트롤러(Controller)'**라는 감시 로봇 덕분이다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;13&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,0,0&quot;&gt;역할:&lt;/b&gt; **&quot;현재 상태(Current State)&quot;**와 사용자가 원하는 **&quot;희망 상태(Desired State)&quot;**를 끊임없이 비교하고 맞춘다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,1,0&quot;&gt;작동 방식 (Reconciliation Loop):&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;13,1,1&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,1,1,0,0&quot;&gt;비교:&lt;/b&gt; &quot;사용자는 파드 3개를 원하는데(희망 상태), 지금은 2개밖에 없네(현재 상태)?&quot;&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,1,1,1,0&quot;&gt;조치:&lt;/b&gt; &quot;그럼 1개 더 만들어야겠다!&quot; 하고 즉시 실행에 옮긴다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;13,2,0&quot;&gt;핵심:&lt;/b&gt; 배포할 때만 작동하는 게 아니라, &lt;b data-index-in-node=&quot;23&quot; data-path-to-node=&quot;13,2,0&quot;&gt;24시간 내내 무한 루프&lt;/b&gt;를 돌며 감시한다. 그래서 파드가 갑자기 죽으면 컨트롤러가 즉시 알아채고 살려내는 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;14&quot; data-ke-size=&quot;size26&quot;&gt;4. 네트워크: 서비스(Service)가 필요한 이유&lt;/h2&gt;
&lt;p data-path-to-node=&quot;15&quot; data-ke-size=&quot;size16&quot;&gt;파드는 자꾸 죽고 IP가 바뀌는데, 어떻게 접속해야 할까? 여기서 **서비스(Service)**가 등장한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;16&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,0,0&quot;&gt;역할:&lt;/b&gt; 파드의 IP가 바뀌든 말든, 변하지 않는 **고정된 가상 IP(ClusterIP)**를 제공한다. (파드들의 대표 전화번호)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,1,0&quot;&gt;동작 원리:&lt;/b&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;16,1,1&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,1,1,0,0&quot;&gt;라벨(Label):&lt;/b&gt; 서비스는 &quot;이마에 app: my-web 스티커 붙은 애들 다 모여!&quot;라고 파드를 찾는다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,1,1,1,0&quot;&gt;엔드포인트(Endpoints):&lt;/b&gt; 서비스 뒤에는 실제로 연결된 파드들의 최신 IP 목록을 적어두는 '주소록(Endpoints)'이 있다. (kubectl get ep로 확인 가능)&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;16,1,1,2,0&quot;&gt;가상 IP:&lt;/b&gt; 서비스의 IP는 실제 랜카드에 존재하는 게 아니라, kube-proxy가 네트워크 규칙(iptables)을 조작해서 트래픽을 납치해 파드로 보내주는 논리적인 IP다.&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-path-to-node=&quot;17&quot; data-ke-size=&quot;size26&quot;&gt;5. 외부 연결: 서비스 타입(Service Type) 4가지&lt;/h2&gt;
&lt;p data-path-to-node=&quot;18&quot; data-ke-size=&quot;size16&quot;&gt;상황에 따라 어떤 '문'을 열지 결정해야 한다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-path-to-node=&quot;19&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,0,0&quot;&gt;ClusterIP (기본):&lt;/b&gt; &quot;우리끼리만 쓰자.&quot;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;19,0,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부 접속 불가. 클러스터 내부 파드끼리 통신할 때 사용. (DB, 백엔드 내부 통신 등)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,1,0&quot;&gt;NodePort:&lt;/b&gt; &quot;창문 열어둘게.&quot;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;19,1,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;모든 노드의 특정 포트(예: 30000)를 개방.&lt;/li&gt;
&lt;li&gt;로컬 개발이나 테스트용으로 가장 적합하다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,2,0&quot;&gt;LoadBalancer:&lt;/b&gt; &quot;정문 안내원 고용.&quot;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;19,2,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AWS, GCP 같은 클라우드 환경에서 사용하며, 실제 공인 IP를 받는다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,2,1,1,0&quot;&gt;주의:&lt;/b&gt; Kind 환경에서는 로드밸런서를 줄 클라우드가 없어서 &amp;lt;pending&amp;gt; 상태로 멈춘다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;19,3,0&quot;&gt;ExternalName:&lt;/b&gt; &quot;단축번호.&quot;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;19,3,1&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부의 긴 도메인(예: AWS RDS 주소)을 내부에서 짧은 별칭으로 부르기 위해 사용한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-path-to-node=&quot;20&quot; data-ke-size=&quot;size26&quot;&gt;6. 실전 팁: 로컬 개발 환경 구성 (Kind + Docker Compose)&lt;/h2&gt;
&lt;p data-path-to-node=&quot;21&quot; data-ke-size=&quot;size16&quot;&gt;Kind에 백엔드를 올리고, 로컬(Docker Compose)에서 프론트엔드를 띄워 연동하려면 어떻게 해야 할까?&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-path-to-node=&quot;22&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;22,0,0&quot;&gt;추천 방식:&lt;/b&gt; 백엔드 서비스를 &lt;b data-index-in-node=&quot;16&quot; data-path-to-node=&quot;22,0,0&quot;&gt;NodePort&lt;/b&gt; 타입으로 만든다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;22,1,0&quot;&gt;Kind 설정 필수:&lt;/b&gt; Kind는 도커 컨테이너 안에 갇혀 있으므로, 클러스터 생성 시 extraPortMappings 설정을 통해 **[내 컴퓨터 포트] &amp;harr; [Kind 노드 포트]**를 연결해줘야 한다.&lt;/li&gt;
&lt;li&gt;&lt;b data-index-in-node=&quot;0&quot; data-path-to-node=&quot;22,2,0&quot;&gt;결과:&lt;/b&gt; 프론트엔드에서 localhost:30000으로 요청을 보내면 &amp;rarr; 내 컴퓨터 &amp;rarr; Kind 노드 &amp;rarr; 파드 순서로 연결된다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Container</category>
      <author>VIRGIL ABLOH</author>
      <guid isPermaLink="true">https://virgil-abloh.tistory.com/150</guid>
      <comments>https://virgil-abloh.tistory.com/150#entry150comment</comments>
      <pubDate>Sun, 11 Jan 2026 12:12:57 +0900</pubDate>
    </item>
    <item>
      <title>Context Hierarchy-컨텍스트 계층</title>
      <link>https://virgil-abloh.tistory.com/149</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;스프링 컨텍스트 계층은 크게 나누면 자식 컨텍스트인 Servlet-WebApplicationContext가 있고 그의 부모인 Root-WebApplicationContext가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Servlet-WebApplicationContext엔 주로 웹과 관련된 컨트롤러-뷰-헨들러가 있으며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Root-WebApplicationContext엔 비즈니스 계층, DB 커넥터와 같은 공통적으로 사용하는 리소스에 대한 빈을 관리한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 빈을 조회하면 먼저 자식 컨텍스트에서 조회를 하고 자식에게 빈이 없으면 부모 컨텍스트에서 조회를 하고 빈을 찾으면 자식 컨텍스트에게 주입하여 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;http 요청시 동작방식(GPT 참고)&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;&lt;b&gt;HTTP 요청 &amp;rarr; DispatcherServlet&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;클라이언트가 &lt;span&gt;/foo&lt;/span&gt; 같은 URL로 요청을 보내면, &lt;span&gt;DispatcherServlet&lt;/span&gt;(서블릿 컨텍스트)에서 받음.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;웹 전용 빈 조회&lt;/b&gt;이때 필요한 인터셉터나 뷰 리졸버 등 웹계층 전용 빈들은 &lt;span&gt;&lt;b&gt;자식(Web) 컨텍스트&lt;/b&gt;&lt;/span&gt;에서 바로 조회&lt;/li&gt;
&lt;li&gt;HandlerMapping&lt;span&gt; &amp;rarr; &lt;/span&gt;@Controller&lt;span&gt; 빈 &amp;rarr; 해당 컨트롤러의 메서드 실행&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;비즈니스 로직 호출&lt;/b&gt;&lt;br /&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;먼저 &lt;span&gt;&lt;b&gt;자식 컨텍스트&lt;/b&gt;&lt;/span&gt;에 &lt;span&gt;MyService&lt;/span&gt; 빈이 있는지 찾고,&lt;/li&gt;
&lt;li&gt;없으면 &lt;span&gt;&lt;b&gt;부모(Root) 컨텍스트&lt;/b&gt;&lt;/span&gt;로 올라가서 &lt;span&gt;MyService&lt;/span&gt; 빈을 찾아 주입&amp;middot;실행&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;컨트롤러 내부에서 &lt;span&gt;@Autowired&lt;/span&gt; 된 &lt;span&gt;MyService&lt;/span&gt; 같은 &lt;span&gt;&lt;b&gt;서비스 빈&lt;/b&gt;&lt;/span&gt;을 호출하면,&lt;/li&gt;
&lt;li&gt;&lt;b&gt;DB 접근 등 인프라 빈&lt;/b&gt;모두 부모 컨텍스트에서 조회되어 사용&lt;/li&gt;
&lt;li&gt;MyService&lt;span&gt; 안에서 &lt;/span&gt;@Autowired&lt;span&gt; 된 &lt;/span&gt;DataSource&lt;span&gt;&amp;middot;&lt;/span&gt;TransactionManager&lt;span&gt;&amp;middot;&lt;/span&gt;MyRepository&lt;span&gt; 같은 빈들도 &lt;/span&gt;모두 부모 컨텍스트에서 조회되어 사용&lt;/li&gt;
&lt;/ol&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;이런 구조를 통해&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;웹&amp;middot;비즈니스 로직을 깔끔히 분리할 수 있고,&lt;/li&gt;
&lt;li&gt;여러 서블릿이 하나의 공통 서비스&amp;middot;리포지토리를 재사용할 수 있다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>Spring Framework</category>
      <author>VIRGIL ABLOH</author>
      <guid isPermaLink="true">https://virgil-abloh.tistory.com/149</guid>
      <comments>https://virgil-abloh.tistory.com/149#entry149comment</comments>
      <pubDate>Tue, 29 Apr 2025 00:03:19 +0900</pubDate>
    </item>
    <item>
      <title>InnoDB 클러스터링 및 세컨더리 인덱스: 페이지 분할(.ibd파일)</title>
      <link>https://virgil-abloh.tistory.com/148</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2580&quot; data-origin-height=&quot;430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cBjRwE/btsLzwyzNQY/8WgxRlekww8CHBH1IPoAUk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cBjRwE/btsLzwyzNQY/8WgxRlekww8CHBH1IPoAUk/img.png&quot; data-alt=&quot;테이블 최소 생성시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cBjRwE/btsLzwyzNQY/8WgxRlekww8CHBH1IPoAUk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcBjRwE%2FbtsLzwyzNQY%2F8WgxRlekww8CHBH1IPoAUk%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;2580&quot; height=&quot;430&quot; data-origin-width=&quot;2580&quot; data-origin-height=&quot;430&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테이블 최소 생성시&lt;/figcaption&gt;
&lt;/figure&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-origin-width=&quot;2579&quot; data-origin-height=&quot;483&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bD5ICT/btsLzDLewNS/COPSanz7NKZy10yUPNpK9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bD5ICT/btsLzDLewNS/COPSanz7NKZy10yUPNpK9k/img.png&quot; data-alt=&quot;테이블 최초생성후 세컨더리 인덱스 생성시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bD5ICT/btsLzDLewNS/COPSanz7NKZy10yUPNpK9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbD5ICT%2FbtsLzDLewNS%2FCOPSanz7NKZy10yUPNpK9k%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;2579&quot; height=&quot;483&quot; data-origin-width=&quot;2579&quot; data-origin-height=&quot;483&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;테이블 최초생성후 세컨더리 인덱스 생성시&lt;/figcaption&gt;
&lt;/figure&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-origin-width=&quot;2579&quot; data-origin-height=&quot;602&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kxByU/btsLxDFrwj8/WLi78q1GJNgcJztPqD0FHk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kxByU/btsLxDFrwj8/WLi78q1GJNgcJztPqD0FHk/img.png&quot; data-alt=&quot;327개 로우 추가시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kxByU/btsLxDFrwj8/WLi78q1GJNgcJztPqD0FHk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkxByU%2FbtsLxDFrwj8%2FWLi78q1GJNgcJztPqD0FHk%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;2579&quot; height=&quot;602&quot; data-origin-width=&quot;2579&quot; data-origin-height=&quot;602&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;327개 로우 추가시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;클러스터링 인덱스는 데이터가 증가함에 따라 2개의 페이지(leaf page)로 분리되고 기존의 인덱스(root page)는 새로 분리된 2개의 페이지에 대한 주소값을 갖는다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2577&quot; data-origin-height=&quot;613&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xMER4/btsLx9qBsj4/4KHyqmxKzRpbvj8GKiORb1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xMER4/btsLx9qBsj4/4KHyqmxKzRpbvj8GKiORb1/img.png&quot; data-alt=&quot;위에서 1개 로우 추가시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xMER4/btsLx9qBsj4/4KHyqmxKzRpbvj8GKiORb1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxMER4%2FbtsLx9qBsj4%2F4KHyqmxKzRpbvj8GKiORb1%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;2577&quot; height=&quot;613&quot; data-origin-width=&quot;2577&quot; data-origin-height=&quot;613&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;위에서 1개 로우 추가시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2576&quot; data-origin-height=&quot;644&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bR03jA/btsLx1F876V/TDsqJxwlp3PkRMQd50Emwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bR03jA/btsLx1F876V/TDsqJxwlp3PkRMQd50Emwk/img.png&quot; data-alt=&quot;위에서 330개 로우 추가시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bR03jA/btsLx1F876V/TDsqJxwlp3PkRMQd50Emwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbR03jA%2FbtsLx1F876V%2FTDsqJxwlp3PkRMQd50Emwk%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;2576&quot; height=&quot;644&quot; data-origin-width=&quot;2576&quot; data-origin-height=&quot;644&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;위에서 330개 로우 추가시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2576&quot; data-origin-height=&quot;773&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DqsMn/btsLxq0FU1R/cX4LBR77bELLYwAmI4gKck/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DqsMn/btsLxq0FU1R/cX4LBR77bELLYwAmI4gKck/img.png&quot; data-alt=&quot;위에서 1개 로우 추가시&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DqsMn/btsLxq0FU1R/cX4LBR77bELLYwAmI4gKck/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDqsMn%2FbtsLxq0FU1R%2FcX4LBR77bELLYwAmI4gKck%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;2576&quot; height=&quot;773&quot; data-origin-width=&quot;2576&quot; data-origin-height=&quot;773&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;위에서 1개 로우 추가시&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;</description>
      <category>DB</category>
      <author>VIRGIL ABLOH</author>
      <guid isPermaLink="true">https://virgil-abloh.tistory.com/148</guid>
      <comments>https://virgil-abloh.tistory.com/148#entry148comment</comments>
      <pubDate>Thu, 26 Dec 2024 14:47:14 +0900</pubDate>
    </item>
    <item>
      <title>Postgrsql에서 라인 포인터의 역할</title>
      <link>https://virgil-abloh.tistory.com/147</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1942&quot; data-origin-height=&quot;1214&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dPQUYx/btsLn7Zp87K/LQ5kZ0pLeKqbwc0JkJ30YK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dPQUYx/btsLn7Zp87K/LQ5kZ0pLeKqbwc0JkJ30YK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dPQUYx/btsLn7Zp87K/LQ5kZ0pLeKqbwc0JkJ30YK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdPQUYx%2FbtsLn7Zp87K%2FLQ5kZ0pLeKqbwc0JkJ30YK%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;1942&quot; height=&quot;1214&quot; data-origin-width=&quot;1942&quot; data-origin-height=&quot;1214&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;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;만약 데이터가 삭제되면 라인포인터는 해당 로우에 대한 데이터가 비어있다고 저장하며 추후 재사용된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>DB</category>
      <author>VIRGIL ABLOH</author>
      <guid isPermaLink="true">https://virgil-abloh.tistory.com/147</guid>
      <comments>https://virgil-abloh.tistory.com/147#entry147comment</comments>
      <pubDate>Wed, 18 Dec 2024 13:54:51 +0900</pubDate>
    </item>
    <item>
      <title>Oracle, Postgresql, Mysql(InnoDB)</title>
      <link>https://virgil-abloh.tistory.com/146</link>
      <description>&lt;h3 data-ke-size=&quot;size23&quot;&gt;Oracle&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;오라클에서 트랜잭션이 커밋되면 트랜잭션 테이블의 undo 세그먼트 헤더의 state 값을 10(active) -&amp;gt; 9(committed) 처리&lt;/li&gt;
&lt;li&gt;scn(system change number)에는 현재 커밋시점의 scn으로 업데이트&lt;/li&gt;
&lt;li&gt;그 외 ITL(interested transcation list)의 항목들중 일부 컬럼들을 commit시점의 데이터로 정리하는데 이를 블록 클린 아웃&lt;/li&gt;
&lt;li&gt;만약 클린아웃할 항목들이 많으면 일단 메모리에 있는 일부만 우선 클린아웃을 하고 나머지들은 해당 데이터를 select할때 동작한다. 이를 dealay block cleanout이라고 한다. 해당 기능은 디비에 과부하를 줄이기 위해 동작한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Postgresql&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;레코드마다 튜플헤더가 존재하는데 이중에 infomask컬럼의 첫번째 byte(하위 4bits)로 해당 레코드의 트랜잭션 상태값을 표현&lt;/li&gt;
&lt;li&gt;postgresql은 delete 커맨드가 들어오면 이전 데이터는 delete, 새로운 데이터 insert 처리하여 update하여 insert된 데이터와 새로 insert된 byte는 다름&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 로컬에서 확인해본결과 일단 postgresql에 확장프로그램을 깔아서 t_infomask를 볼수 있도록 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1732947702883&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;-- 익스텐션 설치
CREATE EXTENSION pageinspect;
-- infomask 식별할수 있는 테이블생성
CREATE TABLE infomask_flags (
                                flag_name text,
                                flag_bits bit(16)
);
-- infomask컬럼의 데이터를 보고 상태값을 볼수 있는 데이터 입력
INSERT INTO infomask_flags VALUES
                               ('HEAP_HASNULL',        B'0000000000000001'),
                               ('HEAP_HASVARWIDTH',    B'0000000000000010'),
                               ('HEAP_HASEXTERNAL',    B'0000000000000100'),
                               ('HEAP_HASOID',         B'0000000000001000'),
                               ('HEAP_XMAX_KEYSHR_LOCK', B'0000000000010000'),
                               ('HEAP_COMBOCID',       B'0000000000100000'),
                               ('HEAP_XMAX_EXCL_LOCK', B'0000000001000000'),
                               ('HEAP_XMAX_LOCK_ONLY', B'0000000010000000'),
                               ('HEAP_XMIN_COMMITTED', B'0000000100000000'),
                               ('HEAP_XMIN_INVALID',   B'0000001000000000'),
                               ('HEAP_XMAX_COMMITTED', B'0000010000000000'),
                               ('HEAP_XMAX_INVALID',   B'0000100000000000'),
                               ('HEAP_XMAX_IS_MULTI',  B'0001000000000000'),
                               ('HEAP_UPDATED',        B'0010000000000000'),
                               ('HEAP_MOVED_OFF',      B'0100000000000000'),
                               ('HEAP_MOVED_IN',       B'1000000000000000');
-- 데이터 출력
SELECT
    hpi.t_ctid,
    hpi.t_xmin,
    hpi.t_xmax,
    hpi.t_infomask,
    string_agg(ifl.flag_name, ', ') AS set_flags
FROM
    heap_page_items(get_raw_page('test_table', 0)) AS hpi
        JOIN infomask_flags AS ifl
             ON (hpi.t_infomask::bit(16) &amp;amp; ifl.flag_bits)::integer::boolean
GROUP BY
    hpi.t_ctid, hpi.t_xmin, hpi.t_xmax, hpi.t_infomask;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위의 커맨드를 동작시키고 일단 데이터 한개를 insert 했을때이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2254&quot; data-origin-height=&quot;164&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cpeQa5/btsK31FfXIj/fwCDxI3RMk7O811XJ4mD9K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cpeQa5/btsK31FfXIj/fwCDxI3RMk7O811XJ4mD9K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cpeQa5/btsK31FfXIj/fwCDxI3RMk7O811XJ4mD9K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcpeQa5%2FbtsK31FfXIj%2FfwCDxI3RMk7O811XJ4mD9K%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;2254&quot; height=&quot;164&quot; data-origin-width=&quot;2254&quot; data-origin-height=&quot;164&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;xmin 컬럼에 값이 있고 xmax에 값이 없는것을 보니 이건 insert된 데이터인것을 확인할수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 set-flag를 보면 hasvarwidth는 해당 레코드에 가변 길이 데이터(예: &lt;span&gt;VARCHAR&lt;/span&gt;)가 포함되어 있다는 것을 알수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 xmin-committed는 해당 데이터는 insert하여 comit이 된 데이터라는 점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;xmax-invalid인것은 아직 해당 레코드가 update or delete 된 것이 아니라는 점을 알수가 있다.&lt;/p&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&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;2230&quot; data-origin-height=&quot;197&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bepMve/btsK2CNduTA/4bEgokpawq4JM26KKIkDg1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bepMve/btsK2CNduTA/4bEgokpawq4JM26KKIkDg1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bepMve/btsK2CNduTA/4bEgokpawq4JM26KKIkDg1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbepMve%2FbtsK2CNduTA%2F4bEgokpawq4JM26KKIkDg1%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;2230&quot; height=&quot;197&quot; data-origin-width=&quot;2230&quot; data-origin-height=&quot;197&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ctid가 같은 값인걸 보니 한 트랜잭션이라는 것을 알수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;xmin=837인 레코드가 새로 입력된 데이터이고 그 이전의 값은 이전 레코드라는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 레코드의 set-flag를 보면 이전의 데이터와 달라진 점을 알수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이전 레코드는 xmin-committed만 남았고 새로운 레코드는 xmax-invalid, updated 되었다는 것을 알수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 두개의 레코드로 알수 있는 점은 수정전의 데이터와 업데이트된 데이터는 존재하고 수정전의 데이터는 xmax값이 있기때문에 vaccum을 동작시키면 타겟팅이 되어 해당 로우는 물리적으로도 삭제되는 데이터라는 점이다.&lt;/p&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(InnoDB)&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;트랜잭션이 커밋되면 해당 커밋시점의 max-trx-id를 언두 블록의 trx-no 컬럼에 저장&lt;/li&gt;
&lt;li&gt;롤백 세그먼트 히스토리 리스트에 커밋된 언두 블록을 등록&lt;/li&gt;
&lt;li&gt;trx-sys에 active-trx 리스트중에 커밋된 trx-sturcture를 제외시켜 갱신&lt;/li&gt;
&lt;/ol&gt;</description>
      <category>DB</category>
      <author>VIRGIL ABLOH</author>
      <guid isPermaLink="true">https://virgil-abloh.tistory.com/146</guid>
      <comments>https://virgil-abloh.tistory.com/146#entry146comment</comments>
      <pubDate>Sat, 30 Nov 2024 16:00:33 +0900</pubDate>
    </item>
    <item>
      <title>Mysql(InnoDB)에서 Transaction ID 를 통해 데이터 롤백과정 정리</title>
      <link>https://virgil-abloh.tistory.com/145</link>
      <description>&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;트랜잭션과 함께 데이터 변경이 발생하면 해당 데이터 로우의 trx_id는 최신 트랜잭션 id를 저장&lt;/li&gt;
&lt;li&gt;롤백 세그먼트 영역이 별도로 존재하는데 거기에는 undo 세그먼트가 그 하위에 undo 로그로 변경 전의 데이터를 저장&lt;/li&gt;
&lt;li&gt;roll_ptr은 해당 데이터 로우에 존재하며 해당 영역에는 undo 로그의 위치를 저장&lt;/li&gt;
&lt;li&gt;아직 커밋이 안된상태에서 select 쿼리가 오면 roll_ptr를 통해 undo 로그에 저장된 변경전의 데이터를 반환
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;커밋이 되면 해당 undo 로그는 필요하지 않으므로 가비지 콜랙터에 들어가게되어 삭제&lt;/li&gt;
&lt;li&gt;롤백이 되면 해당 데이터 로우의 roll_ptr의 undo 로그에 있는 변경전 데이터를 다시 입력하여 데이터 원복&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #333333; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: disc;&quot;&gt;동작 시각화(생성: GPT)&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1732347581740&quot; class=&quot;bash&quot; style=&quot;background-color: #f6f7f8; color: #333333; text-align: start;&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;CREATE TABLE test_table (
    id INT PRIMARY KEY,
    value VARCHAR(50)
);

INSERT INTO test_table (id, value) VALUES (1, 'one');


-- 트랜잭션 1: 데이터 변경
START TRANSACTION;
UPDATE test_table SET value = 'two' WHERE id = 1;

-- 데이터 상태 확인 (트랜잭션 중)
SELECT id, value, trx_id, roll_ptr FROM information_schema.innodb_trx;

-- 결과 (예제):
-- id   | value | trx_id | roll_ptr
-- -----|-------|--------|----------
-- 1    | two   | 1001   | 0x12345678 (Undo 로그 위치)


-- 트랜잭션 롤백
ROLLBACK;

-- 데이터 상태 확인
SELECT * FROM test_table;

-- 결과:
-- id   | value
-- -----|-------
-- 1    | one   -- 변경 전 상태로 복원


-- 트랜잭션 Commit
COMMIT;

-- Undo 로그는 일정 시간이 지나면 정리됨&lt;/code&gt;&lt;/pre&gt;</description>
      <category>DB</category>
      <author>VIRGIL ABLOH</author>
      <guid isPermaLink="true">https://virgil-abloh.tistory.com/145</guid>
      <comments>https://virgil-abloh.tistory.com/145#entry145comment</comments>
      <pubDate>Sat, 23 Nov 2024 16:40:34 +0900</pubDate>
    </item>
  </channel>
</rss>