<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>kr_jkjung.log</title>
        <link>https://velog.io/</link>
        <description>DB Specialist를 향해 끊임없이 탐구하고, 배움의 과정을 기록하는 DBA의 기술 노트</description>
        <lastBuildDate>Mon, 26 May 2025 14:48:13 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <image>
            <title>kr_jkjung.log</title>
            <url>https://images.velog.io/images/kr_jkjung/profile/c885742a-e5d9-467d-9612-ca2cb5a3b976/social.png</url>
            <link>https://velog.io/</link>
        </image>
        <copyright>Copyright (C) 2019. kr_jkjung.log. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/kr_jkjung" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[PostgreSQL 튜닝 기술(상) - 4장 Join]]></title>
            <link>https://velog.io/@kr_jkjung/4%EC%9E%A5-join</link>
            <guid>https://velog.io/@kr_jkjung/4%EC%9E%A5-join</guid>
            <pubDate>Mon, 26 May 2025 14:48:13 GMT</pubDate>
            <description><![CDATA[<h2 id="📚-학습-범위-『postgresql-튜닝-기술-상』-4장-join-168-페이지-까지">📚 학습 범위: 『PostgreSQL 튜닝 기술 상』 4장 Join 168 페이지 까지</h2>
<hr>
<h3 id="1-outer-table--inner-table-개념">1. Outer Table / Inner Table 개념</h3>
<ul>
<li><strong>Outer Table</strong>: Nested Loop 조인에서 바깥 루프를 도는 테이블. 먼저 처리됨.</li>
<li><strong>Inner Table</strong>: 바깥에서 선택된 row에 대해 반복적으로 접근하는 테이블.</li>
<li><code>EXPLAIN</code> 실행계획에서 먼저 나오는 쪽이 outer, 후순위가 inner로 보면 됨.</li>
</ul>
<hr>
<h3 id="2-실행계획-분석-예시-1-nested-loop">2. 실행계획 분석 예시 1 (Nested Loop)</h3>
<pre><code class="language-text">Nested Loop
  -&gt; Seq Scan on t1      -- outer
  -&gt; Index Scan on t2    -- inner</code></pre>
<ul>
<li>t1이 먼저 실행되며 outer, 각 row에 대해 t2를 index scan</li>
<li>loop 수를 보면 t1은 loop=1, t2는 loop=101 등으로 확인 가능</li>
</ul>
<hr>
<h3 id="3-buffer-정보에서-shared-read--written-의미">3. Buffer 정보에서 shared read / written 의미</h3>
<ul>
<li>shared read: 디스크에서 shared buffer로 로딩된 블록 수</li>
<li>written: buffer 공간이 부족해서 dirty buffer가 디스크로 flush된 수</li>
<li>단순 읽기만 해도 쓰기가 발생할 수 있음 → dirty buffer 교체 때문</li>
</ul>
<hr>
<h3 id="4-materialize-노드">4. Materialize 노드</h3>
<ul>
<li>작은 inner table 결과를 <strong>메모리에 저장 (work_mem)</strong> 해 두고 outer loop에서 재사용</li>
<li>조건: 작은 테이블, work_mem 내 적재 가능</li>
<li>조인 순서에 영향: Materialize가 가능하면 작은 테이블을 inner로 둬서 reuse 유도</li>
</ul>
<h4 id="실험-결과">실험 결과</h4>
<ul>
<li>Materialize ON: t2 outer, t1 inner → 효율적</li>
<li>Materialize OFF: t1 outer, t2 inner → Index Scan 없이 반복 Full Scan, 성능 저하</li>
</ul>
<hr>
<h3 id="5-memoize--nested-loop-join">5. Memoize + Nested Loop Join</h3>
<h4 id="개념-요약">개념 요약</h4>
<ul>
<li>Memoize는 <strong>파라미터화된 Index Scan의 결과를 key 기반으로 캐시</strong>해서 재사용</li>
<li>조건: inner 쿼리가 <code>WHERE t.col = outer.col</code> 형태일 때</li>
</ul>
<h4 id="실행계획-예시">실행계획 예시</h4>
<pre><code class="language-text">Nested Loop
  -&gt; Seq Scan on bizpl_sale_sum b    -- outer
  -&gt; Memoize
      -&gt; Index Scan on product a     -- inner</code></pre>
<ul>
<li><code>b.prod_id</code> 값이 반복되므로 <code>product</code> 테이블에 대한 결과를 Memoize 노드가 캐시</li>
<li>73,200개 row 중 200번만 Index Scan → 나머지는 캐시 재활용</li>
</ul>
<hr>
<h3 id="6-memoize-통계-해석">6. Memoize 통계 해석</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>의미</th>
</tr>
</thead>
<tbody><tr>
<td>Cache Key</td>
<td>캐시 키로 사용된 파라미터 (ex. b.prod_id)</td>
</tr>
<tr>
<td>Hits</td>
<td>캐시 재사용된 횟수</td>
</tr>
<tr>
<td>Misses</td>
<td>실제로 Index Scan 한 횟수</td>
</tr>
<tr>
<td>Memory Usage</td>
<td>캐시 저장에 사용된 메모리량</td>
</tr>
</tbody></table>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[PostgreSQL 튜닝 기술(상) - 3장 Index]]></title>
            <link>https://velog.io/@kr_jkjung/PostgreSQL-%ED%8A%9C%EB%8B%9D-%EA%B8%B0%EC%88%A0%EC%83%81-3%EC%9E%A5-Index</link>
            <guid>https://velog.io/@kr_jkjung/PostgreSQL-%ED%8A%9C%EB%8B%9D-%EA%B8%B0%EC%88%A0%EC%83%81-3%EC%9E%A5-Index</guid>
            <pubDate>Wed, 14 May 2025 15:52:42 GMT</pubDate>
            <description><![CDATA[<h2 id="📚-학습-범위-『postgresql-튜닝-기술-상』-3장-index">📚 학습 범위: 『PostgreSQL 튜닝 기술 상』 3장 Index</h2>
<hr>
<h2 id="1-인덱스-듀플리케이션-index-duplication">1. 인덱스 듀플리케이션 (Index Duplication)</h2>
<ul>
<li>동일한 인덱스 키 값이 여러 개 존재할 경우, PostgreSQL은 해당 <strong>키 값을 인덱스에 한 번만 저장하고</strong>, 그 키에 해당하는 <strong>여러 TID(Tuple ID)</strong> 를 연결해서 관리한다.</li>
<li>이는 인덱스 크기를 줄이기 위한 설계이며, 키 값을 매번 저장하는 오라클 방식과 대비된다.</li>
<li>유니크 인덱스에서도 MVCC 때문에 일시적으로 같은 키 값을 가진 <strong>dead row + live row</strong> 가 존재할 수 있으며, 이 경우에도 키는 하나만 저장되고 TID만 여러 개가 유지된다.<blockquote>
<p>위 개념을 이해하기 위해서는 다 같이 PostgreSQL의 MVCC 구조를 배워보는 시간을 가져보면 좋을듯 </p>
</blockquote>
</li>
</ul>
<h2 id="2-oracle과의-인덱스-구조-비교">2. Oracle과의 인덱스 구조 비교</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>PostgreSQL</th>
<th>Oracle</th>
</tr>
</thead>
<tbody><tr>
<td>키 저장 방식</td>
<td>키 값 1개 + 다수의 TID</td>
<td>키 값 중복 저장 + 각 RowID 연결</td>
</tr>
<tr>
<td>주소 방식</td>
<td>TID (block(page) + offset, 논리적 주소)</td>
<td>RowID (물리적 주소)</td>
</tr>
<tr>
<td>설계 목적</td>
<td>공간 절약 + MVCC 최적화</td>
<td>정렬 및 빠른 직접 접근</td>
</tr>
</tbody></table>
<h2 id="3-mvcc와-인덱스-설계">3. MVCC와 인덱스 설계</h2>
<ul>
<li>PostgreSQL은 <strong>MVCC (Multi-Version Concurrency Control)</strong> 을 사용하여, 하나의 행이 여러 버전(TID)을 가질 수 있음.</li>
<li>VACUUM 이전까지는 같은 키 값을 가진 데드/라이브 로우가 공존하므로 인덱스도 여러 TID를 유지해야 함.</li>
<li>이 구조에 따라 PostgreSQL 인덱스는 <strong>중복 키 값을 하나로 통합해 저장</strong>하고, 다수의 TID를 매핑함.</li>
</ul>
<h2 id="4-table에-대해-vacuum을-수행하면-index의-dead-tid도-같이-정리-될까">4. table에 대해 vacuum을 수행하면 index의 dead TID도 같이 정리 될까?</h2>
<ul>
<li>chatgpt 답변 :<ul>
<li>VACUUM은 테이블의 데드 튜플 제거뿐 아니라 <strong>인덱스 내 데드 TID도 함께 제거</strong>한다.</li>
<li>VACUUM FULL은 테이블 및 인덱스를 <strong>완전히 재작성</strong>한다.</li>
</ul>
</li>
</ul>
<h2 id="5-reindex의-필요성">5. REINDEX의 필요성</h2>
<ul>
<li><code>REINDEX</code>는 인덱스만 <strong>별도로 재작성</strong>하는 명령어.</li>
<li>VACUUM FULL이 전체 인덱스를 재생성하긴 하지만, 다음의 경우에는 <code>REINDEX</code>가 더 적합:<ul>
<li>인덱스 손상 시 (corruption)</li>
<li>특정 인덱스에만 bloat가 집중된 경우</li>
<li>전체 테이블에 대한 vacuum full 수행이 어려운 경우(테이블에 대한 exclusive lock, 모든 index에 대한 재생성 등과 같은 이유로 인해) </li>
</ul>
</li>
</ul>
<h2 id="6-인덱스의-exact-vs-lossy-모드">6. 인덱스의 EXACT vs LOSSY 모드?</h2>
<ul>
<li><strong>EXACT 모드</strong>: TID까지 정확히 포함 → 테이블 접근 없이 처리 가능</li>
<li><strong>LOSSY(LOGICAL) 모드</strong>: 페이지 단위 정보만 포함 → Heap Scan 단계에서 조건 재검증 필요 (Recheck)</li>
</ul>
<h2 id="7-index-only-scan의-두-가지-조건">7. Index Only Scan의 두 가지 조건</h2>
<ol>
<li><strong>Index Coverage</strong>: 인덱스만으로 필요한 컬럼을 모두 제공할 수 있어야 함</li>
<li><strong>Visibility Map (VM)</strong>:<ul>
<li>각 테이블 페이지가 <strong>모든 튜플이 visible함</strong>을 기록한 비트맵<ul>
<li>dead tuple 없이 모든 live 튜플만으로 구성되어 있을 때 </li>
</ul>
</li>
</ul>
</li>
</ol>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[PostgreSQL 튜닝 기술(상) - 1,2장 ]]></title>
            <link>https://velog.io/@kr_jkjung/PostgreSQL-%ED%8A%9C%EB%8B%9D-%EA%B8%B0%EC%88%A0%EC%83%81-12%EC%9E%A5</link>
            <guid>https://velog.io/@kr_jkjung/PostgreSQL-%ED%8A%9C%EB%8B%9D-%EA%B8%B0%EC%88%A0%EC%83%81-12%EC%9E%A5</guid>
            <pubDate>Wed, 07 May 2025 14:28:17 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p><a href="https://velog.velcdn.com/images/kr_jkjung/post/06fd0c7f-3694-4602-942b-17c2242c9764/image.sql">test_ddl.sql</a></p>
</blockquote>
<h2 id="📚-학습-범위-『postgresql-튜닝-기술-상』-83페이지까지">📚 학습 범위: 『PostgreSQL 튜닝 기술 상』 83페이지까지</h2>
<hr>
<h2 id="1-gather-노드-당-병렬-실행-수란">1. <code>Gather 노드 당 병렬 실행 수</code>란?</h2>
<ul>
<li>PostgreSQL의 병렬 쿼리에서 <code>Gather</code> 노드는 병렬 워커들의 결과를 모으는 역할을 한다.</li>
<li><code>max_parallel_workers_per_gather</code>는 <strong>한 Gather 노드가 사용할 수 있는 병렬 워커의 수</strong>를 의미함.</li>
<li>OLTP 환경에서는 병렬 실행의 오버헤드가 더 클 수 있어 <code>0</code>으로 설정하면 throughput이 좋아질 수 있음.</li>
</ul>
<hr>
<h2 id="2-random_page_cost는-왜-ssd에서-낮춰야-하나">2. <code>random_page_cost</code>는 왜 SSD에서 낮춰야 하나?</h2>
<ul>
<li><code>random_page_cost</code>는 <strong>랜덤 블록 접근 비용을 추정하는 파라미터</strong>로, 기본값은 4.0 (HDD 기준).</li>
<li>PostgreSQL 옵티마이저는 이 값을 기반으로 Index Scan 사용 여부를 결정함.</li>
<li>SSD는 랜덤 I/O 속도가 빠르기 때문에 이 값을 <code>1.1 ~ 1.5</code> 정도로 낮추는 것이 적절.</li>
<li>값이 낮을수록 Index Scan이 더 자주 선택됨.</li>
</ul>
<hr>
<h2 id="3-index-scan이-랜덤-io인-이유">3. Index Scan이 랜덤 I/O인 이유</h2>
<ul>
<li>Index Scan은 인덱스를 통해 얻은 TID를 이용해 테이블(heap)의 각 블록을 개별적으로 읽는다.</li>
<li>이 블록들이 물리적으로 흩어져 있어 <strong>랜덤 I/O가 발생</strong>함.</li>
<li>반면, Seq Scan은 테이블을 순차적으로 읽기 때문에 <strong>순차 I/O</strong> 처리.</li>
</ul>
<hr>
<h2 id="4-hdd-vs-ssd-랜덤-io-성능-차이">4. HDD vs SSD: 랜덤 I/O 성능 차이</h2>
<table>
<thead>
<tr>
<th>항목</th>
<th>HDD</th>
<th>SSD</th>
</tr>
</thead>
<tbody><tr>
<td>구조</td>
<td>회전판 + 물리 헤드</td>
<td>플래시 메모리</td>
</tr>
<tr>
<td>랜덤 I/O 성능</td>
<td>느림 (seek + rotation)</td>
<td>빠름 (직접 접근)</td>
</tr>
<tr>
<td>순차 I/O 성능</td>
<td>빠름</td>
<td>빠름</td>
</tr>
<tr>
<td>PostgreSQL에서의 적용</td>
<td><code>random_page_cost = 4.0</code></td>
<td><code>random_page_cost = 1.1</code> 권고</td>
</tr>
</tbody></table>
<ul>
<li>HDD는 물리적 이동이 필요해 랜덤 I/O가 느림.</li>
<li>SSD는 메모리 기반 구조라 랜덤/순차 모두 빠름.</li>
</ul>
<hr>
<h2 id="5-work_mem의-적용-범위">5. <code>work_mem</code>의 적용 범위</h2>
<ul>
<li><code>work_mem</code>은 <strong>쿼리 전체가 아닌, 각 정렬/해시 노드마다 개별 적용</strong>되는 메모리 제한값.</li>
<li>예: <code>work_mem = 100MB</code>, Sort 노드 10개 → 최대 1GB 사용 가능.</li>
<li>병렬 쿼리인 경우 워커마다 <code>work_mem</code> 별도 적용됨.</li>
<li>너무 크게 설정 시 <strong>다수의 쿼리 동시 실행 시 메모리 부족 가능성</strong> 있음.</li>
</ul>
<hr>
<h2 id="6-exists-서브쿼리에서는-왜-startup-cost가-더-중요할까">6. <code>EXISTS</code> 서브쿼리에서는 왜 startup cost가 더 중요할까?</h2>
<ul>
<li><code>EXISTS</code>는 서브쿼리 결과가 <strong>존재하는지만 확인</strong>하고, row 하나만 찾으면 바로 종료됨.</li>
<li>그래서 전체 비용(<code>total cost</code>)보다 <strong>첫 결과를 얻기까지 드는 비용(<code>startup cost</code>)</strong>이 훨씬 더 중요함.</li>
<li>옵티마이저는 <strong>startup cost가 낮은 쿼리 계획을 선호</strong>하여 빠르게 True/False를 판단하려 함.</li>
</ul>
<hr>
<h2 id="7-loops의-의미는">7. <code>loops</code>의 의미는?</h2>
<ul>
<li><code>loops</code>는 <strong>해당 쿼리 계획 노드가 실제 실행된 횟수</strong>를 나타냄.</li>
<li><code>loops = 1</code>: 한 번만 실행됨 (예: 전체 테이블 1회 스캔)</li>
<li><code>loops = N</code>: 외부 쿼리로 인해 N번 반복 실행됨 (예: Nested Loop Join에서 내부 테이블)</li>
<li>실제 총 row 수는 <code>loops × rows</code>로 계산됨.</li>
</ul>
<p>예:</p>
<pre><code class="language-text">Index Scan on orders (actual time=0.02..0.05 rows=3 loops=10)
→ 총 3 × 10 = 30 rows 반환</code></pre>
<hr>
<h2 id="8-explain에서-generic-plan과-바인드-변수의-관계">8. EXPLAIN에서 generic plan과 바인드 변수의 관계</h2>
<ul>
<li>바인드 변수는 SQL문을 재사용하면서 값만 나중에 전달하는 방식.</li>
<li>PostgreSQL은 바인드 변수가 있을 경우, 처음엔 custom plan을 만들고, 일정 횟수 이후 generic plan으로 전환할지 판단함.</li>
<li>Custom Plan: 바인드 값에 따라 실행 계획이 매번 달라짐</li>
<li>Generic Plan: 바인드 값과 관계없이 하나의 고정된 계획을 반복 사용</li>
<li>특정 바인드 값에 따라 성능 차이가 클 경우, generic plan이 비효율적일 수 있음.</li>
</ul>
<hr>
<h2 id="✅-참고-파라미터-정리">✅ 참고 파라미터 정리</h2>
<table>
<thead>
<tr>
<th>파라미터</th>
<th>설명</th>
</tr>
</thead>
<tbody><tr>
<td><code>max_parallel_workers_per_gather</code></td>
<td>Gather 노드 당 병렬 워커 수</td>
</tr>
<tr>
<td><code>random_page_cost</code></td>
<td>랜덤 I/O 비용 추정치</td>
</tr>
<tr>
<td><code>seq_page_cost</code></td>
<td>순차 I/O 비용 추정치</td>
</tr>
<tr>
<td><code>work_mem</code></td>
<td>노드별 임시 작업 메모리 (정렬, 해시 등)</td>
</tr>
</tbody></table>
]]></description>
        </item>
        <item>
            <title><![CDATA[PostgreSQL tunning]]></title>
            <link>https://velog.io/@kr_jkjung/PostgreSQL-tunning</link>
            <guid>https://velog.io/@kr_jkjung/PostgreSQL-tunning</guid>
            <pubDate>Wed, 07 May 2025 12:58:59 GMT</pubDate>
            <description><![CDATA[<p><a href="https://edbkorea.com/blog/postgresql-oltp-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94/">https://edbkorea.com/blog/postgresql-oltp-%EC%84%B1%EB%8A%A5-%EC%B5%9C%EC%A0%81%ED%99%94/</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[dplyr 설치 및 PostgreSQL PL/R 테스트]]></title>
            <link>https://velog.io/@kr_jkjung/dplyr</link>
            <guid>https://velog.io/@kr_jkjung/dplyr</guid>
            <pubDate>Wed, 07 May 2025 02:31:24 GMT</pubDate>
            <description><![CDATA[<h2 id="📦-dplyr-설치를-위한-종속-라이브러리-설치">📦 dplyr 설치를 위한 종속 라이브러리 설치</h2>
<pre><code class="language-bash"># tibble 관련 패키지
sudo R CMD INSTALL /data1/staging/R_lib/dplyr/tibble/pillar/utf8_1.2.5.tar.gz
sudo R CMD INSTALL /data1/staging/R_lib/dplyr/tibble/pillar_1.10.2.tar.gz
sudo R CMD INSTALL /data1/staging/R_lib/dplyr/tibble/pkgconfig_2.0.3.tar.gz
sudo R CMD INSTALL /data1/staging/R_lib/dplyr/tibble/magrittr_2.0.3.tar.gz
sudo R CMD INSTALL /data1/staging/R_lib/dplyr/tibble/fansi_1.0.6.tar.gz
sudo R CMD INSTALL /data1/staging/R_lib/dplyr/tibble_3.2.1.tar.gz

# tidyselect 관련 패키지
sudo R CMD INSTALL /data1/staging/R_lib/dplyr/tidyselect/withr_3.0.2.tar.gz
sudo R CMD INSTALL /data1/staging/R_lib/dplyr/tidyselect_1.2.1.tar.gz

# 기타 의존 패키지
sudo R CMD INSTALL /data1/staging/R_lib/dplyr/R6_2.6.1.tar.gz
sudo R CMD INSTALL /data1/staging/R_lib/dplyr/generics_0.1.3.tar.gz

# dplyr 설치
sudo R CMD INSTALL /data1/staging/R_lib/dplyr/dplyr_1.1.4.tar.gz</code></pre>
<hr>
<h2 id="✅-dplyr-설치-확인-r-콘솔">✅ dplyr 설치 확인 (R 콘솔)</h2>
<pre><code class="language-r">library(dplyr)
cat(&quot;dplyr version:&quot;, as.character(packageVersion(&quot;dplyr&quot;)), &quot;\n&quot;)</code></pre>
<p>출력 예:</p>
<pre><code>dplyr version: 1.1.4</code></pre><hr>
<h2 id="🧪-postgresql-plr-테스트-함수">🧪 PostgreSQL PL/R 테스트 함수</h2>
<pre><code class="language-sql">CREATE OR REPLACE FUNCTION dplyr_test() RETURNS text AS $$
  library(dplyr)

  df &lt;- data.frame(
    category = c(&quot;A&quot;, &quot;B&quot;, &quot;A&quot;, &quot;B&quot;, &quot;A&quot;),
    value = c(10, 20, 15, 25, 30)
  )

  result &lt;- df %&gt;%
    group_by(category) %&gt;%
    summarise(total = sum(value), .groups = &quot;drop&quot;)

  result &lt;- as.data.frame(result)

  paste(capture.output(print(result)), collapse = &quot;\n&quot;)
$$ LANGUAGE plr;</code></pre>
<hr>
<h2 id="🧾-실행-결과">🧾 실행 결과</h2>
<pre><code class="language-sql">SELECT dplyr_test();</code></pre>
<pre><code> category total
 1        A    55
 2        B    45
(1 row)</code></pre><hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[PL/R 환경에서 R 분석 패키지 설치 및 테스트 (reshape2)]]></title>
            <link>https://velog.io/@kr_jkjung/PLR-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-R-%EB%B6%84%EC%84%9D-%ED%8C%A8%ED%82%A4%EC%A7%80-%EC%84%A4%EC%B9%98-%EB%B0%8F-%ED%85%8C%EC%8A%A4%ED%8A%B8-reshape2</link>
            <guid>https://velog.io/@kr_jkjung/PLR-%ED%99%98%EA%B2%BD%EC%97%90%EC%84%9C-R-%EB%B6%84%EC%84%9D-%ED%8C%A8%ED%82%A4%EC%A7%80-%EC%84%A4%EC%B9%98-%EB%B0%8F-%ED%85%8C%EC%8A%A4%ED%8A%B8-reshape2</guid>
            <pubDate>Mon, 05 May 2025 10:57:59 GMT</pubDate>
            <description><![CDATA[<h4 id="📦-reshape2-호출-테스트">📦 reshape2 호출 테스트</h4>
<hr>
<h2 id="1-파일-준비">1. 파일 준비</h2>
<h3 id="🌐-필요한-r-라이브러리-다운로드">🌐 필요한 R 라이브러리 다운로드</h3>
<p>CRAN에서 필요한 패키지를 수동 다운로드<br>🔗 <a href="https://cran.r-project.org/web/packages/available_packages_by_name.html">https://cran.r-project.org/web/packages/available_packages_by_name.html</a></p>
<hr>
<h3 id="📁-다운로드-파일-업로드-확인">📁 다운로드 파일 업로드 확인</h3>
<pre><code class="language-bash">[postgres@pgprimary01 R_lib]$ ls | grep reshape2
reshape2_1.4.4.tar.gz</code></pre>
<hr>
<h2 id="2-reshape2-및-종속-패키지-설치">2. reshape2 및 종속 패키지 설치</h2>
<h3 id="📦-reshape2-의존-패키지-설치">📦 reshape2 의존 패키지 설치</h3>
<pre><code class="language-bash">sudo R CMD INSTALL /data1/staging/R_lib/Rcpp_1.0.14.tar.gz
sudo R CMD INSTALL /data1/staging/R_lib/plyr_1.8.9.tar.gz
sudo R CMD INSTALL /data1/staging/R_lib/stringr_1.5.1.tar.gz # 밑에서 의존성 해결 필요</code></pre>
<h3 id="📦-stringr-의존-패키지-설치">📦 stringr 의존 패키지 설치</h3>
<pre><code class="language-bash">sudo R CMD INSTALL /data1/staging/R_lib/stringi_1.8.7.tar.gz
sudo R CMD INSTALL /data1/staging/R_lib/cli_3.6.5.tar.gz
sudo R CMD INSTALL /data1/staging/R_lib/glue_1.8.0.tar.gz
sudo R CMD INSTALL /data1/staging/R_lib/magrittr_2.0.3.tar.gz
sudo R CMD INSTALL /data1/staging/R_lib/rlang_1.1.6.tar.gz
sudo R CMD INSTALL /data1/staging/R_lib/lifecycle_1.0.4.tar.gz
sudo R CMD INSTALL /data1/staging/R_lib/vctrs_0.6.5.tar.gz
sudo R CMD INSTALL /data1/staging/R_lib/stringr_1.5.1.tar.gz</code></pre>
<h3 id="📦-reshape2-설치">📦 reshape2 설치</h3>
<pre><code class="language-bash">sudo R CMD INSTALL /data1/staging/R_lib/reshape2_1.4.4.tar.gz</code></pre>
<hr>
<h2 id="2-reshape2-설치-확인-r-콘솔">2. reshape2 설치 확인 (R 콘솔)</h2>
<pre><code class="language-r">library(reshape2)
cat(&quot;reshape2 version:&quot;, as.character(packageVersion(&quot;reshape2&quot;)), &quot;\n&quot;)</code></pre>
<pre><code>reshape2 version: 1.4.4</code></pre><hr>
<h2 id="3-postgresql-내-plr-함수-테스트">3. PostgreSQL 내 PL/R 함수 테스트</h2>
<pre><code class="language-sql">CREATE OR REPLACE FUNCTION reshape2_test() RETURNS text AS $$
  library(reshape2)
  df &lt;- data.frame(
    id = c(1,1,2,2),
    time = c(&quot;morning&quot;,&quot;evening&quot;,&quot;morning&quot;,&quot;evening&quot;),
    value = c(10,20,30,40)
  )
  result &lt;- dcast(df, id ~ time)
  paste(capture.output(print(result)), collapse = &quot;\n&quot;)
$$ LANGUAGE plr;</code></pre>
<pre><code class="language-sql">SELECT reshape2_test();</code></pre>
<pre><code> id evening morning
 1      20      10
 2      40      30</code></pre><hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[# PostgreSQL에서 R 사용하기 (PL/R extension 설치)]]></title>
            <link>https://velog.io/@kr_jkjung/PostgreSQL%EC%97%90%EC%84%9C-R-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@kr_jkjung/PostgreSQL%EC%97%90%EC%84%9C-R-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Mon, 05 May 2025 09:54:52 GMT</pubDate>
            <description><![CDATA[<h2 id="📌-시스템-환경">📌 시스템 환경</h2>
<ul>
<li>OS: Red Hat Enterprise Linux 8.10</li>
<li>DB: PostgreSQL 16</li>
<li>R: 4.5.0-3.el8 (EPEL)</li>
<li>PL/R 버전: 8.4.7</li>
</ul>
<hr>
<h2 id="1-r-설치">1. R 설치</h2>
<h3 id="💡-사전-준비">💡 사전 준비</h3>
<p><code>R</code>을 설치하기 위해선 여러 dependency를 직접 다운로드해 설치가 필요하다. 
<del>(유료 구독을 쓰는 사람들은 논외)</del> </p>
<h3 id="📦-수동-설치한-패키지-목록">📦 수동 설치한 패키지 목록</h3>
<pre><code class="language-bash"># R 설치를 위한 dependency
openblas-0.3.15-6.el8.x86_64.rpm

# openblas-0.3.15-6.el8.x86_64.rpm 설치를 위한 dependency list
openblas-devel-0.3.15-6.el8.x86_64.rpm
openblas-openmp-0.3.15-6.el8.x86_64.rpm
openblas-openmp64-0.3.15-6.el8.x86_64.rpm
openblas-openmp64_-0.3.15-6.el8.x86_64.rpm
openblas-serial64-0.3.15-6.el8.x86_64.rpm
openblas-serial64_-0.3.15-6.el8.x86_64.rpm
openblas-threads64-0.3.15-6.el8.x86_64.rpm
openblas-threads64_-0.3.15-6.el8.x86_64.rpm

</code></pre>
<blockquote>
<p>위 패키지들은 일반 repo에서는 구할 수 없어 수동으로 rpm을 다운받아 설치 진행</p>
</blockquote>
<hr>
<h2 id="2-plr-설치">2. PL/R 설치</h2>
<h3 id="📥-plr-rpm-다운로드">📥 PL/R <code>.rpm</code> 다운로드</h3>
<pre><code class="language-bash">wget https://download.postgresql.org/pub/repos/yum/16/redhat/rhel-8-x86_64/plr_16-8.4.7-1PGDG.rhel8.x86_64.rpm</code></pre>
<h3 id="📂-서버에-업로드-및-설치">📂 서버에 업로드 및 설치</h3>
<pre><code class="language-bash"># 업로드 확인
ls | grep plr
# 출력 예시:
plr_16-8.4.7-1PGDG.rhel8.x86_64.rpm

# 설치
sudo dnf install ./plr_16-8.4.7-1PGDG.rhel8.x86_64.rpm</code></pre>
<hr>
<h2 id="3-postgresql에서-plr-확장-사용">3. PostgreSQL에서 PL/R 확장 사용</h2>
<h3 id="🔍-확장-가능한지-확인">🔍 확장 가능한지 확인</h3>
<pre><code class="language-sql">SELECT * FROM pg_available_extensions WHERE name = &#39;plr&#39;;</code></pre>
<pre><code> name | default_version | installed_version |                            comment
------+-----------------+-------------------+----------------------------------------------------------------
 plr  | 8.4.7           |                   | load R interpreter and execute R script from within a database</code></pre><h3 id="✅-확장-생성-및-버전-확인">✅ 확장 생성 및 버전 확인</h3>
<pre><code class="language-sql">CREATE EXTENSION plr;
SELECT plr_version();</code></pre>
<pre><code class="language-text"> plr_version
-------------
 8.4</code></pre>
<hr>
<h2 id="4-마무리">4. 마무리</h2>
<h3 id="✅-간단한-테스트-함수">✅ 간단한 테스트 함수</h3>
<pre><code class="language-sql">CREATE OR REPLACE FUNCTION r_now() RETURNS text AS $$
  sprintf(&quot;R time is now: %s&quot;, Sys.time())
$$ LANGUAGE plr

SELECT r_now();
                   r_now
-------------------------------------------
 R time is now: 2025-05-05 06:00:52.607928
(1 row)
</code></pre>
<blockquote>
<p>PostgreSQL 내부에서 직접 R 코드를 실행</p>
</blockquote>
<hr>
]]></description>
        </item>
        <item>
            <title><![CDATA[FastAPI로 자연어 기반 PostgreSQL 질의하기(1)]]></title>
            <link>https://velog.io/@kr_jkjung/FastAPI%EB%A1%9C-%EC%9E%90%EC%97%B0%EC%96%B4-%EA%B8%B0%EB%B0%98-PostgreSQL-%EC%A7%88%EC%9D%98%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@kr_jkjung/FastAPI%EB%A1%9C-%EC%9E%90%EC%97%B0%EC%96%B4-%EA%B8%B0%EB%B0%98-PostgreSQL-%EC%A7%88%EC%9D%98%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 06 Apr 2025 14:26:36 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>FastAPI 사용을 위한 환경 구성 절차</p>
</blockquote>
<p>✅ 실험 목적</p>
<ul>
<li>AI 에이전트가 자연어로 PostgreSQL에 질문을 던지는 구조를 실험</li>
<li>FastAPI를 이용해서 간단한 REST API 서버를 구성함으로써, LLM과 DB 사이의 연동 흐름을 손쉽게 체험하고 테스트</li>
<li>궁극적으로는 MCP와의 차이를 확인하기 위함</li>
</ul>
<h3 id="사전-작업-root-권한">사전 작업 (root 권한)</h3>
<ul>
<li>먼저 psycopg2 패키지 빌드를 위해 Python 개발 헤더와 C 컴파일러를 설치<pre><code>dnf install -y python3-devel gcc</code></pre></li>
</ul>
<p>[이하 작업은 postgres 유저로 진행]</p>
<h3 id="1-실험용-디렉토리-만들기">1. 실험용 디렉토리 만들기</h3>
<pre><code>mkdir /data1/api_pg_test
cd /data1/api_pg_test</code></pre><h3 id="2-python-가상환경venv-생성">2. Python 가상환경(venv) 생성</h3>
<ul>
<li>프롬프트 앞에 (venv)가 붙으면 가상환경이 활성화된 상태<pre><code>python3 -m venv venv
source venv/bin/activate</code></pre></li>
</ul>
<h3 id="3-필요한-패키지-설치">3. 필요한 패키지 설치</h3>
<ul>
<li>FastAPI 사용을 위한 패키지 설치<pre><code>pip install fastapi uvicorn psycopg2</code></pre></li>
</ul>
<h3 id="4-fastapi-서버-코드-작성">4. FastAPI 서버 코드 작성</h3>
<pre><code>vi api_server.py</code></pre><ul>
<li>python code<pre><code>from fastapi import FastAPI
from pydantic import BaseModel
import psycopg2
import re
from typing import Any
</code></pre></li>
</ul>
<p>app = FastAPI()</p>
<h1 id="postgresql-연결-설정">PostgreSQL 연결 설정</h1>
<p>DB_CONFIG = {
    &quot;host&quot;: &quot;localhost&quot;,
    &quot;port&quot;: 5432,
    &quot;user&quot;: &quot;postgres&quot;,
    &quot;password&quot;: &quot;yourpassword&quot;,
    &quot;dbname&quot;: &quot;yourdb&quot;
}</p>
<p>class PGRequest(BaseModel):
    input: str</p>
<p>class PGResponse(BaseModel):
    output: Any</p>
<h1 id="간단한-프롬프트-→-쿼리-매핑-함수">간단한 프롬프트 → 쿼리 매핑 함수</h1>
<p>def prompt_to_query(prompt: str) -&gt; str:
    if &quot;고객&quot; in prompt and &quot;주문&quot; in prompt:
        # 고객 ID 추출 (예: &quot;고객 2&quot; → 2)
        match = re.search(r&quot;고객\s*(\d+)&quot;, prompt)
        if match:
            customer_id = match.group(1)
            return f&quot;SELECT * FROM orders WHERE customer_id = {customer_id} ORDER BY order_date DESC LIMIT 5;&quot;
        else:
            return &quot;SELECT &#39;고객 ID를 찾을 수 없습니다.&#39; AS message;&quot;
    else:
        return &quot;SELECT &#39;알 수 없는 요청입니다.&#39; AS message;&quot;</p>
<p>@app.post(&quot;/mcp/query&quot;, response_model=PGResponse)
async def handle_query(req: PGRequest):
    prompt = req.input
    query = prompt_to_query(prompt)</p>
<pre><code>try:
    conn = psycopg2.connect(**DB_CONFIG)
    cur = conn.cursor()
    cur.execute(query)
    rows = cur.fetchall()
    colnames = [desc[0] for desc in cur.description]
    result = [dict(zip(colnames, row)) for row in rows]
    cur.close()
    conn.close()
except Exception as e:
    return PGResponse(output={&quot;error&quot;: str(e)})

return PGResponse(output=result)</code></pre><p>@app.get(&quot;/&quot;)
def root():
    return {&quot;status&quot;: &quot;API PostgreSQL 서버 실행 중 🚀&quot;}</p>
<pre><code>
### 5. FastAPI 서버 실행 

* 로컬 접속만 허용하는 경우</code></pre><p>uvicorn api_server:app --reload</p>
<p>Uvicorn running on <a href="http://127.0.0.1:8000">http://127.0.0.1:8000</a> (Press CTRL+C to quit)</p>
<pre><code>
* 외부 접속 허용하는 경우 (서버 IP 바인딩)
- 0.0.0.0으로 설정하면 모든 IP 대역에서 접속 허용 (실험용일 땐 편리하지만, 운영 환경에서는 위험할 수 있음)</code></pre><p>uvicorn api_server:app --host 192.x.x.x --port 8000 --reload</p>
<pre><code>

### 6. 로컬 PC에서 접속 테스트

http://192.x.x.x:8000/
* 아래 메세지가 뜨면 정상
{&quot;status&quot;:&quot;API PostgreSQL 서버 실행 중 🚀&quot;}

### 7. 조회 테스트

* Swagger UI를 통해 API 자연어 질의 테스트

- Request</code></pre><p>{
  &quot;input&quot;: &quot;고객 2의 주문 내역 보여줘&quot;
}</p>
<pre><code>- Response</code></pre><p>{
  &quot;output&quot;: [
    {
      &quot;id&quot;: 3,
      &quot;customer_id&quot;: 2,
      &quot;order_date&quot;: &quot;2024-12-03&quot;,
      &quot;amount&quot;: 20000
    }
  ]
}
```</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Citus]]></title>
            <link>https://velog.io/@kr_jkjung/Citus</link>
            <guid>https://velog.io/@kr_jkjung/Citus</guid>
            <pubDate>Fri, 04 Apr 2025 14:18:02 GMT</pubDate>
            <description><![CDATA[<p>citus와 greenplum 비교글</p>
<ul>
<li>추후 자세히 들여다 보면 좋을듯</li>
</ul>
<p><a href="https://docs.arenadata.io/en/blog/current/ADB/greenplum-vs-citus.html">https://docs.arenadata.io/en/blog/current/ADB/greenplum-vs-citus.html</a></p>
<p><a href="https://docs.arenadata.io/en/blog/current/ADB/greenplum-vs-citus-2.html">https://docs.arenadata.io/en/blog/current/ADB/greenplum-vs-citus-2.html</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[PostgreSQL HA test]]></title>
            <link>https://velog.io/@kr_jkjung/PostgreSQL-HA-test</link>
            <guid>https://velog.io/@kr_jkjung/PostgreSQL-HA-test</guid>
            <pubDate>Thu, 30 Jan 2025 09:34:41 GMT</pubDate>
            <description><![CDATA[<h3 id="testing-your-ha-solution">Testing Your HA Solution</h3>
<p>Testing an HA solution is a time consuming process, with many variables. This is particularly true considering a cross-platform application. You need a trained system administrator or a consultant to do this work. It is not something we can cover in depth in the documentation.</p>
<p>That said, here are some pieces of your infrastructure you should be sure to test:</p>
<ul>
<li>Network (the network in front of your system as well as the NICs [physical or virtual] themselves)</li>
<li>Disk IO</li>
<li>file limits (nofile in Linux)</li>
<li>RAM. Even if you have oomkiller turned off, the unavailability of RAM could cause issues.</li>
<li>CPU</li>
<li>Virtualization Contention (overcommitting the hypervisor)</li>
<li>Any cgroup limitation (likely to be related to the above)</li>
<li>kill -9 of any postgres process (except postmaster!). This is a decent simulation of a segfault.</li>
</ul>
<p>One thing that you should not do is run kill -9 on a postmaster process. This is because doing so does not mimic any real life scenario. If you are concerned your infrastructure is insecure and an attacker could run kill -9, no amount of HA process is going to fix that. The attacker will simply kill the process again, or cause chaos in another way.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[PostgreSQL 고가용성(HA) 솔루션 비교]]></title>
            <link>https://velog.io/@kr_jkjung/PostgreSQL-%EA%B3%A0%EA%B0%80%EC%9A%A9%EC%84%B1HA-%EC%86%94%EB%A3%A8%EC%85%98-%EB%B9%84%EA%B5%90</link>
            <guid>https://velog.io/@kr_jkjung/PostgreSQL-%EA%B3%A0%EA%B0%80%EC%9A%A9%EC%84%B1HA-%EC%86%94%EB%A3%A8%EC%85%98-%EB%B9%84%EA%B5%90</guid>
            <pubDate>Thu, 30 Jan 2025 07:04:33 GMT</pubDate>
            <description><![CDATA[<p>PosgreSQL에서는 이중화를 위한 몇 가지 기능(논리적 복제, 스트리밍 복제)을 제공한다.
그러나, 기본 기능만으로는 자동 장애 조치 (Failover) 및 모니터링 기능이 제한적이므로, 보다 강력한 고가용성 관리를 위해 추가적인 솔루션이 필요하다.
이 페이지에서는 HA에 필요한 사항을 기술하고, PostgreSQL의 고가용성을 보장하기 위한 네 가지 주요 솔루션을 비교하고자 한다.</p>
<h3 id="postgresql-ha">PostgreSQL HA</h3>
<p><a href="https://cloud.google.com/architecture/architectures-high-availability-postgresql-clusters-compute-engine?hl=ko#postgresql_ha_architectures">클라우드 아키텍처 센터</a>
구글 클라우드에서는 가장 기본적인 수준에서 데이터 계층 HA는 다음으로 구성된다고 말한다.</p>
<ul>
<li>기본 노드의 장애 발생 여부를 식별하는 메커니즘</li>
<li>복제본 노드가 기본 노드로 승격되는 경우 장애 조치를 수행하는 프로세스</li>
<li>애플리케이션 요청이 새 기본 노드에 도달할 수 있도록 쿼리 라우팅을 변경하는 프로세스</li>
<li>장애 조치 전 원래 용량의 기본 노드와 복제본 노드를 사용하여 원래 아키텍처로 대체할 수 있는 방법(선택사항)</li>
</ul>
<h3 id="postgresql-ha-solution">PostgreSQL HA solution</h3>
<p>위에서 말한 네 가지 사항을 준수하는 PostgreSQL HA 솔루션에 대해 기술한다. 각 솔루션 별 HA 아키텍처를 설명하고 장 단점을 기술하고자 한다.</p>
<ul>
<li>pg_auto_failover</li>
<li>patroni + etcd</li>
<li>repmgr</li>
<li>pg_poolII</li>
</ul>
<h4 id="1-pg_auto_failover">1. Pg_auto_failover</h4>
<p><a href="https://pg-auto-failover.readthedocs.io/en/main/architecture.html">pg_auto_failover docs</a>
pg_auto_failover는 적극적으로 개발 중인 오픈소스(PostgreSQL 라이선스) PostgreSQL 확장 프로그램(extension)이다. 올바른 HA 구성을 위해서는 최소 3개의 노드가 필요하며, 각각 모니터 노드, 기본 노드, 보조 노드 역할을 수행한다.</p>
<p><strong>구성 요소</strong></p>
<ul>
<li>Monitor(관리 노드): 클러스터의 상태를 모니터링하고 자동 장애 조치를 수행하는 중앙 관리 노드</li>
<li>Primary(주 노드): 애플리케이션이 읽기 및 쓰기 작업을 수행하는 기본 데이터베이스 노드</li>
<li>Sencondary(스탠바이 노드): Primary 노드를 실시간으로 복제하여 장애 발생 시 즉시 승격 가능</li>
</ul>
<p>PAF 아키텍처(single)
<img src="https://velog.velcdn.com/images/kr_jkjung/post/fa61ad36-e8f3-4387-a6bd-0207fd73c058/image.svg" alt="">
PAF 아키텍처(multi)
<img src="https://velog.velcdn.com/images/kr_jkjung/post/9f5537d6-3567-41c3-a680-5cd3e8e3f0dd/image.svg" alt=""></p>
<p>장점</p>
<ul>
<li>비교적 구성이 간단함</li>
<li>auto FO 가능</li>
<li>수동 전환 가능</li>
<li>국내,외 reference가 많음 (N사 Cloud DB for PostgreSQL, VMware PostgreSQL 등) </li>
<li>split-brain 상황을 회피할 수 있음(Monitor 노드로 인해)</li>
<li>PostgreSQL 이외의 다른 외부 구성 요소에 의존하지 않음</li>
</ul>
<p>단점</p>
<ul>
<li>모니터 노드 장애시 FO 불가</li>
</ul>
<h4 id="2-repmgr">2. repmgr</h4>
<p><a href="https://www.repmgr.org/docs/current/index.html">repmgr Docs</a>
repmgr 는 PostgreSQL 서버 클러스터에서 복제 및 장애 조치를 관리하기 위한 오픈 소스 도구 모음이다. PostgreSQL의 기본 제공(hot-standby) 기능을 향상시켜 스탠바이 서버를 설정하고, 복제를 모니터링하고, 장애 조치나 수동 스위치오버 작업과 같은 관리 작업을 수행할 수 있는 도구를 제공한다.</p>
<p><strong>구성요소</strong></p>
<ul>
<li>Witness(증인 서버):(필수는 아님)여러 개의 대기 서버가 있는 장애 조치 상황에서 새로운 기본 서버를 결정하는 데 도움</li>
<li>Primary(주 서버): 애플리케이션이 읽기 및 쓰기 작업을 수행하는 기본 데이터베이스 노드</li>
<li>Sencondary(스탠바이 서버): Primary 노드를 실시간으로 복제하여 장애 발생 시 즉시 승격 가능</li>
</ul>
<p>장점</p>
<ul>
<li>open source</li>
<li>Auto FO 가능</li>
<li>수동 전환 가능</li>
<li>witness server를 활용하여, 일시적인 통신 장애로 인한 FO 상황을 회피하거나 split-brain 상황을 회피할 수 있음</li>
</ul>
<p>단점</p>
<ul>
<li>장애 이후, 수동 조치 필요</li>
<li>split-brain 상황이 쉽게 발생할 여지가 있음</li>
<li>FO 로직이 다른 솔루션에 비해 단순</li>
</ul>
<h4 id="3-patroni">3. Patroni</h4>
<p><a href="https://patroni.readthedocs.io/en/latest/">Patroni Docs</a>
Patroni는 PostgreSQL 데이터베이스에서 고가용성을 구현하기 위한 Python 기반 소프트웨어 템플릿이다.
템플릿 이기에, 다양한 환경에서 유연한 설정이 가능하며 복잡한 HA 시나리오를 지원한다.
완전한 HA 클러스터 구현을 위해서는 etcd,Consul,Zookeeper와 함께 사용이 필요하다.</p>
<p><strong>구성요소</strong></p>
<ul>
<li>patroni: HA cluster template</li>
<li>DCS(Distributed Configuration Store): 클러스터의 상태를 저장하고 장애 감지 기능을 수행할 수 있는 분산 저장소(ex. etcd)</li>
<li>load balancer:App에서 단일 엔드포인트를 통해 DB connection을 유지하도록 도와줌(ex. HAProxy)</li>
<li>Postgres:Patroni를 사용하여 관리되는 PosgreSQL 인스턴스</li>
</ul>
<p>Patroni 아키텍처
<a href="https://medium.com/@chriskevin_80184/high-availability-ha-postgresql-cluster-with-patroni-1af7a528c6be">High-availability (HA) PostgreSQL Cluster with Patroni</a>
<img src="https://velog.velcdn.com/images/kr_jkjung/post/7f92c7f3-d40d-43de-873e-dba7d78a65fb/image.webp" alt=""></p>
<p>장점</p>
<ul>
<li>REST API 제공</li>
<li>다양한 기능을 갖추고 있으며, 유연하게 적용 가능하다</li>
<li>다양한 고가용성 테스트 시나리오에서 매우 잘 수행된다</li>
</ul>
<p>단점</p>
<ul>
<li>비교적 구성 난이도가 높다</li>
<li>별도 솔루션과 함께 써야, HA 구성이 완벽해진다</li>
<li>별도 솔루션에 대한 대처 능력이 필요하다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[PostgreSQL 내부 동작 이해: Write 작업과 WAL, Shared Buffer, Checkpoint]]></title>
            <link>https://velog.io/@kr_jkjung/PostgreSQL-%EB%82%B4%EB%B6%80-%EB%8F%99%EC%9E%91-%EC%9D%B4%ED%95%B4-Write-%EC%9E%91%EC%97%85%EA%B3%BC-WAL-Shared-Buffer-Checkpoint</link>
            <guid>https://velog.io/@kr_jkjung/PostgreSQL-%EB%82%B4%EB%B6%80-%EB%8F%99%EC%9E%91-%EC%9D%B4%ED%95%B4-Write-%EC%9E%91%EC%97%85%EA%B3%BC-WAL-Shared-Buffer-Checkpoint</guid>
            <pubDate>Sun, 26 Jan 2025 09:28:23 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>PostgreSQL은 write 작업에 대해 어떻게 내부적으로 처리할까?
WAL 파일은 무엇이고, 언제 데이터 파일로 반영될까? </p>
</blockquote>
<p>위와 같은 궁금즘에서 시작된 질문에 대한 답을 풀어나가기 위해 PostgreSQL의 내부 동작을 단계적으로 살펴보았다.</p>
<h3 id="1-write-작업의-기본-흐름">1. Write 작업의 기본 흐름</h3>
<ol>
<li>클라이언트의 데이터 변경 요청<ul>
<li>INSERT, UPDATE, DELETE 등 데이터 변경 쿼리를 실행하면 PostgreSQL은 데이터를 처리할 준비를 합니다.</li>
</ul>
</li>
</ol>
<ol start="2">
<li>데이터가 shared_buffer에 기록</li>
</ol>
<ul>
<li>PostgreSQL은 데이터를 바로 디스크에 쓰지 않고 shared_buffer(메모리 공간)에 우선 기록합니다. 이는 디스크 I/O를 줄이고 성능을 향상시키기 위한 메커니즘입니다.</li>
</ul>
<ol start="3">
<li>WAL에 변경 내용 기록</li>
</ol>
<ul>
<li><p>변경된 데이터는 shared_buffer에 기록되는 동시에 <strong>WAL(Write-Ahead Log)</strong>에도 기록됩니다.</p>
</li>
<li><p>WAL은 트랜잭션의 변경 내용을 순차적으로 저장하여 장애 발생 시 복구를 가능하게 합니다.</p>
</li>
</ul>
<ol start="4">
<li>트랜잭션 Commit</li>
</ol>
<ul>
<li>클라이언트가 COMMIT을 실행하면 PostgreSQL은 WAL에 기록된 내용을 디스크에 플러시(flush)하여 트랜잭션이 안전하게 완료되었음을 보장합니다.</li>
</ul>
<ol start="5">
<li>데이터 파일 반영 (Checkpoint)</li>
</ol>
<ul>
<li>PostgreSQL은 특정 시점에 Checkpoint를 실행하여 shared_buffer의 내용을 실제 디스크의 데이터 파일에 기록합니다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[PostgreSQL에서 pg_analytics를 이용해 iceberg 테이블 읽기]]></title>
            <link>https://velog.io/@kr_jkjung/PostgreSQL%EC%97%90%EC%84%9C-pganalytics%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%B4-iceberg-%ED%85%8C%EC%9D%B4%EB%B8%94-%EC%9D%BD%EA%B8%B0</link>
            <guid>https://velog.io/@kr_jkjung/PostgreSQL%EC%97%90%EC%84%9C-pganalytics%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%B4-iceberg-%ED%85%8C%EC%9D%B4%EB%B8%94-%EC%9D%BD%EA%B8%B0</guid>
            <pubDate>Tue, 07 Jan 2025 14:55:31 GMT</pubDate>
            <description><![CDATA[<h3 id="1-iceberg-table-데이터-조회">1. iceberg table 데이터 조회</h3>
<pre><code>[postgres@DESKTOP-OUB301V ~]$ aws s3 ls --profile iceberg_test s3://test-iceberg-parquet/iceberg-warehouse/default/sample_table/data/
2025-01-07 19:03:05        916 00000-0-121ba239-b336-4375-9b16-1a0d8030a8fe-0-00001.parquet
2025-01-07 19:22:36        909 00000-0-c67ceb11-8b9b-4100-8e3e-ad8db6d40cd6-0-00001.parquet
2025-01-07 19:03:05        923 00001-1-121ba239-b336-4375-9b16-1a0d8030a8fe-0-00001.parquet
2025-01-07 19:22:36        902 00001-1-c67ceb11-8b9b-4100-8e3e-ad8db6d40cd6-0-00001.parquet</code></pre><h3 id="2-foreign-wrapper--server-생성">2. Foreign wrapper &amp; server 생성</h3>
<pre><code>-- CREATE WRAPPER
s3test=#  CREATE FOREIGN DATA WRAPPER iceberg_wrapper
HANDLER iceberg_fdw_handler
VALIDATOR iceberg_fdw_validator;
CREATE FOREIGN DATA WRAPPER

-- CREATE SERVER
s3test=# CREATE SERVER iceberg_server
FOREIGN DATA WRAPPER iceberg_wrapper;
CREATE SERVER</code></pre><h3 id="3-user-mapping">3. user mapping</h3>
<pre><code>s3test=# CREATE USER MAPPING FOR current_user
SERVER iceberg_server
OPTIONS (
  type &#39;S3&#39;,
  key_id &#39;****&#39;, -- AWS Access Key ID
  secret &#39;****&#39;, -- AWS Secret Access Key
  region &#39;ap-northeast-2&#39;
);</code></pre><h3 id="4-foreign-table-생성">4. Foreign table 생성</h3>
<pre><code>-- CREATE FOREIGN TABLE
s3test=# CREATE FOREIGN TABLE iceberg_test ()
SERVER iceberg_server
OPTIONS (files &#39;s3://test-iceberg-parquet/iceberg-warehouse/default/sample_table&#39;);

-- SELECT FOREIGN TABLE
s3test=# select * from iceberg_test;
 id |  name   | timestamp
----+---------+------------
  4 | Dave    | 2023-01-04
  5 | Eve     | 2023-01-05
  1 | Alice   | 2023-01-01
  2 | Bob     | 2023-01-02
  3 | Charlie | 2023-01-03

 -- CTAS
 s3test=# CREATE TABLE icebergtb as select * From iceberg_test ;

s3test=# select * from icebergtb;
 id |  name   | timestamp
----+---------+------------
  4 | Dave    | 2023-01-04
  5 | Eve     | 2023-01-05
  1 | Alice   | 2023-01-01
  2 | Bob     | 2023-01-02
  3 | Charlie | 2023-01-03
(5 rows)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[데이터 레이크 vs 웨어하우스 vs 마트 – 데이터 저장소의 모든 것]]></title>
            <link>https://velog.io/@kr_jkjung/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%A0%88%EC%9D%B4%ED%81%AC-vs-%EC%9B%A8%EC%96%B4%ED%95%98%EC%9A%B0%EC%8A%A4-vs-%EB%A7%88%ED%8A%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%80%EC%9E%A5%EC%86%8C%EC%9D%98-%EB%AA%A8%EB%93%A0-%EA%B2%83</link>
            <guid>https://velog.io/@kr_jkjung/%EB%8D%B0%EC%9D%B4%ED%84%B0-%EB%A0%88%EC%9D%B4%ED%81%AC-vs-%EC%9B%A8%EC%96%B4%ED%95%98%EC%9A%B0%EC%8A%A4-vs-%EB%A7%88%ED%8A%B8-%EB%8D%B0%EC%9D%B4%ED%84%B0-%EC%A0%80%EC%9E%A5%EC%86%8C%EC%9D%98-%EB%AA%A8%EB%93%A0-%EA%B2%83</guid>
            <pubDate>Tue, 07 Jan 2025 12:20:27 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>chatgpt 답변 기반으로, 데이터 저장소 분류에 따른 비교</p>
</blockquote>
<h3 id="🔹-1-데이터-레이크-data-lake">🔹 1. 데이터 레이크 (Data Lake)</h3>
<p>🔸 개념
비정형, 반정형, 정형 데이터를 모두 저장하는 대규모 저장소야.
데이터를 <strong>가공하지 않은 원본 상태(raw data)</strong>로 저장해.
마치 <strong>호수(lake)</strong>처럼 다양한 형태의 데이터가 섞여 있는 구조라고 생각하면 돼.
S3, HDFS, Azure Data Lake 같은 기술이 사용돼.
🔸 특징
확장성: 대용량 데이터를 저렴한 비용으로 저장할 수 있어.
유연성: 스키마 없이 데이터를 저장하고, 나중에 필요할 때 처리해. (Schema-on-read 방식)
다양한 데이터: 로그 데이터, 이미지, 동영상, JSON 등 형태에 구애받지 않고 저장할 수 있어.
분산 처리: Spark, Hadoop 같은 분산 처리 시스템과 연동돼.
🔸 단점
데이터 혼잡성: 데이터가 가공되지 않고 쌓이기 때문에 <strong>데이터 늪(Data Swamp)</strong>이 될 위험이 있어.
느린 쿼리: 전체 데이터를 스캔해야 해서 분석 속도가 느릴 수 있어.
데이터 품질 관리 어려움: 원시 데이터가 많아서 일관성이 부족할 수 있어.
🔸 사용 사례
대규모 로그 데이터 수집, IoT 데이터 저장, 비디오/이미지 분석 등.
Netflix, Uber, AWS 등에서 데이터 레이크를 운영해.</p>
<h3 id="🔹-2-데이터-웨어하우스-data-warehouse">🔹 2. 데이터 웨어하우스 (Data Warehouse)</h3>
<p>🔸 개념
구조화된 데이터를 저장하는 분석용 데이터 저장소야.
데이터는 가공 및 정제된 후 저장돼.
기존 데이터베이스와 비슷하지만, 대규모 분석 쿼리에 특화되어 있어.
Snowflake, Redshift, BigQuery, Teradata 등이 대표적인 기술이야.
🔸 특징
스키마 고정: 데이터가 들어오기 전에 스키마가 정해져 있어. (Schema-on-write 방식)
빠른 쿼리 속도: 분석용으로 최적화돼 있어서 쿼리 속도가 빠르다.
정제된 데이터: 데이터가 정리되어 있어서 신뢰도가 높아.
비즈니스 분석 최적화: OLAP(Online Analytical Processing)에 특화되어 있어.
🔸 단점
비용이 비쌈: 대규모 데이터 저장 및 처리 비용이 크다.
유연성 부족: 새로운 데이터 유형을 다루기 어렵고, 스키마 변경이 제한적이야.
비정형 데이터 처리 어려움: 이미지, 영상 같은 비정형 데이터는 처리하기 힘들어.
🔸 사용 사례
비즈니스 인텔리전스(BI), 재무 분석, 리포팅, KPI 모니터링 등.
은행, 보험사, 대기업에서 많이 사용해.</p>
<h3 id="🔹-3-데이터-마트-data-mart">🔹 3. 데이터 마트 (Data Mart)</h3>
<p>🔸 개념
데이터 웨어하우스에서 특정 부서나 프로젝트에서 사용할 작은 단위의 데이터 저장소야.
데이터 웨어하우스가 전체 데이터를 저장한다면, 데이터 마트는 특정 주제에 맞게 가공된 데이터를 저장해.
🔸 특징
속도와 접근성: 특정 부서에서 필요한 데이터만 저장하니까 쿼리 속도가 빠르고 접근이 쉬워.
비용 절감: 전체 웨어하우스를 조회하는 것보다 비용이 적게 든다.
간편성: 특정 비즈니스 목표에 맞게 데이터를 쉽게 추출할 수 있어.
🔸 단점
데이터 일관성 문제: 각 부서별로 데이터 마트를 운영하다 보면 중복 데이터가 생길 수 있어.
범위 제한: 특정 부서나 프로젝트 중심으로 데이터를 저장하기 때문에 전체 데이터 흐름 파악이 어렵다.
🔸 사용 사례
마케팅, 영업, HR 등 부서별로 데이터 마트를 운영해.
예를 들어, 마케팅 부서에서는 고객 행동 데이터를 저장하고, 영업 부서는 판매 데이터를 따로 관리해.</p>
<h3 id="🔹-차이점-요약">🔹 차이점 요약</h3>
<table>
<thead>
<tr>
<th>항목</th>
<th>데이터 레이크</th>
<th>데이터 웨어하우스</th>
<th>데이터 마트</th>
</tr>
</thead>
<tbody><tr>
<td><strong>데이터 형식</strong></td>
<td>비정형, 반정형, 정형 데이터 모두</td>
<td>정형 데이터 중심</td>
<td>특정 주제의 정형 데이터</td>
</tr>
<tr>
<td><strong>스키마</strong></td>
<td>Schema-on-read (나중에 정의)</td>
<td>Schema-on-write (미리 정의)</td>
<td>Schema-on-write (미리 정의)</td>
</tr>
<tr>
<td><strong>처리 속도</strong></td>
<td>느림 (전체 스캔 필요)</td>
<td>빠름</td>
<td>매우 빠름</td>
</tr>
<tr>
<td><strong>비용</strong></td>
<td>낮음</td>
<td>높음</td>
<td>중간</td>
</tr>
<tr>
<td><strong>유연성</strong></td>
<td>높음 (모든 데이터 저장 가능)</td>
<td>낮음</td>
<td>중간</td>
</tr>
<tr>
<td><strong>데이터 처리</strong></td>
<td>원시 데이터 그대로 저장</td>
<td>정제 및 가공된 데이터 저장</td>
<td>정제된 데이터의 일부</td>
</tr>
<tr>
<td><strong>데이터 정합성</strong></td>
<td>낮음</td>
<td>높음 (일관성 유지)</td>
<td>높음</td>
</tr>
<tr>
<td><strong>사용 목적</strong></td>
<td>대규모 데이터 저장 및 머신러닝</td>
<td>비즈니스 분석 및 리포팅</td>
<td>특정 부서/프로젝트 데이터 분석</td>
</tr>
<tr>
<td><strong>사용 사례</strong></td>
<td>로그 데이터, IoT, 멀티미디어 데이터 저장</td>
<td>재무 분석, BI 리포팅</td>
<td>마케팅, 영업, HR 부서별 데이터 분석</td>
</tr>
</tbody></table>
<h3 id="🔹-언제-어떤-걸-써야-할까">🔹 언제 어떤 걸 써야 할까?</h3>
<p>데이터 레이크:
비정형 데이터를 많이 다루고, 머신러닝이나 대규모 데이터 수집이 필요한 경우.
데이터 웨어하우스:
비즈니스 인텔리전스(BI), 재무 분석, 리포팅 등 정형 데이터 중심의 분석이 필요한 경우.
데이터 마트:
특정 부서나 프로젝트에서 빠르게 데이터를 추출하고 분석하고 싶을 때.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[PostgreSQL에서 pg_analytics를 이용해 S3에 저장된 파일 읽기]]></title>
            <link>https://velog.io/@kr_jkjung/PostgreSQL%EC%97%90%EC%84%9C-pganalytics%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%B4-S3%EC%97%90-%EC%A0%80%EC%9E%A5%EB%90%9C-%ED%8C%8C%EC%9D%BC-%EC%9D%BD%EA%B8%B0</link>
            <guid>https://velog.io/@kr_jkjung/PostgreSQL%EC%97%90%EC%84%9C-pganalytics%EB%A5%BC-%EC%9D%B4%EC%9A%A9%ED%95%B4-S3%EC%97%90-%EC%A0%80%EC%9E%A5%EB%90%9C-%ED%8C%8C%EC%9D%BC-%EC%9D%BD%EA%B8%B0</guid>
            <pubDate>Mon, 06 Jan 2025 14:21:34 GMT</pubDate>
            <description><![CDATA[<ul>
<li>PostgreSQL은 직접적으로 S3 연결을 지원하지 않음</li>
<li>pg_analytics extension은 PostgreSQL에 DuckDB를 임베디드 형태로 통합하여 S3 파일을 외부 테이블로 바로 쿼리가 가능하게 지원</li>
</ul>
<h3 id="1-pg_analytics-개요">1. pg_analytics 개요</h3>
<p>pg_analytics는 PostgreSQL 확장으로, DuckDB의 강력한 OLAP(Online Analytical Processing) 기능을 PostgreSQL에 추가하는 방식이다. 
이를 통해 PostgreSQL에서 Parquet, CSV, JSON 파일을 직접 읽고 분석할 수 있다.
특히, pg_analytics는 duckdb를 이용하여 S3 버킷에 저장된 데이터에 직접 접근하여 PostgreSQL에서 외부 테이블로 불러오는 기능을 제공한다.</p>
<blockquote>
<p>pg_analytics : <a href="https://github.com/paradedb/pg_analytics">https://github.com/paradedb/pg_analytics</a>
duckdb : <a href="https://duckdb.org/docs/data/overview">https://duckdb.org/docs/data/overview</a></p>
</blockquote>
<h3 id="2-pg_analytics-설치">2. pg_analytics 설치</h3>
<blockquote>
<p>OS : Rocky linux9 on wsl
  DB : PostgreSQL 16.1.2</p>
</blockquote>
<h4 id="21-rpm-file-download">2.1 rpm file download</h4>
<ul>
<li>운영 환경에 맞는 버젼으로 다운로드</li>
<li><a href="https://github.com/paradedb/pg_analytics/releases/tag/v0.2.4">https://github.com/paradedb/pg_analytics/releases/tag/v0.2.4</a></li>
</ul>
<h4 id="22-rpm-install">2.2 rpm install</h4>
<pre><code>[root@DESKTOP-OUB301V postgresql]# dnf install ./pg_analytics_16-0.2.4-1PARADEDB.el9.x86_64.rpm</code></pre><h4 id="23-shared_preload_libraries-추가">2.3 shared_preload_libraries 추가</h4>
<pre><code>[postgres@DESKTOP-OUB301V ~]$ cat /var/lib/pgsql/16/data/postgresql.conf  |grep &#39;shared_preload_libraries&#39;
shared_preload_libraries = &#39;pg_analytics&#39;       # (change requires restart)

[postgres@DESKTOP-OUB301V ~]$ sudo systemctl restart postgresql-16
</code></pre><h4 id="24-postgresql에서-extension-활성화">2.4 PostgreSQL에서 extension 활성화</h4>
<ul>
<li>TESTDB  생성 후 extension 활성화<pre><code>postgres=# create database s3test;
CREATE DATABASE
postgres=# \c s3test
You are now connected to database &quot;s3test&quot; as user &quot;postgres&quot;.
s3test=# create extension pg_analytics ;
CREATE EXTENSION</code></pre></li>
</ul>
<h3 id="3-s3에-저장된-parquet-파일-읽기-테스트">3. S3에 저장된 parquet 파일 읽기 테스트</h3>
<ul>
<li>github 페이지내 절차 참고</li>
</ul>
<h4 id="31-wrapper--server-설정">3.1 wrapper &amp; server 설정</h4>
<pre><code>-- S3 parquet test 
CREATE FOREIGN DATA WRAPPER parquet_wrapper HANDLER parquet_fdw_handler VALIDATOR parquet_fdw_validator;

-- Provide S3 credentials
CREATE SERVER parquet_server FOREIGN DATA WRAPPER parquet_wrapper;
</code></pre><h4 id="32-foreign-table-생성">3.2 foreign table 생성</h4>
<ul>
<li>해당 파일은 public access가 허용되어 있어 바로 접근 가능</li>
</ul>
<pre><code>-- Create foreign table with auto schema creation
CREATE FOREIGN TABLE trips ()
SERVER parquet_server
OPTIONS (files &#39;s3://paradedb-benchmarks/yellow_tripdata_2024-01.parquet&#39;);
</code></pre><h4 id="33-테이블-조회">3.3 테이블 조회</h4>
<ul>
<li>건수가 적은편은 아니지만, 조회 성능이 좋지는 않은듯</li>
</ul>
<pre><code>s3test=# \d trips
                                    Foreign table &quot;public.trips&quot;
        Column         |            Type             | Collation | Nullable | Default | FDW options
-----------------------+-----------------------------+-----------+----------+---------+-------------
 vendorid              | integer                     |           |          |         |
 tpep_pickup_datetime  | timestamp without time zone |           |          |         |
 tpep_dropoff_datetime | timestamp without time zone |           |          |         |
 passenger_count       | bigint                      |           |          |         |
 trip_distance         | double precision            |           |          |         |
 ratecodeid            | bigint                      |           |          |         |
 store_and_fwd_flag    | character varying           |           |          |         |
 pulocationid          | integer                     |           |          |         |
 dolocationid          | integer                     |           |          |         |
 payment_type          | bigint                      |           |          |         |
 fare_amount           | double precision            |           |          |         |
 extra                 | double precision            |           |          |         |
 mta_tax               | double precision            |           |          |         |
 tip_amount            | double precision            |           |          |         |
 tolls_amount          | double precision            |           |          |         |
 improvement_surcharge | double precision            |           |          |         |
 total_amount          | double precision            |           |          |         |
 congestion_surcharge  | double precision            |           |          |         |
 airport_fee           | double precision            |           |          |         |
Server: parquet_server
FDW options: (files &#39;s3://paradedb-benchmarks/yellow_tripdata_2024-01.parquet&#39;)

-- Success! Now you can query the remote Parquet file like a regular Postgres table
SELECT COUNT(*) FROM trips;
  count
---------
 2964624
(1 row)

-- 조회 성능 확인
s3test=# SELECT count(*) FROM trips;
  count
---------
 2964624
(1 row)

Time: 2942.286 ms (00:02.942)

s3test=# SELECT * FROM trips limit 1;
 vendorid | tpep_pickup_datetime | tpep_dropoff_datetime | passenger_count | trip_distance | ratecodeid | store_and_fwd_flag | pulocationid | dolocationid |
 payment_type | fare_amount | extra | mta_tax | tip_amount | tolls_amount | improvement_surcharge | total_amount | congestion_surcharge | airport_fee
----------+----------------------+-----------------------+-----------------+---------------+------------+--------------------+--------------+--------------+
--------------+-------------+-------+---------+------------+--------------+-----------------------+--------------+----------------------+-------------
        2 | 2024-01-01 00:57:55  | 2024-01-01 01:17:43   |               1 |          1.72 |          1 | N                  |          186 |           79 |
            2 |        17.7 |     1 |     0.5 |          0 |            0 |                     1 |         22.7 |                  2.5 |           0
(1 row)

Time: 53570.829 ms (00:53.571)
</code></pre><h4 id="34-ctas">3.4 CTAS</h4>
<pre><code>s3test=# create table ctas_trips as select * from trips;
SELECT 2964624
Time: 33662.761 ms (00:33.663)

s3test=# select count(*) from ctas_trips ;
  count
---------
 2964624
(1 row)

Time: 447.115 ms</code></pre><h3 id="4-s3에-저장된-csv-파일-읽기-테스트">4. S3에 저장된 csv 파일 읽기 테스트</h3>
<h4 id="41-sample-scv-생성">4.1 sample scv 생성</h4>
<pre><code>s3test=# create table csvtest (name text, num int);
CREATE TABLE
s3test=# insert into csvtest values (&#39;jkjung&#39;,1);
INSERT 0 1
s3test=# insert into csvtest values (&#39;kilee&#39;,2);
INSERT 0 1
s3test=# insert into csvtest values (&#39;hmson&#39;,3);
INSERT 0 1
s3test=# copy csvtest to &#39;/home/postgres/csvtest.csv&#39;;
COPY 3</code></pre><h4 id="42-s3-upload">4.2 s3 upload</h4>
<pre><code>[postgres@DESKTOP-OUB301V ~]$ aws s3 cp csvtest.csv s3://test-parquet-jkjung
[postgres@DESKTOP-OUB301V ~]$ aws s3 ls s3://test-parquet-jkjung
2025-01-06 21:24:55         25 csvtest.csv</code></pre><h4 id="43-wrapper--server-생성">4.3 wrapper &amp; server 생성</h4>
<pre><code>CREATE FOREIGN DATA WRAPPER csv_wrapper
HANDLER csv_fdw_handler
VALIDATOR csv_fdw_validator;

CREATE SERVER csv_server
FOREIGN DATA WRAPPER csv_wrapper;</code></pre><h4 id="44-user-mapping">4.4 user mapping</h4>
<ul>
<li>내 S3는 public access를 차단했기에, user mapping을 통해 등록된 서버에 AWS IAM 키와 리전 정보를 매핑해주어야 함.<pre><code>CREATE USER MAPPING FOR postgres
SERVER csv_server
OPTIONS (
type &#39;S3&#39;,
key_id &#39;****&#39;,
secret &#39;****&#39;,
region &#39;ap-northeast-2&#39;
);</code></pre><h4 id="45-foreign-table-생성">4.5 foreign table 생성</h4>
<pre><code>CREATE FOREIGN TABLE s3csvtest ()
SERVER csv_server
OPTIONS (files &#39;s3://test-parquet-jkjung/csvtest.csv&#39;);</code></pre></li>
</ul>
<h4 id="46-테이블-조회">4.6 테이블 조회</h4>
<pre><code>s3test=# \d s3csvtest
                      Foreign table &quot;public.s3csvtest&quot;
 Column  |       Type        | Collation | Nullable | Default | FDW options
---------+-------------------+-----------+----------+---------+-------------
 column0 | character varying |           |          |         |
 column1 | bigint            |           |          |         |
Server: csv_server
FDW options: (files &#39;s3://test-parquet-jkjung/csvtest.csv&#39;)

s3test=# select * from s3csvtest ;
 column0 | column1
---------+---------
 jkjung  |       1
 kilee   |       2
 hmson   |       3
(3 rows)</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[WSL에서 AWS S3에 파일 업로드 및 다운로드 테스트하기]]></title>
            <link>https://velog.io/@kr_jkjung/WSL%EC%97%90%EC%84%9C-AWS-S3%EC%97%90-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C-%EB%B0%8F-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@kr_jkjung/WSL%EC%97%90%EC%84%9C-AWS-S3%EC%97%90-%ED%8C%8C%EC%9D%BC-%EC%97%85%EB%A1%9C%EB%93%9C-%EB%B0%8F-%EB%8B%A4%EC%9A%B4%EB%A1%9C%EB%93%9C-%ED%85%8C%EC%8A%A4%ED%8A%B8%ED%95%98%EA%B8%B0</guid>
            <pubDate>Sun, 05 Jan 2025 13:40:39 GMT</pubDate>
            <description><![CDATA[<ul>
<li>버킷 생성 및 IAM 계정 생성은 추후 업로드</li>
</ul>
<p>[wsl에서 버킷 업로드 테스트]</p>
<h3 id="1-awscli-설치">1. awscli 설치</h3>
<pre><code>[~]#  dnf install awscli</code></pre><h3 id="2-aws-s3-연결">2. aws s3 연결</h3>
<pre><code>[postgres@DESKTOP-OUB301V ~]$ aws configure
AWS Access Key ID [None]: ****
AWS Secret Access Key [None]: ****
Default region name [None]: ap-northeast-2
Default output format [None]: text

[postgres@DESKTOP-OUB301V ~]$ aws s3 ls
2025-01-05 22:05:41 test-parquet-jkjung</code></pre><h3 id="3-upload-test">3. upload test</h3>
<pre><code>[postgres@DESKTOP-OUB301V ~]$ echo &quot;Hello, S3 from wsl!&quot; &gt; testfile.txt
 [postgres@DESKTOP-OUB301V ~]$ aws s3 cp testfile.txt s3://test-parquet-jkjung
 upload: ./testfile.txt to s3://test-parquet-jkjung/testfile.txt</code></pre><ul>
<li>aws 콘솔에서 확인
<img src="https://velog.velcdn.com/images/kr_jkjung/post/3ee79960-2fb1-4d89-a950-ad3d0b5daefe/image.png" alt="">
<img src="https://velog.velcdn.com/images/kr_jkjung/post/c0ab6568-1b78-4272-acd2-b1039507fa98/image.png" alt=""></li>
</ul>
<h3 id="4-download-test">4. download test</h3>
<pre><code> [postgres@DESKTOP-OUB301V ~]$ aws s3 cp s3://test-parquet-jkjung/testfile.txt ./download_testfile.txt
 download: s3://test-parquet-jkjung/testfile.txt to ./download_testfile.txt
 [postgres@DESKTOP-OUB301V ~]$ cat download_testfile.txt
 Hello, S3 from wsl!</code></pre><h3 id="5-delete-test">5. delete test</h3>
<pre><code> [postgres@DESKTOP-OUB301V ~]$ aws s3 rm s3://test-parquet-jkjung/testfile.tx
t
delete: s3://test-parquet-jkjung/testfile.txt

[postgres@DESKTOP-OUB301V ~]$ aws s3 ls s3://test-parquet-jkjung
[postgres@DESKTOP-OUB301V ~]$</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[rocky linux에 PostgreSQL16 설치]]></title>
            <link>https://velog.io/@kr_jkjung/rocky-linux%EC%97%90-PostgreSQL16-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@kr_jkjung/rocky-linux%EC%97%90-PostgreSQL16-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Sun, 05 Jan 2025 09:27:35 GMT</pubDate>
            <description><![CDATA[<p>[rocky9 postgres on wsl]</p>
<blockquote>
<p>OS : Rocky linux 9.5 on wsl
DB : PostgreSQL 16.1.2</p>
</blockquote>
<h3 id="1-postgres-user-생성">1. postgres user 생성</h3>
<pre><code>useradd -G wheel postgres
[postgres@DESKTOP-OUB301V ~]$ sudo hostname
DESKTOP-OUB301V</code></pre><h3 id="2-설치-파일-준비">2. 설치 파일 준비</h3>
<p><a href="https://download.postgresql.org/pub/repos/yum/16/redhat/rhel-9-x86_64/">https://download.postgresql.org/pub/repos/yum/16/redhat/rhel-9-x86_64/</a></p>
<ul>
<li>wsl 경로로 파일 준비<pre><code>[root@DESKTOP-OUB301V postgresql]# cd /mnt/c/WSL/postgresql
[root@DESKTOP-OUB301V postgresql]# ls
postgresql16-16.1-2PGDG.rhel9.x86_64.rpm
postgresql16-server-16.1-2PGDG.rhel9.x86_64.rpm
postgresql16-contrib-16.1-2PGDG.rhel9.x86_64.rpm
postgresql16-devel-16.1-2PGDG.rhel9.x86_64.rpm
postgresql16-docs-16.1-2PGDG.rhel9.x86_64.rpm
postgresql16-libs-16.1-2PGDG.rhel9.x86_64.rpm</code></pre><h3 id="3-postgresql16-install">3. PostgreSQL16 install</h3>
설치 순서: libs → postgresql → server → contrib → devel → docs</li>
<li>devel의 경우 &#39;perl-IPC-Run&#39; 의존성 오류가 발생할 수 있음.</li>
<li>그럴 경우 &#39;CRB&#39; repo를 활성화 한후 설치 진행.</li>
<li><del>repo 활성화가 불가한 폐쇄망일 경우 힘들게 구해야함</del>
```
dnf install ./postgresql16-libs-16.1-2PGDG.rhel9.x86_64.rpm
dnf install ./postgresql16-16.1-2PGDG.rhel9.x86_64.rpm
dnf install ./postgresql16-server-16.1-2PGDG.rhel9.x86_64.rpm
dnf install ./postgresql16-contrib-16.1-2PGDG.rhel9.x86_64.rpm
dnf install postgresql16-devel-16.1-2PGDG.rhel9.x86_64.rpm</li>
<li><ul>
<li>dependency
dnf config-manager --set-enabled crb 
[root@DESKTOP-OUB301V data]# dnf repolist |grep CRB
crb                 Rocky Linux 9 - CRB</li>
</ul>
</li>
</ul>
<p>dnf install perl-IPC-Run
dnf install ./postgresql16-devel-16.1-2PGDG.rhel9.x86_64.rpm</p>
<pre><code>### 4. PostgreSQL initdb
#### 4.1 PostgreSQL DB 초기화
 /usr/pgsql-16/bin/postgresql-16-setup initdb
 * 오류 발생</code></pre><p>System has not been booted with systemd as init system (PID 1). Can&#39;t operate.
Failed to connect to bus: Host is down
failed to find PGDATA setting in postgresql-16.service</p>
<pre><code>-&gt; WSL은 systemd 대신 init 시스템을 사용해 부팅하기 때문에, systemctl 명령어가 제대로 작동 x
https://learn.microsoft.com/ko-kr/windows/wsl/systemd
- 해결방법
WSL에서 systemd를 활성화 한 후, OS reboot

</code></pre><h1 id="wsl에서-systemd-활성화">WSL에서 systemd 활성화</h1>
<p> vi /etc/wsl.conf
 [boot]
 systemd=true</p>
<h1 id="wsl-재시작">wsl 재시작</h1>
<p>PS C:\Users&gt; wsl --shutdown
PS C:\Users&gt; wsl -l -v
  NAME      STATE           VERSION</p>
<ul>
<li>Ubuntu    Stopped         2
 Rocky9    Stopped         2
PS C:\Users&gt; wsl -d Rocky9</li>
</ul>
<h1 id="initdb">initdb</h1>
<p> [root@DESKTOP-OUB301V Users]# /usr/pgsql-16/bin/postgresql-16-setup initdb
 Initializing database ... OK </p>
<pre><code>### 3. PostgreSQL DB 활성화</code></pre><p>[postgres@DESKTOP-OUB301V ~]$ sudo systemctl status postgresql-16
○ postgresql-16.service - PostgreSQL 16 database server
     Loaded: loaded (/usr/lib/systemd/system/postgresql-16.service; disable&gt;
     Active: inactive (dead)
       Docs: <a href="https://www.postgresql.org/docs/16/static/">https://www.postgresql.org/docs/16/static/</a>       </p>
<p>[postgres@DESKTOP-OUB301V 16]$ sudo systemctl start postgresql-16</p>
<pre><code>### 4. 접속 확인</code></pre><p>[postgres@DESKTOP-OUB301V 16]$ psql -c &#39;select version();&#39;
                                                 version</p>
<hr>
<hr>
<p> PostgreSQL 16.1 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 11.4.1 202306
05 (Red Hat 11.4.1-2), 64-bit
(1 row)</p>
<p>````</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[WSL에 Rocky linux 설치]]></title>
            <link>https://velog.io/@kr_jkjung/WSL%EC%97%90-Rocky-linux-%EC%84%A4%EC%B9%98</link>
            <guid>https://velog.io/@kr_jkjung/WSL%EC%97%90-Rocky-linux-%EC%84%A4%EC%B9%98</guid>
            <pubDate>Sun, 05 Jan 2025 09:24:36 GMT</pubDate>
            <description><![CDATA[<p><del>- redhat9 하려다 포기
   wsl을 위한 이미지파일 만드는게 생각보다 까다로움.</del></p>
<ul>
<li>호환 가능한 rocky9으로 설치
참고 : <a href="https://m.blog.naver.com/islove8587/223435396324">https://m.blog.naver.com/islove8587/223435396324</a></li>
</ul>
<h3 id="1-rocky9tar-준비">1. rocky9.tar 준비</h3>
<p> 1.1 설치 파일 준비
<a href="https://docs.rockylinux.org/guides/interoperability/import_rocky_to_wsl/">https://docs.rockylinux.org/guides/interoperability/import_rocky_to_wsl/</a></p>
<p> 1.2 7zip으로 압축풀기
 tar.xz -&gt; tar</p>
<h3 id="2-wsl-setting">2. wsl setting</h3>
<p> 2.1. WSL 설치 및 설정
 PowerShell 관리자 권한으로 실행</p>
<pre><code> wsl --install or
 wsl --update</code></pre><p> 2.2 WSL version 변경</p>
<pre><code> wsl --set-default-version 2</code></pre><p> 2.3 wsl list 확인</p>
<pre><code> wsl --list --verbose</code></pre><h3 id="3-rocky-linux-이미지-wsl에-등록">3. Rocky linux 이미지 WSL에 등록</h3>
<p> 3.1. 이미지 다운로드 경로로 이동</p>
<pre><code>PS C:\WINDOWS\system32&gt; cd C:\Users\jjk\Downloads\rocky_linux9
PS C:\Users\jjk\Downloads\rocky_linux9&gt; ls

    디렉터리: C:\Users\jjk\Downloads\rocky_linux9


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----      2025-01-05   오후 4:40      240936960 Rocky-9-Container-Base.latest.x86_64.tar
-a----      2025-01-05   오후 4:40       52991368 Rocky-9-Container-Base.latest.x86_64.xz</code></pre><p> 3.2 이미지 설치
  wsl --import $설치경로 $설치파일</p>
<pre><code>PS C:\Users\jjk\Downloads\rocky_linux9&gt; wsl --import Rocky9 C:\WSL\Rocky9 C:\Users\jjk\Downloads\rocky_linux9\Rocky-9-Container-Base.latest.x86_64.tar
가져오기가 진행 중입니다. 이 작업은 몇 분 정도 걸릴 수 있습니다.
작업을 완료했습니다.

PS C:\Users\jjk\Downloads\rocky_linux9&gt; wsl --list -v
  NAME      STATE           VERSION
* Ubuntu    Stopped         2
  Rocky9    Stopped         2</code></pre><h3 id="4-rocky9-실행">4. rocky9 실행</h3>
<pre><code>PS C:\Users&gt; wsl -d Rocky9
[root@DESKTOP-OUB301V Users]#</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[postgresql에서 NVL함수 사용하기]]></title>
            <link>https://velog.io/@kr_jkjung/postgresql%EC%97%90%EC%84%9C-NVL%ED%95%A8%EC%88%98-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</link>
            <guid>https://velog.io/@kr_jkjung/postgresql%EC%97%90%EC%84%9C-NVL%ED%95%A8%EC%88%98-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0</guid>
            <pubDate>Tue, 12 Dec 2023 12:06:44 GMT</pubDate>
            <description><![CDATA[<p>대부분의 DBMS는 ANSI SQL을 따르고 있기에, 동일 쿼리와 명령어를 통해 원하는 결과값을 얻어낼 수 있다.
그러나, 내장 함수와 같은 추가 기능에 있어서는 각 DBMS에서 사용하는 것에 차이가 존재한다.
대표적으로 Oracle에서 자주 사용되는 &#39;NVL&#39;함수가 그 예이다.</p>
<p>(1) NVL 함수
NVL은 null 값을 다른 값으로 대체할 때 주로 활용된다.
-실제 수행 예시</p>
<pre><code>-- 테이블 생성
CREATE TABLE employees (
    employee_id NUMBER PRIMARY KEY,
    employee_name VARCHAR2(50),
    salary NUMBER
);

-- 데이터 insert
INSERT INTO employees VALUES (1, &#39;John Doe&#39;, 50000);
INSERT INTO employees VALUES (2, &#39;Jane Smith&#39;, NULL);
INSERT INTO employees VALUES (3, &#39;Bob Johnson&#39;, 60000);

-- NVL 함수 활용
SELECT employee_id, employee_name, NVL(salary, 0) AS modified_salary
FROM employees;

| EMPLOYEE_ID | EMPLOYEE_NAME | MODIFIED_SALARY |
|-------------|---------------|------------------|
| 1           | John Doe      | 50000            |
| 2           | Jane Smith    | 0                |
| 3           | Bob Johnson   | 60000            |</code></pre><p>(2) 다른 DBMS에서 NVL 사용
여러 DBMS는 각자의 방식으로 NULL 대체를 위한 함수를 제공한다.
때문에, 본인이 사용하는 DBMS에 맞춰 적절한 용법으로 NULL 값을 대체해야 한다.</p>
<ul>
<li>SQL SERVER<pre><code>SELECT ISNULL(column_name, replacement_value) FROM table_name;
</code></pre></li>
</ul>
<pre><code>
- MYSQL, Postgresql</code></pre><p>SELECT COALESCE(column_name, replacement_value) FROM table_name;</p>
<pre><code>

(3) postgresql 에서 NVL 사용하기
다양한 DBMS를 접해본 사용자나, DBMS에 대해 어느정도 지식이 있는 사람들은 위와 같이 DBMS의 차이를 인지하고 적절한 함수를 대체하여 사용한다.
그러나, ORACLE만을 사용해본 사용자 혹은, Migration을 통해 어쩔 수 없이 타 DBMS로 넘어온 시스템의 경우 즉시 대체함수를 사용하기는 어려울 수 있다.
유능한 DBA라면 이러한 상황까지 고려해, 또 한가지의 대책안을 제시할 수 있어야 있다고 생각한다.

</code></pre>]]></description>
        </item>
        <item>
            <title><![CDATA[[GPDB] 특정 segment instance 임의로 stop ]]></title>
            <link>https://velog.io/@kr_jkjung/GPDB-%ED%8A%B9%EC%A0%95-segment-instance-%EC%9E%84%EC%9D%98%EB%A1%9C-stop</link>
            <guid>https://velog.io/@kr_jkjung/GPDB-%ED%8A%B9%EC%A0%95-segment-instance-%EC%9E%84%EC%9D%98%EB%A1%9C-stop</guid>
            <pubDate>Wed, 11 May 2022 08:57:35 GMT</pubDate>
            <description><![CDATA[<p>postgres 명령어 통해 특정 instance postgres DB stop</p>
<p>ex) pg_ctl stop -D /data/primary/gpseg4 -m fast</p>
<p>pg_ctl start 통해 DB 기동하나, 
mirror로 role이 변경된 경우 gprecoverseg 통하여 복구 필요</p>
]]></description>
        </item>
    </channel>
</rss>