<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>천천히 꾸준하게</title>
        <link>https://velog.io/</link>
        <description>TIL 남기는 공간입니다</description>
        <lastBuildDate>Wed, 24 Apr 2024 09:38:23 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <copyright>Copyright (C) 2019. 천천히 꾸준하게. All rights reserved.</copyright>
        <atom:link href="https://v2.velog.io/rss/kyukim" rel="self" type="application/rss+xml"/>
        <item>
            <title><![CDATA[Chapter 02. 명세 기반 테스트]]></title>
            <link>https://velog.io/@kyukim/Chapter-02.-%EB%AA%85%EC%84%B8-%EA%B8%B0%EB%B0%98-%ED%85%8C%EC%8A%A4%ED%8A%B8</link>
            <guid>https://velog.io/@kyukim/Chapter-02.-%EB%AA%85%EC%84%B8-%EA%B8%B0%EB%B0%98-%ED%85%8C%EC%8A%A4%ED%8A%B8</guid>
            <pubDate>Wed, 24 Apr 2024 09:38:23 GMT</pubDate>
            <description><![CDATA[<h1 id="chapter-02-명세-기반-테스트">Chapter 02. 명세 기반 테스트</h1>
<ul>
<li><strong>명세 기반 테스트</strong>란 애자일의 유저 스토리나 UML의 유스 케이스 같은 프로그램 요구사항을 테스트의 입력으로 사용</li>
<li>요구사항은 무엇을 수행하고, 무엇을 수행하면 안되는지 명확하기 때문에 테스트 적용시 가장 우선적으로 적용</li>
<li>요구사항에 따라 개발 진행 후엔 버그를 잡기 위한 &quot;<strong>테스트 모드</strong>&quot;로 몰입하자<ul>
<li>달성하고자하는 일련의 로직 절차를 구현하는 중에 생각나는 테스트 케이스는 일단 체크 리스트에 넣어두고, 나중에 &quot;테스트 모드&quot; 때 버그를 잡자</li>
</ul>
</li>
<li>완벽한 테스트는 거의 불가능. 실용적으로 접근하자</li>
</ul>
<h2 id="21-요구사항이-모든-것을-말한다">2.1 요구사항이 모든 것을 말한다</h2>
<h3 id="211-1단계-요구사항과-입출력에-대해-이해하기">2.1.1 1단계: 요구사항과 입출력에 대해 이해하기</h3>
<ul>
<li>요구사항은 어떻게 작성했든 세부분으로 이루어짐<ol>
<li>비즈니스 규칙. 즉 함수가 무엇을 수행해야하는지</li>
<li>입력. 무엇을 입력해야하는지</li>
<li>출력. 입력에 의해 무엇이 출력이되어야하는지</li>
</ol>
</li>
<li>위 부분들을 생각하는 과정은 함수가 어떻게 동작해야할지 생각할 때 유용</li>
</ul>
<h3 id="212-2단계-여러-입력값에-대해-프로그램이-수행하는-바를-탐색하기">2.1.2 2단계: 여러 입력값에 대해 프로그램이 수행하는 바를 탐색하기</h3>
<ul>
<li>느낌가는대로 여러 입력값으로 테스트 케이스 작성</li>
<li>이 테스트 케이스들을 통해 코드를 더 잘 알게 됐다면 Good!</li>
<li>필자의 경우 머릿속에 프로그램이 어떻게 동작할지 명확한 그림을 가지게 돼면 탐색 단계 중단</li>
<li>코너 케이스를 도출해내는 것보다 프로그램을 더 잘 이해한다는 것에 초점</li>
</ul>
<h3 id="213-3단계-테스트-가능한-입출력과-구획을-탐색하기">2.1.3 3단계: 테스트 가능한 입출력과 구획을 탐색하기</h3>
<ul>
<li>입력값을 달리해도 출력값이 같은 <strong>동등한 테스트</strong>는 한번만 하자.</li>
<li>입력값을 달리하면서 케이스를 작성하다보면 어떤 구획(부류)가 생긴다.<ul>
<li>이런 구획을 심도있게 탐색하다보면 이전에 보지못한 입력케이스를 찾을 수 있음</li>
</ul>
</li>
</ul>
<h3 id="214-4단계-경계-분석하기">2.1.4 4단계: 경계 분석하기</h3>
<ul>
<li>시스템의 버그는 <strong>경계</strong>에서 가장 많이 발생</li>
<li>경계를 찾으면 경계 근처를 옮겨가며 어떤 일이 발생하는지 테스트</li>
</ul>
<h3 id="5단계-6단계">5단계, 6단계</h3>
<ul>
<li>생략</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[비블록(non-block) 코드 만들기]]></title>
            <link>https://velog.io/@kyukim/non-block</link>
            <guid>https://velog.io/@kyukim/non-block</guid>
            <pubDate>Sat, 17 Jun 2023 01:29:49 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>모던 자바 인 액션 읽고 정리 한 문서입니다.</p>
</blockquote>
<h1 id="163-비블록non-block-코드-만들기">16.3 비블록(non-block) 코드 만들기</h1>
<p>요약: 스트림 -&gt; 병렬 스트림 -&gt; CompletableFuture 활용하여 병렬 작업을 개선할 수 있다.</p>
<p>예제코드: <a href="https://github.com/kyupid/java-test/tree/main/modern-chapter16/src/main/java/org/example">https://github.com/kyupid/java-test/tree/main/modern-chapter16/src/main/java/org/example</a></p>
<h2 id="테스트-코드">테스트 코드</h2>
<pre><code class="language-java">    public static void test(List&lt;Shop&gt; shops) {
        long start = System.nanoTime();
        System.out.println(findPricesV1(shops, &quot;myPhone27S&quot;));
        long duration = (System.nanoTime() - start) / 1_000_000;
        System.out.println(&quot;Done in &quot; + duration + &quot; msecs&quot;);
    }</code></pre>
<h2 id="스트림-버전">스트림 버전</h2>
<p>shop.getPrice 는 임의로 price 를 찾을때마다 1초정도 지연시켰다는 것을 인지하고 아래를 보자.</p>
<pre><code class="language-java">    private static List&lt;String&gt; findPrices(List&lt;Shop&gt; shops, String product) {
        return shops.stream()
                .map(shop -&gt; String.format(&quot;%s price is %.2f&quot;, shop.getName(), shop.getPrice(product)))
                .collect(Collectors.toList());
    }

</code></pre>
<pre><code>결과

[BestPrice price is 203.01, LetsSaveBig price is 188.42, MyFavoriteShop price is 203.86, BuyItAll price is 126.32]
Done in 4048 msecs</code></pre><p>순차적으로 실행했기 때문에 하나에 1초 총 4개를 돌렸으므로 약 4초정도의 결과가 나왔다.</p>
<h2 id="병렬-스트림-버전">병렬 스트림 버전</h2>
<pre><code class="language-java">    private static List&lt;String&gt; findPrices(List&lt;Shop&gt; shops, String product) {
        return shops.parallelStream()
                .map(shop -&gt; String.format(&quot;%s price is %.2f&quot;, shop.getName(), shop.getPrice(product)))
                .collect(Collectors.toList());
    }</code></pre>
<pre><code>결과

[BestPrice price is 218.33, LetsSaveBig price is 160.23, MyFavoriteShop price is 220.10, BuyItAll price is 193.95]
Done in 1053 msecs</code></pre><p>병렬로 실행해서 1초의 결과가 나와서 개선되었다. 이를 더 개선해보자.</p>
<h2 id="completablefuture-버전">CompletableFuture 버전</h2>
<pre><code class="language-java">    private static List&lt;String&gt; findPrices(List&lt;Shop&gt; shops, String product) {
        List&lt;CompletableFuture&lt;String&gt;&gt; priceFutures = shops.stream()
                .map(shop -&gt; CompletableFuture.supplyAsync(() -&gt; String.format(&quot;%s price is %.2f&quot;, shop.getName(), shop.getPrice(product))))
                .collect(Collectors.toList());

        return priceFutures.stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList());
    }</code></pre>
<pre><code>결과

[BestPrice price is 172.50, LetsSaveBig price is 193.90, MyFavoriteShop price is 142.92, BuyItAll price is 152.91]
Done in 2069 msecs</code></pre><p>2초 라는 결과가 나왔지만, Shop 이 많아질수록 CompletableFuture 의 효과는 커진다. 왜 그럴까?</p>
<p>CompletableFuture.supplyAsync() 메소드는 ForkJoinPool.commonPool()을 사용하는데, 이는 JVM에 의해 관리되며 기본적으로 시스템의 코어 수에 따라 스레드 수가 결정된다.</p>
<p>따라서, 스레드 풀 크기와 CPU 코어의 수, 그리고 JVM에 의해 어떻게 스케줄링되는지에 따라 결과가 다르게 나타날 수 있다.</p>
<p>그런데 알아두어야할 점은 스레드 수를 증가시키는 것이 무조건적으로 성능 향상을 의미하진 않는다. 스레드를 관리하고 컨텍스트 스위칭을 하는데는 비용이 들기 때문에 너무 많은 수의 스레드는 오히려 성능을 저하시킬 수 있다. 따라서 병렬 처리를 위한 최적의 스레드 수를 결정하는 것이 중요한 고려사항이다.</p>
<h2 id="completablefuture-개선-위한-커스텀-executor-사용">CompletableFuture 개선 위한 커스텀 Executor 사용</h2>
<p>스레드 풀 크기를 조절하는 것에는 &quot;자바 병렬 프로그래밍&quot;에 최적값을 찾는 방법이 알려져있는 방법인것같다.</p>
<p><code>N_threads = N_cpu * U_cpu * (1 + W/C)</code></p>
<ul>
<li>N_threads: 최적의 스레드 풀 크기</li>
<li>N_cpu: CPU 코어의 수,</li>
<li>U_cpu: CPU 사용률(0 &lt;= U_cpu &lt;= 1),</li>
<li>W/C: 대기 시간과 계산 시간의 비율</li>
</ul>
<p>예를 들어, Shop 의 응답을 대략 99% 시간만큼 기다리미르 W/C 비율을 100으로 간주한다.<br>N_cpu가 4라면, 400스레드를 갖는 풀을 만들어야함을 의미한다.<br>하지만 상점 수보다 많은 스레드를 가지는것이 의미가없으므로 최대 개수를 100 이하로 설정하는것이 바람직하다. (책에 의하면)</p>
<pre><code class="language-java">    private static List&lt;String&gt; findPricesV4(List&lt;Shop&gt; shops, String product) {
        final Executor executor = Executors.newFixedThreadPool(Math.min(shops.size(), 100), new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread t = new Thread(r);
                t.setDaemon(true); // 프로그램 종료를 방해하지 않는 데몬 스레드를 사용한다.
                return t;
            }
        });

        List&lt;CompletableFuture&lt;String&gt;&gt; priceFutures = shops.stream()
                .map(shop -&gt; CompletableFuture.supplyAsync(() -&gt; shop.getName() + &quot; price is &quot; + shop.getPrice(product), executor))
                .collect(Collectors.toList());
        return priceFutures.stream()
                .map(CompletableFuture::join)
                .collect(Collectors.toList());
    }</code></pre>
<pre><code>결과

[BestPrice price is 196.50406616365115, LetsSaveBig price is 184.24541442317607, MyFavoriteShop price is 161.91241143763202, BuyItAll price is 229.4755925617906]
Done in 1058 msecs</code></pre><p>5개 상점을 검색할땐 1021 밀리초, 9개 상점을 검색할땐 1022 밀리초가 소요된다. 이전에 계산한거처럼 400개의 상점까지 같은 성능을 유지할 수 있다.<br>결국 어플리케이션 특성에 맞는 Executor 를 만들어 CompletableFuture 를 활용하는것이 가장 바람직하다는 사실을 확인할 수 있다.<br>비동기 동작을 많이 사용하는 상황이라면 지금 기법이 가장 효과적이라는것을 기억하자.   </p>
]]></description>
        </item>
        <item>
            <title><![CDATA[11. 오라클 백그라운드 프로세스]]></title>
            <link>https://velog.io/@kyukim/oracle11</link>
            <guid>https://velog.io/@kyukim/oracle11</guid>
            <pubDate>Tue, 18 Apr 2023 23:03:37 GMT</pubDate>
            <description><![CDATA[<h2 id="백그라운드-프로세스-왜-배워야하는가">백그라운드 프로세스 왜 배워야하는가?</h2>
<ul>
<li>장애가 발생하면 백그라운드 프로세스의 동작과 관련된 지식이 필요할 때가 많다.</li>
</ul>
<h2 id="백그라운드-프로세스와-서버-프로세스의-관계">백그라운드 프로세스와 서버 프로세스의 관계</h2>
<ul>
<li>서로 바쁘면 잠들고 하던거 끝나고 요청할때 깨운다.</li>
<li>CPU가 1,2개인데 프로세스가 수십에서 수백까지 활성화되어 있는 경우가 많다<ul>
<li>왜 이게 가능? 대부분 슬립상태라서 CPU 자원을 소모하지 않기 때문이다.</li>
</ul>
</li>
<li>백그라운드 프로세스 중 몇가지는 정기적으로 어떤 처리를 위해 정기적으로 wake-up 한다.</li>
</ul>
<h2 id="dbwr의-동작과-역할">DBWR의 동작과 역할</h2>
<h3 id="복습해봅시다">복습해봅시다.</h3>
<ul>
<li>DBWR: 변경된 데이터를 캐시에서 디스크로 기록하는 역할</li>
<li>LGWR: 백그라운드 프로세스가 커밋한 시점의 REDO 로그를 디스크에 기록하는 역할.<ul>
<li>혹시 DBWR 이 기록못하는 경우가 있더라도 LGWR 가 백업해주는 느낌.</li>
</ul>
</li>
<li>ARCH: 아카이브 REDO 로그 파일에 기록.</li>
</ul>
<h3 id="어떤-식으로-io를-하는-것인가">어떤 식으로 I/O를 하는 것인가?</h3>
<ul>
<li><p>동기 비동기 방식의 I/O 를 지원하는것 같다.</p>
</li>
<li><p>비동기 I/O 라고 할지라도 부하가 너무 커지면 SQL 을 처리하기위해 데이터 읽어오는 속도가 느려질 수 있어 디스크에 너무 많은 부하를 주어서는 안된다</p>
<ul>
<li><p>DBWR 는 부하가 많을 때 긴 시간에 걸쳐 가능한한 부하를 균일하게 줄 수있도록한다.</p>
<ul>
<li>응답 속도 시간을 위함이다.</li>
</ul>
<p>왜?</p>
</li>
</ul>
</li>
</ul>
<h3 id="dbwr의-수는-왜-장비마다-다른-것일까">DBWR의 수는 왜 장비마다 다른 것일까?</h3>
<ul>
<li>비동기 IO 를 사용할 수 없는 환경(?)에서 효과적</li>
<li>성능 향상</li>
</ul>
<h3 id="어떤-때-dbwr에-장애가-발생하는가">어떤 때 DBWR에 장애가 발생하는가?</h3>
<ul>
<li>IO의 hang이나 지연</li>
<li>DBWR이 캐시-&gt;디스크 기록 지연<ul>
<li>서버 프로스세가 사용할 수 있는 빈 버퍼를 확보하지 못함 -&gt; free buffer waits 대기 이벤트<ul>
<li>또는 write complete waits, buffer busy waits</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="lgwr의-동작과-역할">LGWR의 동작과 역할</h2>
<h3 id="언제-io-하는가">언제 I/O 하는가?</h3>
<ul>
<li>일반적으로는 rdbms ipc message 상태 (슬립상태)</li>
<li>커밋할때 REDO 로그 기록</li>
<li>3초에 한번 rdbms ipc message 에서 깨어나 로그 버퍼의 데이터를 기록<ul>
<li>기록할떄 log file prallel write 상태로 대기 (병렬로 기록)</li>
</ul>
</li>
<li>서버 프로세스가 LGWR 기록하는 것을 기다릴때 log file sync 대기 이벤트</li>
</ul>
<h3 id="어떤때-lgwr에-장애가-발생">어떤때 LGWR에 장애가 발생?</h3>
<ul>
<li>성능부족, REDO 로그 생성량 많을때, 아카이브 받는 디스크가 가득찼을때, 로그버퍼 용량이 작을때<ul>
<li>가장흔한건 디스크가 가득찼을때</li>
</ul>
</li>
</ul>
<h2 id="다른-백그라운드-프로세스들">다른 백그라운드 프로세스들</h2>
<h3 id="smon">SMON</h3>
<ul>
<li>System MONitor</li>
<li>공간 전문 청소 업체</li>
<li>테이블스페이스 안의 빈공간을 합친다거나, 처리도중 종료된 트랜잭션을 롤백한다거나, 임시 세그먼트를 청소</li>
<li>평소엔 smon timer 대기이벤트</li>
<li>디비 비정상 종료되었을때 재기동하면 인스턴스 복구라는 처리를 함<ul>
<li>REDO 로그파일을 사용한다는데 LGWR 랑 다른점은?</li>
</ul>
</li>
</ul>
<h3 id="pmon">PMON</h3>
<ul>
<li>Process MONitor</li>
<li>메모리와 프로세스 전문 청소 업체</li>
<li>서버 프로세스 비정상 종료했을때 메모리나 프로세스 정리</li>
<li>정리딘 프로세스가 내부 Lock 이나 메모리를 보유하고 있으면 그것을 해제</li>
<li>pmon timer 대기 이벤트<ul>
<li>필요한 경우 청소 약 1분에 한번 수행</li>
</ul>
</li>
<li>pmon 자체가 비정상종료하면 인스턴스가 정지됨</li>
</ul>
<h3 id="lpeg">LPEG</h3>
<ul>
<li>Listener REGistration</li>
<li>인스턴스의 정보, 현재프롯스의 수, 인스턴스의 부하를 리스너에게 등록하는 작업</li>
<li>if oracle &lt;= 11g PMON<ul>
<li>else if oracle &gt;= 12c LPEG</li>
</ul>
</li>
<li>listener.ora 파일에 인스턴스의 정보를 등록하지 않더라도 리스너는 인스턴스의 정보를 알고있는 경우가있는데 LPEG가 리스너에 정보를 등록했기 때문</li>
<li>1분마다 인스턴스정보, 현재프로세스수,부하등의 정보를 1분마다 한번씩 리스너 프로세스에게 알려줌</li>
</ul>
<h3 id="arch">ARCH</h3>
<ul>
<li>ARCHiver</li>
<li>REDO 로그 파일을 아카이브하는 프로세스</li>
<li>아카이브 로그 모드 (REDO 로그를 아카이브해서 남기는 모드)일땐 아카이브 REDO 로그가 생성될떄까지 해당 REDO 로그파일을 다시 사용할수없음<ul>
<li>따라서 ARCH 프로세스가 중지되면 디비에 큰 문제가 발생할 수 있음</li>
</ul>
</li>
<li>평소엔 rdbms ipc message 대기 이벤트</li>
<li>ARCH 프로ㅔ스는 노-아카이브 로그 모드에서는 생성도지ㅣ 않음</li>
<li>디비 복제/동기화를 말하는 데이터 가드 기능을 사용할떄 ARCH 프로세스 사용</li>
</ul>
<h3 id="그외-백그라운드-프로세스">그외 백그라운드 프로세스</h3>
<ul>
<li>214p 표</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[9. 오라클 REDO와 UNDO의 동작]]></title>
            <link>https://velog.io/@kyukim/oracle9</link>
            <guid>https://velog.io/@kyukim/oracle9</guid>
            <pubDate>Tue, 04 Apr 2023 23:39:10 GMT</pubDate>
            <description><![CDATA[<h1 id="9-오라클-redo와-undo의-동작">9. 오라클 REDO와 UNDO의 동작</h1>
<ul>
<li>복구를 위한 기초지식</li>
<li>데이터의 보증 메커니즘</li>
<li>읽기 일관성</li>
</ul>
<h2 id="왜-배워야하는가">왜 배워야하는가?</h2>
<ul>
<li>트랜잭션 특성 ACID 가 있는데 이를 구현하기 위해선 REDO와 UNDO가 빠질 수 없다.<ul>
<li>Atomicity(원자성): all or nothing<ul>
<li>장비가 꺼지더라도 복구 가능하여야함</li>
<li>장비가 꺼져도 복구 가능하다는 건 트랜잭션 중간에 종료된 것은 롤백되어야 할것임</li>
</ul>
</li>
<li>Consistency(일관성)<ul>
<li>데이터가 A 가 변경되었는데 A 를 포함한 통계에 적용이 안된다는 둥.</li>
</ul>
</li>
<li>Isolation(고립성): 트랜잭션끼리는 독립적<ul>
<li>고립의 레벨 설정: <a href="https://code-lab1.tistory.com/52">MySQL 격리 수준 설정</a></li>
<li>고립 레벨을 어떻게 가져가냐에 따라 I/O 성능을 좌우할 수 있어 어플리케이션 성능과 데이터 정합성에 직접적으로 영향을 끼치기 때문에 가장 중요하지않을까 싶음</li>
</ul>
</li>
<li>Durability(지속성)<ul>
<li>커밋한 데이터는 지키는 특성</li>
<li>오라클 경우에 메모리에서 DBWR 가 쓰지 못한것은 REDO 로그에 남아 있어야한다는 것을 의미?</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="지속성-구현">지속성 구현</h2>
<ul>
<li>지속성 구현을 위해 REDO 로그 이용</li>
</ul>
<p>Q) 그림 9.4 왜 회전 대기시간이 한 번이지? 어쨌든 추가,변경,삭제를 위해서 실제 데이터 위치로 가기위해 세번돌아야하는 게 아닌지..</p>
<h2 id="redo-와-undo-의-개념">REDO 와 UNDO 의 개념</h2>
<ul>
<li>누군가 무엇을 했다를 기록한 게 REDO</li>
<li>어떻게 하면 과거의 상태로 돌아갈 수 있는지 기록한 게 UNDO</li>
</ul>
<h3 id="redo-의-구조">REDO 의 구조</h3>
<ul>
<li>데이터 변경이 버퍼 캐시에 일어날떄 REDO와 UNDO 를 서버 프로세스가 생성함</li>
<li>커밋이 발생하기전에 디스크에 기록하는 방식</li>
<li>이역시 바로 디스크에 기록하는 것은 아니고 REDO 로그 버퍼가 공유 메모리에 존재</li>
<li>디스크 기록은 LGWR 라는 프로세스가 수행</li>
<li>REDO 로그 파일 - 일시적 보관, 아카이브 REDO 로그 파일 - 오래 보관</li>
<li>REDO 로그 파일은 반드시 다중화 필요</li>
</ul>
<h3 id="undo-의-구조">UNDO 의 구조</h3>
<ul>
<li>롤백 세그먼트라고도 함</li>
<li>세그먼트이므로 테이블스페이스들 중 어딘가에 보관<ul>
<li>지난 공부 <a href="https://velog.io/@kyukim/oracle7">https://velog.io/@kyukim/oracle7</a></li>
</ul>
</li>
<li>링 버퍼라고 함</li>
<li>한 트랜잭션이 한 UNDO 세그먼트 사용하도록 동작하고 필요할때 세그먼트 수를 늘림</li>
<li>링버 퍼라고하는데 가장 오래된 UNDO 정보가 덮어쓰고하는 구조</li>
</ul>
<p>그림 9.9</p>
<h2 id="여러-상황에서-redo-와-undo-동작">여러 상황에서 REDO 와 UNDO 동작</h2>
<h3 id="롤백할때의-동작">롤백할때의 동작</h3>
<ul>
<li>롤백이 수행될때 UNDO 를 이용하여 데이터를 원래값으로 되돌림</li>
<li>서버프로세스가 비정상적으로 종료하면 PMON 백그라운드 프로세스가 정기적으로 체크 및 종료된 서버 프로세스 정리</li>
<li>SMON 백그라운드 프로세스가 트랜잭션 시작전 상태로 되돌림</li>
</ul>
<h3 id="읽기-일관성에-동반되는-동작">읽기 일관성에 동반되는 동작</h3>
<ul>
<li>예를 들어 검색을 해서 한 10초 걸린다면 10초 동안에 검색되는 데이터는 검색을 시작한 시점의 데이터를 보여주도록 하는 것이 읽기 일관성<ul>
<li>이 때 UNDO 를 사용</li>
</ul>
</li>
</ul>
<h3 id="커밋되지-않은-데이터를-읽어올-떄의-동작">커밋되지 않은 데이터를 읽어올 떄의 동작</h3>
<ul>
<li>오라클은 고립성 지키기 위해 레벨을 read committed 로 하도록 설정</li>
</ul>
<h3 id="ora-1555">ORA-1555</h3>
<ul>
<li>스냅샷이 너무 오래되었습니다(Snapshot too old)<ul>
<li>과거의 데이터를 보려 했으나 필요한 정보가 이미 없어졌다</li>
</ul>
</li>
<li>흠...</li>
</ul>
<h3 id="체크포인트의-동작">체크포인트의 동작</h3>
<ul>
<li>DBWR 이 디스크에 기록할 때를 말함 </li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[7. 오라클의 데이터 구조]]></title>
            <link>https://velog.io/@kyukim/oracle7</link>
            <guid>https://velog.io/@kyukim/oracle7</guid>
            <pubDate>Tue, 21 Mar 2023 23:11:54 GMT</pubDate>
            <description><![CDATA[<h1 id="오라클의-데이터-구조">오라클의 데이터 구조</h1>
<p>저자왈: 데이터 구조는 복잡하지만 이해할 때까지 몇번이든 반복해서 학습하세요.</p>
<ul>
<li>테이블스페이스(tablespace)</li>
<li>세그먼트(segment)</li>
<li>익스텐트(extent)</li>
<li>블록(block)</li>
<li>데이터 파일(datafile)</li>
</ul>
<h2 id="가변-길이-데이터를-관리할-프로그램을-만들기-위헤선">가변 길이 데이터를 관리할 프로그램을 만들기 위헤선?</h2>
<p>질문:<br>여러 테이블의 데이터를 파일로 관리하기 위한 프로그램을 처음부터 만든다고 가정합시다.<br>해당 프로그램의 구조를 떠올려보세요.<br>관리할 데이터는 가변 길이이며, 길어지는 경우도 있지만 짧아지는 경우도 있고, 삭제하거나 생성하는 경우도 있다고 하겠습니다</p>
<h3 id="데이터를-차례대로-넣는다">데이터를 차례대로 넣는다?</h3>
<p>파일의 시작부터 a,b,c,d, .... 파일의 끝까지 차례대로 넣는다면?<br>데이터를 변경할 때 곤란할 수. 있다.<br>중간에 e 를 삽입한다고 해보자.<br>뒤에 데이터가 존재해서 힘들다.<br>동시에 처리할 수 도 없다.<br>데이터가 100만건이라면 100만건의 I/O 가 발생할 수도 있다.<br>사용하지않는 공간 관리도 힘들다</p>
<p><img src="https://velog.velcdn.com/images/kyukim/post/2caac7e0-ae87-449a-911e-3d71988e39fc/image.jpeg" alt=""></p>
<h3 id="어떻게-해야할까">어떻게 해야할까?</h3>
<p>완전하진 않지만 적당한 크기로 정리해서 접근 방식이라면 통할 수도 있다.<br>예를들면, 100만건을 적당히 1만건을 한개의 집합으로 관리하면 최대 100번의 I/O만 생기기때문이다.</p>
<p><img src="https://velog.velcdn.com/images/kyukim/post/478b9c1f-0d89-433b-bb31-ba7f8b06844d/image.jpeg" alt=""></p>
<h3 id="정리">정리</h3>
<ul>
<li>관리 및 I/O 효율을 고려해 공간을  어느정도 크기로 뭉쳐서 할당한다</li>
<li>데이터 변경에 필요한 공간을 확보한다</li>
<li>비어있는 공간을 관리한다.</li>
</ul>
<h2 id="오라클의-데이터-구조-1">오라클의 데이터 구조</h2>
<ul>
<li>테이블스페이스(tablespace)</li>
<li>세그먼트(segment)</li>
<li>익스텐트(extent)</li>
<li>블록(block)</li>
<li>데이터 파일(datafile)</li>
</ul>
<p>크게 물리구조와 논리구조로 나뉜다.
물리구조 - 데이터 파일 등 OS 에서 보이는 구조
논리구조 - OS에서 식별할 수 없는 오라클 제품 내부 구조, ex) 데이터 파일 아넹 보관된 테이블이나 로우는 논리구조</p>
<p>책에서 이해하기 쉬운 비유를 하나 보고 가자.</p>
<blockquote>
<p>집안에 책(데이터)이 늘어나서 창고(데이터베이스)에 맡긴다고 가정하자.</p>
<p>책을 창고에 마구잡이식으로 쌓아두면 관리하기가 어려우므로 관리하기 편하게 상자(블록)에 집어넣습니다.<br>고객마다 상자를 한 곳(또는 선반)에 정리해서 모아둡니다.<br>이런 선반 하나가 익스텐트를 의미하며, 한 고객이 맡긴 물건 전부가 세그먼트입니다.  </p>
</blockquote>
<p>즉, 아래와 같은 그림이 될 수 있다.</p>
<p><img src="https://velog.velcdn.com/images/kyukim/post/fde82c57-5bbf-4a6e-8811-546bba264631/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kyukim/post/1cbff2c5-34ef-41f1-9d29-74b0956d2587/image.jpeg" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kyukim/post/a50a8bfc-2d49-4bb7-85b6-636fb73b8b7a/image.jpeg" alt=""></p>
<h3 id="데이터-구조에는-어떤-것들이-있는가">데이터 구조에는 어떤 것들이 있는가?</h3>
<h4 id="세그먼트">세그먼트</h4>
<p>세그먼트는 사용자가 조작이 가능한 테이블이나 인덱스가 있고,
오라클에서 자동으로 만드는 데이터 정렬 세그먼트나 UNDO라고 불리는 과거 데이터를 보관하는 세그먼트도 있다.</p>
<p><img src="https://velog.velcdn.com/images/kyukim/post/be354b15-f505-406e-8574-ce47fc9415ed/image.jpg" alt=""></p>
<h4 id="테이블스페이스">테이블스페이스</h4>
<p>한 개 이상의 데이터 파일로 구성.<br>테이블스페이스는 오라클이 디비를 관리하기위해 사용하는 테이블스페이스와 사용자가 사용하는 테이블스페이스(mysql에서 database) 등 몇가지 종류가 존재.  </p>
<h4 id="블록-안의-공간">블록 안의 공간</h4>
<p>오라클은 블록 안에 데이터 변경에 대비한 공간을 남겨둔다.
다시 읽어봐야함. 125p</p>
<h4 id="rowid">ROWID</h4>
<p>데이터 로우 주소를 ROWID 라고함.<br>그냥 연속된 숫자가 아니라 데이터파일번호나 데이터파일안의 블록 번호, 블록 안에 로우번호와 같은 정보로 구성되어있다.
<img src="https://velog.velcdn.com/images/kyukim/post/608283de-add9-4288-88c1-874ffec4f22a/image.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[6. 커넥션과 서버 프로세스의 생성]]></title>
            <link>https://velog.io/@kyukim/oracle6</link>
            <guid>https://velog.io/@kyukim/oracle6</guid>
            <pubDate>Tue, 14 Mar 2023 23:30:56 GMT</pubDate>
            <description><![CDATA[<h1 id="6-커넥션과-서버-프로세스의-생성">6. 커넥션과 서버 프로세스의 생성</h1>
<ul>
<li>주로 어플리케이션과 통신을 클라이언트/서버의 형태</li>
<li>간단한 장애 일때는 아키텍쳐를 이해하고 있다면 대부분 쉽게 해결 가능</li>
</ul>
<h2 id="오라클-소켓과-동작">오라클 소켓과 동작</h2>
<ul>
<li>TCP/IP의 소켓을 네트워크 통신 수단으로 사용</li>
<li>수신을 기다리는 프로세스를 listener 라고 함</li>
</ul>
<h3 id="소켓이란">소켓이란</h3>
<ul>
<li>프로세스에서 소켓을 가지고 있고 소켓끼리 송수신이 가능</li>
<li>그 송수신을 수행하는 것은 네트워크의 드라이버와 OS의 라이브러리</li>
<li>네트워크 안에는 여러 소켓이 존재하고 address 와 port 로 식별</li>
</ul>
<h2 id="커넥션-처리">커넥션 처리</h2>
<h3 id="1-리스너-기동">(1) 리스너 기동</h3>
<p><img src="https://velog.velcdn.com/images/kyukim/post/7a19a4fc-4ce7-4ee2-b6ad-9ea2fa852e10/image.jpg" alt=""></p>
<ul>
<li>listener.ora 가 존재</li>
<li><code>lsnrctl start</code> 기동 </li>
<li>하나의 리스너가 여러 데이터베이스로 안내 가능, 일반적으로는 하나의 데이터베이스만 담당</li>
<li>포트: 1521</li>
<li>lsnrctl 이라는 도구를 사용해 리스너 기동<ul>
<li>자신이 인식해야하는 디비를 인식하는 방법은 listener.ora 설정을 읽거나, 디비에 자동으로 등록</li>
</ul>
</li>
</ul>
<h3 id="2-애플리케이션의-커넥션">(2) 애플리케이션의 커넥션</h3>
<ul>
<li>애플리케이션 안에서 연결하기 위한 명령이 실행되거나 <code>SQL*Plus</code>에서 connect 명령어를 실행</li>
<li>먼저 connecion descriptor 커넥 정보를 가지고 있어야함<ul>
<li>일반적으로 클라이언트는 tnsnames.ora 에 작성해놓는다 주소, 포트, 서비스 이름 등</li>
<li>그리고 커넥션 식별자(별칭: alias)를 붙인다</li>
</ul>
</li>
<li>일반적으로 오라클 ㅋ를라이언트는 tnsnames.ora 의 connection descriptor 정보를 사용해 소켓을 생성</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kyukim/post/c852ddf1-9f6d-432c-bb3a-e0af3a13d1a3/image.jpg" alt=""></p>
<p>클라이언트 JDBC 드라이버</p>
<pre><code class="language-java">OracleDataSource ods = new OracleDataSource();
ods.setURL(&quot;jdbc:oracle:oci:@HELLO_WORLD&quot;); // JDBC OCI 드라이버일 때 별칭을 넣는다, Thin 드라이버를 사용하면 직접 XXXX:1521/orcl 처럼 주소를 입력
ods.setUser(&quot;kyu&quot;);
ods.setPassword(&quot;1234&quot;);
Connection conn = ods.getConnection();</code></pre>
<blockquote>
<p>OCI 와 Thin 드라이버는 둘다 오라클에서 제공하고 OCI 는 오라클 클라이언트와 함께 설치 되어야함
반면에 Thin 은 따로 오라클 클라이언트를 설치할 필요없음 </p>
</blockquote>
<h3 id="3-서버-프로세스의-생성">(3) 서버 프로세스의 생성</h3>
<ul>
<li>소켓이 생성되면 통신을 한다. 이때 서버 프로세스가 항시 대기하고 있는 것이 아니다</li>
<li>클라이언트가 SQL 처리 요청을 할때 그 때 서버 프로세스가 생성이 된다</li>
<li>리스너는 서버 프로세스 생성이 끝나면 소켓을 서버 프로세스에 인계<ul>
<li>이후 클라이언트와 서버 프로세스는 직접 송수신 함</li>
<li>병렬 처리, 높은 처리량</li>
</ul>
</li>
<li>애플리케이션에서 물리 커넥션을 생성하고 종료하는 반복하는 행위를 하면 안된다<ul>
<li>단, 커넥션 풀을 사용하면 애플리케이션이 커넥션을 생성하는 것처럼 보여도 물리 커넥션을 만들지않음</li>
</ul>
</li>
</ul>
<h4 id="서버-프로세스의-생성은-무겁다">서버 프로세스의 생성은 무겁다</h4>
<ul>
<li>OS 상에서 프로세스를 생성해야함, 일반적으로 프로세스를 생성하는 것은 처리가 무겁다</li>
<li>서버 프로세스가 사용할 수 있는 공유 메모리 확보해야한다</li>
<li>서버 프로세스용 전용 메모리(PGA) 확보</li>
<li>그 외 데이터베이스 내부 처리</li>
<li>따라서 가벼운 SQL 처리할때 사용하는 CPU 보다 몇 배에서 몇십배 더많은 CPU 시간을 사용</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[5. 오라클의 기동과 정지]]></title>
            <link>https://velog.io/@kyukim/oracle-5</link>
            <guid>https://velog.io/@kyukim/oracle-5</guid>
            <pubDate>Tue, 07 Mar 2023 22:51:19 GMT</pubDate>
            <description><![CDATA[<h2 id="기동과-정지-왜-배워야-하나">기동과 정지 왜 배워야 하나?</h2>
<ul>
<li>내부 구조를 이해하는 데 도움이 됨</li>
<li>기동할때 어떤 파일을 어떻게 사용하는지, 의존 관계가 어떻게 되어있는지.</li>
</ul>
<p>데이터베이스의 기동과 정지는 데이터베이스 관리자나 개발자가 수행해야 할 일 중 하나이며, 이를 정확하게 수행하지 않으면 데이터 손상이 발생할 수 있다.
따라서 데이터베이스를 운영하거나 개발하는데 있어서 데이터베이스의 기동과 정지 방법을 이해하고, 안전하게 수행할 수 있어야 한다.</p>
<h2 id="상태">상태</h2>
<ul>
<li>OPEN, MOUNT, NOMOUNT, SHUTDOWN 상태로 존재 - 그림 5.1</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kyukim/post/358a9f76-e0a9-42e7-bf60-fe9ee09ca302/image.jpg" alt=""></p>
<h2 id="오라클의-기동">오라클의 기동</h2>
<ul>
<li>백그라운드 프로세스의 생성과 공유 메모리의 확보</li>
<li>컨트롤 파일을 본다</li>
<li>컨트롤 파일이란 디비 구성정보가 적혀있고, 디비 파일 경로등을 알 수 있다</li>
<li>SQL 처리 상태로 만드는 것</li>
<li>데이터 파일이나 REDO 로그 파일을 열어 오라클 내부적으로 사용하는 정보와 비교하여 문제가 있는지 확인</li>
</ul>
<h2 id="인스턴스-데이터베이스-그리그-주요-파일의-구성">인스턴스, 데이터베이스, 그리그 주요 파일의 구성</h2>
<ul>
<li>인스턴스: 백그라운드 프로세스 + 공유 메모리 -&gt; 데이터베이스를 관리하는 것이고, 디비와는 다르다 그림 5.2</li>
<li>일반적으로 인스턴스와 디비는 일대일 대응하지만 RAC(Real Application Clusters) 를 사용하는 경우에는 일대일대응이 아니기 때문에 명확히 구별 필요</li>
</ul>
<blockquote>
<p>RAC란? 인스턴스로 구성된 서버가 여러개임.</p>
</blockquote>
<p><img src="https://velog.velcdn.com/images/kyukim/post/6f6c3daa-efbd-4a87-82ca-60e10ceb521a/image.jpg" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kyukim/post/dcfec221-8c4c-4069-9cee-be92d785599b/image.jpg" alt=""></p>
<h2 id="기동처리-흐름과-내부-동작">기동처리 흐름과 내부 동작</h2>
<h3 id="1-기동-정지-상태에서-nomount-로-전환">1. 기동 정지 상태에서 NOMOUNT 로 전환</h3>
<ul>
<li><code>SQL*PLUS</code> 라는 도구를 통해 오라클 명령어 입력 가능.</li>
<li>일반적으로 <code>startup</code> 치면 기동 끝.<ul>
<li>Q) 아마 NOMOUNT -&gt; MOUNT -&gt; OPEN ? -&gt; A) 87p에 그렇다고 나옴</li>
</ul>
</li>
<li>대신 <code>startup nomount</code> 를 입력해보자. - 리스트5.1</li>
<li>ORACLE_HOME, ORACLE_SID 환경 변수 토대로 초기화 파라미터 파일을 찾아옴.</li>
<li>이 파일에서 읽어온 파라미터 토대로 공유 메모리 확보 및 백그라운드 프로세스 생성 - 리스트 5.2</li>
</ul>
<h3 id="2-nomount-에서-mount-로-전환">2. NOMOUNT 에서 MOUNT 로 전환</h3>
<ul>
<li><code>alter database mount</code></li>
<li>초기화 파라미터에 기술된 컨트롤 파일의 경로를 사용해 컨트롤 파일을 열어 내용을 읽어오는 것으로 REDO 로그 파일이나 데이터 파일의 위치를 파악 해야함</li>
<li>컨트롤 파일을 읽어와서 디비에 <code>Database alteterd</code> 라는 문구 확인 가능</li>
</ul>
<p>Q) 초기화 파라미터란 앞전에서 배웠떤 여러값 셋팅을 위한 그 변수들을 말하는 것?
me A) 그런것 같음. NOMOUNT 로 전환하면서 ORACLE_HOME, ORACLE_SID 환경 변수 토대로 초기화 파라미터 파일을 가지고 오고 컨트롤 파일을 가져온다. 
Q) 그럼 컨트롤 파일이란 무엇?
90p 에 나옴</p>
<h3 id="3-mount-에서-open-으로-전환">3. MOUNT 에서 OPEN 으로 전환</h3>
<ul>
<li><code>alter database open</code></li>
<li>데이터 파일을 열어 간단한 점검 후 백그라운드 프로세스 기동</li>
<li>startup 과 비교해보자. - 리스트5.5</li>
</ul>
<blockquote>
<p>결론은 초기화 파라미터 파일 -&gt; 컨트롤 파일 -&gt; 데이터 파일순
정말 그런지 확인은 88,89p 참고할것</p>
</blockquote>
<h3 id="정리">정리</h3>
<ul>
<li>초기화 파라미터를 읽어 백그라운드 프로세스 생성 -&gt; 공유 메모리를 확보 (NOMOUNT)</li>
<li>초기화 파라미터 파일에 입력된 컨트롤 파일 위치 확인 -&gt; 컨트롤 파일에서 디비 구성하는 각종 파일 위치 확인 (MOUNT)<ul>
<li>위치를 알아낼뿐 파이링 없어도 에러가 발생하지 않음</li>
</ul>
</li>
<li>데이터파일이나 REDO 로그 파일에 문제가 없다면 OPEN 으로 전환, 즉 SQL 실행할 수 있는 상태</li>
<li>어떤 파일이 손상되었다면 그 전 과정에서 생성해주어야함, 예를 들어 컨트롤 파일이 손상되었다면 NOMOUNT 상태에서 생성, 데이터파일에 문제가있따면 MOUNT 에서 복구작업 (95p)</li>
<li>초기화 파라미터 훑어보기 - 93p</li>
</ul>
<h2 id="오라클의-정지">오라클의 정지</h2>
<ul>
<li>모든 오라클 클라이언트 접속이 종료된 후 업무를 종료</li>
<li>기동 작업의 역순 + 버퍼 캐시에 분산된 데이터 정리<ul>
<li>백그라운드 프로세스는 바로 데이터를 저장하지 않기 때문에 저장되지 않은게 있다면 저장함</li>
</ul>
</li>
<li><code>shutdown</code> -&gt; 일반적인 종료, 오라클 클라이언트가 접속을 끊어주지 않거나 등등의 이유로 여러 옵션이 있음 <ul>
<li>참고로 abort 옵션은 데이터를 저장하지 않고 종료하지만, REDO 로그 파일 변경된 기록을 통해 재기동시에 &quot;인스턴스 복구&quot; 시행</li>
</ul>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[4. SQL문 분석과 공유 풀]]></title>
            <link>https://velog.io/@kyukim/4.-SQL%EB%AC%B8-%EB%B6%84%EC%84%9D%EA%B3%BC-%EA%B3%B5%EC%9C%A0-%ED%92%80</link>
            <guid>https://velog.io/@kyukim/4.-SQL%EB%AC%B8-%EB%B6%84%EC%84%9D%EA%B3%BC-%EA%B3%B5%EC%9C%A0-%ED%92%80</guid>
            <pubDate>Sat, 04 Mar 2023 13:55:37 GMT</pubDate>
            <description><![CDATA[<h1 id="chapter-4-sql문-분석과-공유-풀">Chapter 4 SQL문 분석과 공유 풀</h1>
<ul>
<li>옵티마이저와 공유 풀에 대한 설명</li>
<li>옵티마이저: SQL문 분석 최적의 처리방법 -&gt; 실행계획</li>
<li>공유 풀(Shared Pool): 실행계획이 캐시되는 곳</li>
<li>아무리 성능이 좋더라도 처리방법자체가 좋지 않다면 무용지물</li>
<li>SQL을 처리하는 방법을 생성하는 데에 CPU를 오래 사용함</li>
<li>SQL을 처리하는 방법을 생성하는 횟수를 줄이면 또한 성능을 높일 수 있음 -&gt; 공유 풀의 이용</li>
</ul>
<h2 id="sql문과-일반-프로그래밍-언어의-차이">SQL문과 일반 프로그래밍 언어의 차이</h2>
<ul>
<li>SQL 문은 &quot;데이터의 조건이나 관계&quot;만을 기술<ul>
<li><code>SELECT A FROM B WHERE C = 1</code><ul>
<li>여기에서 처리 방법을 제공하진 않는다</li>
</ul>
</li>
</ul>
</li>
<li>프로그래밍 언어에 직접 처리하도록 기술하지만, 반면에 SQL 문은 옵티마이저(파서)가 분ㅂ석하고 실행계획이라는 처리방법을 생성해준다</li>
</ul>
<h2 id="서버-프로세스와-분석">서버 프로세스와 분석</h2>
<ul>
<li>분석이란?<ul>
<li>SQL문을 분해해서 어떤 요소로 구성되어있는지 조사하고 어떤식으로 처리할지 생각하는 것</li>
<li>여기서 말하는 생각이란?<ul>
<li>알고리즘을 말함, 오라클은 두개의 기반이 되는 알고리즘이 있음<ul>
<li>규칙 기반(rule base)</li>
<li>비용 기반(cost base): 처리 시간이나 I/O 횟수가 가장 작은 것이 최고!<ul>
<li>오라클은 이를 예측하기 위해 &quot;비용(cost)&quot; 이라 불리는 수치를 이용</li>
</ul>
</li>
<li>참고: 오라클 10g 부터는 규칙 기반을 지원하지 않음</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="비용cost를-계산하기-위한-기초-수치-통계-정보">비용(cost)를 계산하기 위한 기초 수치, &quot;통계 정보&quot;</h3>
<ul>
<li>서버 프로세스는 비용을 계산하기 위해 통계정보라 불리는 기초수치를 사용<ul>
<li>테이블이나 인덱스의 관한 기초 수치</li>
</ul>
</li>
<li>이 통계정보는 Analyze 작업을 통해 얻는다<ul>
<li>관리자가 수행하지 않더라도 자동으로 수행</li>
</ul>
</li>
</ul>
<h2 id="실행-계획-최적이라는-판단하기-위해서는">실행 계획 최적이라는 판단하기 위해서는?</h2>
<blockquote>
<p>예제 조건</p>
<ol>
<li><code>SELECT * FROM A, B WHERE A.ID = B.ID AND A.value = 1 AND B.value = 1;</code></li>
<li>A 테이블 1000만 건, B테이블 100건, A의 Value 는 대부분 1.</li>
</ol>
</blockquote>
<ul>
<li>어떤 식으로 가져와야 최적으로 데이터를 가져오는 것일까? 생각해보자 67p</li>
<li>어떤 방식으로 접근하냐에 따라 성능 차이가 매우 크게 날 수 있다.</li>
</ul>
<h3 id="그럼-어떤-처리-방법이-가장-좋은지를-어떻게-판단">그럼 어떤 처리 방법이 가장 좋은지를 어떻게 판단?</h3>
<ul>
<li>모든 처리 방법의 비용을 계싼하는 것 외에는 방법이 없음</li>
<li>그러나 실제로 모든 처리방법의 비용을 계산하면 큰일</li>
<li>테이블 수가 늘어날수록 비용을 계산해야하는 경우의 수가 막대해지기 때문임</li>
<li>간혹 DBMS 가 좋지 않은 실행 계획을 선택하는 이유이기도 함<ul>
<li>모든 실행계획의 경우의 수를 계산할 수 없기때문.</li>
<li>또한, 이런 실행계획들도 &quot;예측&quot;에 지나지 않기 때문.</li>
</ul>
</li>
</ul>
<h2 id="공유-풀shared-pool의-동작과-구조">공유 풀(Shared Pool)의 동작과 구조</h2>
<ul>
<li>이런 CPU 소모가 큰 SQL문 분석결과(실행계획)를 공유해서 사용할 수 있도록 캐시할 수 있는 곳이 공유 풀</li>
<li>Shared Pool 또한 서버 프로세스 간에 공유되어야하기 때문에 버퍼캐시처럼 공유메모리에 존재<ul>
<li>참고로 공유메모리에서 버퍼캐시가 가장 많은 부분을 차지</li>
</ul>
</li>
</ul>
<p><img src="https://velog.velcdn.com/images/kyukim/post/630a0822-4fae-450b-818b-744868658b48/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kyukim/post/f51e3c40-a0cb-44d4-bd4e-4a27e69fd525/image.png" alt=""></p>
<h3 id="오라클은-어떻게-sql문이-같다고-판단하고-실행계획을-캐시할까">오라클은 어떻게 SQL문이 같다고 판단하고 실행계획을 캐시할까?</h3>
<ul>
<li>해시 알고리즘을 사용해 SQL 문마다 ID 생성<ul>
<li>참고로 SQL문의 대소문자구별해서 해시생성한다 -&gt; 같은 SQL문이라도 대소문자 다르면 다른 해시값</li>
</ul>
</li>
<li>WHERE 조건에 들어가는 변수 등은 바인드 변수로 해석하고 같은 SQL로 인식<ul>
<li><code>SELECT * FROM customer WHERE id = 1;</code></li>
<li><code>SELECT * FROM customer WHERE id = 2;</code></li>
<li>위 두 쿼리는 <code>SELECT * FROM customer WHERE id = :바인드_변수</code> 같은 쿼리로 취급하여 해시값이 같음</li>
</ul>
</li>
</ul>
<h3 id="하드-파스hard-parse와-소프트-파스soft-parse">하드 파스(hard parse)와 소프트 파스(soft parse)</h3>
<ul>
<li>실행 계획을 생성해서 공유 풀에 생성하는 것을 <strong>hard parse</strong></li>
<li>해시값을 id로 하여 공유풀에 캐시되어있는 실행계획을 찾아 재사용하는 것을 <strong>soft parse</strong></li>
</ul>
<h2 id="기타">기타</h2>
<ul>
<li>Statspack: 오라클 성능 진단 도구<ul>
<li>보고서를 보면서 soft와 hard 분석(parse)을 얼마나하는지 체크할 수 있고</li>
<li>CPU 사용량중에서 얼마만큼이 parse가 사용되는지 확인하여 성능 분석 가능</li>
</ul>
</li>
<li>AWR(Automatic Workload Repository): 오라클 10g 부터 사용가능한 Statspack 업그레이드, 라이센스 필요</li>
<li>통계정보 언제수집? 77p -&gt; 언제수집하는지도 중요하다</li>
</ul>
<p>todo) 해시 알고리즘 공부</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[3. 캐시와 공유 메모리]]></title>
            <link>https://velog.io/@kyukim/3.-%EC%BA%90%EC%8B%9C%EC%99%80-%EA%B3%B5%EC%9C%A0-%EB%A9%94%EB%AA%A8%EB%A6%AC</link>
            <guid>https://velog.io/@kyukim/3.-%EC%BA%90%EC%8B%9C%EC%99%80-%EA%B3%B5%EC%9C%A0-%EB%A9%94%EB%AA%A8%EB%A6%AC</guid>
            <pubDate>Tue, 21 Feb 2023 14:50:26 GMT</pubDate>
            <description><![CDATA[<h1 id="캐시와-공유-메모리">캐시와 공유 메모리</h1>
<ul>
<li>캐시 기능(버퍼 캐시)에 관해 살펴보는 장</li>
<li>캐시 공유를 위한 특수한 메모리 기능 (공유 메모리)</li>
</ul>
<h2 id="캐시는-왜-필요하고-캐시란-무엇인가">캐시는 왜 필요하고 캐시란 무엇인가?</h2>
<ul>
<li>일반적으로 생각하는 캐시의 의미랑 같음</li>
<li>참고로 캐시에 &quot;히트한다/안한다&quot; 라는 표현을 사용</li>
<li>데이터베이스에서의 캐시란 메모리 공간의 사용을 말함</li>
<li>버퍼 캐시라는 용어를 사용</li>
</ul>
<h2 id="데이터의-보관-규격---블록">데이터의 보관 규격 - 블록</h2>
<ul>
<li>테이블의 수천만 row 의 데이터를 어떻게 보관할까? -&gt; 블록이라는 개념이 존재한다<ul>
<li>그림3.6 42p</li>
</ul>
</li>
<li>블록 크기는 2, 4, 8, 16, 32KB 중에서 선택</li>
</ul>
<p>Q) 42p 마지막
크기가 큰 테이블을 시퀀셜 액세스로 읽어와야하는 DW 등에는 16KB, 32KB 같은 큰 크기를 선택한다?
me A) 크기가 크니까 최대한 I/O 를 줄여야할 것이다. 즉, 최대한 많은 데이터를 하나의 블록에 넣어야한다.</p>
<h2 id="캐시와-인덱스">캐시와 인덱스</h2>
<ul>
<li>인덱스도 블록으로 구성</li>
<li>인덱스가 클 땐 두블록 이상이 될 수도 있음</li>
<li>캐시에 있지 않으면 그만큼 시간이 더 많이 걸림<ul>
<li>그림 3.8 44p</li>
</ul>
</li>
</ul>
<h2 id="프로세스는-캐시를-공유">프로세스는 캐시를 공유</h2>
<ul>
<li>프로세스마다 캐시를 가지면 문제가 다른 프로세스에서 변경된 데이터를 볼수없고 낭비가 심해짐</li>
<li>그래서 메모리를 공유한다</li>
<li>OS 에서 공유 메모리라는 기능을 제공<ul>
<li>오라클에서 이를 SGA(System Global Area)</li>
</ul>
</li>
</ul>
<p>Q) PGA 란 무엇인인가? 책에 따르면 공유하지 않는 메모리의 일부라고 하는데 그러면 다른 프로그램 메모리들은 뭘 하는것? 특별히 PGA는 또 무엇을 하는것?
Q) 46p,47p 그림이 잘 이해가안감. PGA 를 거쳐서 SGA 의 데이터를 변경한다고 하는것인가?</p>
<h2 id="공유-메모리-설정">공유 메모리 설정</h2>
<ul>
<li>spfileXXX.ora (XXX에는 데이터베이스를 식별하는 ID가 들어감) 에서 가능 DB_CACHE_SIZE 라는 게 있다</li>
<li>공유 메모리에는 버퍼 캐시 말고도 공유 풀 로그 버퍼 같은 영역도 존재</li>
</ul>
<p>버퍼 캐시의 크기는 어떻게 정하나?</p>
<ul>
<li>임시로 처음 크기는 크게 잡는다.<ul>
<li>이후에 통계 기반 조정<ul>
<li>버퍼 캐시 크기마다 물리 읽기 수를 예측하는 버퍼 캐시 어드바이저 (V$DB_CACHE_ADVICE)</li>
<li>버퍼 캐시 내에서 요청받은 블록을 꺼내 온 빈도를 의미하는 버퍼 캐시 히트율</li>
</ul>
</li>
</ul>
</li>
<li>버퍼 캐시를 아예 설정하지 않는다. (49p) 어려운 이야기.</li>
</ul>
<h2 id="공유-메모리는-어떤-식으로-보이는가">공유 메모리는 어떤 식으로 보이는가?</h2>
<ul>
<li>ps 명령어로 VSZ 항목을 확인해보면 각 프로세스들이 대량의 메모리가 있는 것처럼 보이지만 공유메모리도 같이 포함한다는 것을 알아야한다.</li>
<li>세마포어: 자원의 수에 비해 사용하고자하는 프로세스의 수가 많을 경우 순서대로 자원을 사용할 수 있게 제어해주는 장치의 일종</li>
</ul>
<h2 id="버퍼-캐시를-정리하는-lru-알고리즘">버퍼 캐시를 정리하는 LRU 알고리즘</h2>
<ul>
<li>공간이 한정적이므로 정리가 필요함</li>
<li>LRU (Least Recently Used) - 최근에 사용하지 않은 데이터부터 제거</li>
<li>SELECT -&gt; 그림 3.11 A 가 가장 오래 사용하지 않은 데이터이고 이를 E 로 변경하는 그림</li>
<li>UPDATE -&gt; 그림 3.12 은 업데이트 친다고 바로 디스크에 쓰는 게 아니라 정기적으로 DBWR 이 업데이트 해주는 모습을 보여줌</li>
<li>BULK SELECT -&gt; 그림 3.13 풀 스캔 했을 때 모든 데이터들이 버퍼 캐시에 오면 자주 사용하던 캐시들이 버려지는 일이 발생한다고도 생각할 수 있다.<ul>
<li>그런데 오라클에서는 풀스캔했을 때의 데이터는 일반적으로 버퍼캐시에 적재하지 않는다.</li>
</ul>
</li>
</ul>
<h2 id="os-가상-메모리">OS 가상 메모리</h2>
<ul>
<li>가상메모리: 물리 메모리가 아닌 디스크의 일부를 스왑영역(메모리에 데이터를 보관하는 영역)으로 사용</li>
<li>페이징: 스왑영역에서 데이터가 왔다갔다하는 것을 page out(디스크에 기록), page in(읽기) 라고 한다.</li>
<li>가상메모리는 버퍼캐시와 달리 메모리 영역을 늘리기 위해 디스크에서 사용되므로 당연히 속도는 그만큼 느리다.</li>
<li>오라클은 물리 메모리만 사용하도록 설정할 것을 권함.</li>
<li>59p 재기동 I/O 빨라지는 수수께끼 이해불가.</li>
</ul>
<p>Q) 가상메모리도 결국에 디스크를 사용하는 거라면 무슨 의미가 있는 것인가?
me A) 어쨌든 버퍼캐시(블록)으로 만들어 둔거니까 접근하는데 아예 디스크에서 불러오는 것보단 빠를것이다?
K A) 가상 메모리는<code>프로세스에게 하드웨어 독립적인 메모리 공간을 제공</code>하는게 우선적인 목표입니다. 그래서 OS 환경 위에서 프로그래밍하는 프로그래머에게 하드웨어의 실제 메모리 할당 위치를 고려할 필요 없게 해주는 것이죠.</p>
<p>이렇게 해서 메모리를 가상화하면 프로세스가 실제 하드웨어의 물리 메모리 보다 많은 메모리 공간을 사용할 수 있습니다. 이때 되면 자연스럽게 물리 메모리 공간이 부족하니 디스크를 메모리로 활용하는거구요.</p>
<p>이를 허용할지 말지가 시스템 운영하는 사람의 선택입니다.</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 static 몰랐던 거..]]></title>
            <link>https://velog.io/@kyukim/%EC%9E%90%EB%B0%94-static-%EB%AA%B0%EB%9E%90%EB%8D%98-%EA%B1%B0</link>
            <guid>https://velog.io/@kyukim/%EC%9E%90%EB%B0%94-static-%EB%AA%B0%EB%9E%90%EB%8D%98-%EA%B1%B0</guid>
            <pubDate>Thu, 09 Feb 2023 09:23:25 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-java">public class StopThread {
    private static boolean stopRequested;

    public static void main(String[] args) throws InterruptedException {
        Thread backgroundThread = new Thread(() -&gt; {
            int i = 0;
            while (!stopRequested)
                i++;
        });
        backgroundThread.start();
        TimeUnit.SECONDS.sleep(1);
        stopRequested = true;
    }
}</code></pre>
<p>위 코드는 1초후에 멈추는 프로그램이라고 예상되어진다.
왜?
static 변수인 <code>stopRequested</code>가 1초 후에 true가 되었기 때문이다.
static 변수는 쓰레드가 공유하는 변수이다.
따라서 <code>backgroundThread</code> 와 <code>mainThread</code> 가 서로 공유하는 변수이기 때문에
1초후에 멈추는 프로그램일것이다.
하지만 멈추지 않는다.
왜?
<strong>동기화가 되지 않았기 때문이다.</strong>
내가 알고 있는 동기화는 메소드에 동기화(synchronized)를 걸었다면 어떤 쓰레드가 synchronized 메소드를 읽기전에 해당 메소드를 lock 하여 다른 쓰레드가 접근하지 못하게 하고 메소드를 실행한다. 실행 후에는 lock 을 해제하고 대기중이던 다른 쓰레드가 접근하게 된다.</p>
<p>이런 메커니즘이 동기화와 어떤 관런이 있다는 것일까?</p>
<p>두 쓰레드가 synchronized 나 volatile 키워드를 사용하지 않으면 static 변수를 메인 쓰레드에서 변경했다고 하더라도 다른 쓰레드에 적용될지 안될지는 모른다.</p>
<p>왜냐하면 cpu 가 두개이상이라면 두 cpu 에서 각각의 변수을 메인 메모리가 아니라 캐시 메모리에 복사하여 가져온다. 이때 한쪽의 변수를 수정했다고해서 메인 메모리에 바로 적용되는 것이 아니기 때문에 다른 쓰레드에서 변경된 변수를 사욭할 수가 없고 최초 복사했던 변수를 사용하는 것이다.</p>
<p>처음에 이해했던 방식은 두 쓰레드 스택에서 힙에 있는 공통된 객체를 바라보고 있기 때문에 당연히 변수를 공유했다고 생각했던 것이다.</p>
<p>그런데 두개가 당연히 공유하는 것이아니라 cpu 코어가 2개 이상이라면 쓰레드들은 각각의 cpu에서 작동할테고 한쪽에서 메인메모리에서 cpu 캐시메모리로 가져가서 수정된 변수는 바로 메인메모리에 저장되지 않기 때문에 서로 같은 값을 볼 수 없는 것이다.</p>
<p>cpu 는 cpu가 연산할 때 register 와 캐시 메모리 그리고 메인메모리 사이를 데이터들이 꼭 이동하게 되기때문인데, 이를 메인메모리에 동기화시켜 사용하고 싶으면 volatile 이나 synchronized 키워드를 꼭 사용해야한다.</p>
<p>보면서 공부한 링크들:
<a href="https://madplay.github.io/post/synchronize-access-to-shared-mutable-data">https://madplay.github.io/post/synchronize-access-to-shared-mutable-data</a>
<a href="https://parkcheolu.tistory.com/14">https://parkcheolu.tistory.com/14</a>
<a href="https://github.com/java-squid/effective-java/issues/80">https://github.com/java-squid/effective-java/issues/80</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 Collectors.toMap & Map.merge]]></title>
            <link>https://velog.io/@kyukim/%EC%9E%90%EB%B0%94-Collectors.toMap-map.merge</link>
            <guid>https://velog.io/@kyukim/%EC%9E%90%EB%B0%94-Collectors.toMap-map.merge</guid>
            <pubDate>Wed, 18 Jan 2023 02:38:24 GMT</pubDate>
            <description><![CDATA[<h2 id="collectorstomap">Collectors.toMap()</h2>
<ul>
<li>파라미터 2개 3개 4개 받을 수있다</li>
<li>셋다 기본적으로 (Function keyMapper, Function valueMapper) 를 받는다.</li>
<li>의미는 스트림 원소들 순회해서 key 랑 value 로 만들어서 map 으로 리턴한다는 뜻이다.</li>
<li>예를 들어 Person 이라는 객체가 있으면 아래 이미지 처럼 Map 으로 만들 수 있다.
<img src="https://velog.velcdn.com/images/kyukim/post/2be003d1-4137-4d89-a3ad-5750a19b2218/image.png" alt=""></li>
<li>파라미터 3개 들어가는건 (Function keyMapper, Function valueMapper, Function mapper) 인데 key가 중복으로 왔을때 어떻게 처리할건지 보여주는 것이다.
<img src="https://velog.velcdn.com/images/kyukim/post/8360eaa2-2646-446b-80f0-d5b915249b05/image.png" alt=""></li>
<li>key 가 중복이면 newValue 에다가 oldValue 를 더한 값을 Value 로 지정하겠다는 말이다.</li>
<li>파라미터가 4개 들어가는건, 마지막 네번째 파라미터는 Map 에서 특정 구현체를 선택하고 싶을때 넣을 수 있다. 기본은 HashMap 이다</li>
</ul>
<h2 id="mapmerge">map.merge()</h2>
<ul>
<li>Map.merge() 메소드도 그런식으로 동작한다.<pre><code class="language-java">Map&lt;String, Integer&gt; map = new HashMap&lt;&gt;();  
for (String[] strings : collect) {  
  for (String string : strings) {  
      if (map.get(string) == null) {  
          map.put(string, 1);  
      } else {  
          map.put(string, map.get(string) + 1);  
      }  
  }  
}</code></pre>
</li>
<li>위 코드는 merge 를 사용하지 않고 직접 클라이언트가 로직을 작성한 방식이다.</li>
<li>String 배열을 돌려서 map 에 해당 String key 가 존재하지 여부를 판별하여 기본값을 1로 두고 key가 겹치면 +1을 더하는 방식이다.</li>
<li>위 코드가 정확히 아래코드와 일치하게 동작한다.<pre><code class="language-java">Map&lt;String, Integer&gt; map = new HashMap&lt;&gt;();  
for (String[] strings : collect) {  
  for (String string : strings) {  
      map.merge(string, 1, Integer::sum);
  }  
}</code></pre>
</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[@Async 어노테이션과 약간의 Spring AOP 개념]]></title>
            <link>https://velog.io/@kyukim/Async-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EA%B3%BC-%EC%95%BD%EA%B0%84%EC%9D%98-Spring-AOP-%EA%B0%9C%EB%85%90</link>
            <guid>https://velog.io/@kyukim/Async-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98%EA%B3%BC-%EC%95%BD%EA%B0%84%EC%9D%98-Spring-AOP-%EA%B0%9C%EB%85%90</guid>
            <pubDate>Mon, 09 Jan 2023 06:01:30 GMT</pubDate>
            <description><![CDATA[<blockquote>
<p>📌 스프링 @Async 에 대해서 알아봅시다. 약간의 AOP 배경지식도 필요합니다.</p>
</blockquote>
<hr>
<h2 id="spring-aop">Spring AOP</h2>
<p>스프링이 비동기 작업을 수행하기 위해선
기본적으로 런타임에서 해당 클래스에 대한 프록시가 필요하다.</p>
<h3 id="용어">용어</h3>
<ul>
<li><p><strong>Aspect</strong>: 여러 클래스를 cut across 하는 관심사의 모듈화</p>
<ul>
<li>예를 들면, Transactional 어노테이션을 생각하면 됨
  <img src="https://velog.velcdn.com/images/kyukim/post/5e1729fa-47fd-42af-abc5-73512c51732c/image.png" alt=""></li>
</ul>
</li>
<li><p><strong>Joinpoint</strong>: 프로그램 실행 시점, 메소드의 진입지점이라고 생각하면 됨</p>
</li>
<li><p><strong>Advice</strong>: 특정 join point 에서 aspect 에 의해 취해진 액션</p>
<ul>
<li>다양한 advice type 이 있음, 가령 &quot;around&quot;, &quot;before&quot;, &quot;after&quot; ...</li>
<li>스프링 포함 많은 AOP 프레임웤에서 Advice 를 인터셉터로 모델링하고, 인터셉터 체인을 관리한다.</li>
</ul>
</li>
<li><p><strong>Pointcut</strong>: Joint point 의 정규 표현식, Join point 가 Pointcut에 일치할때마다 해당 Pointcut에 관련된 <strong>Advice</strong>가 실행된다.</p>
</li>
</ul>
<h3 id="자세히-알아보기">자세히 알아보기:</h3>
<ul>
<li><a href="https://stackoverflow.com/a/24902434">왜 <code>@Async</code> 가 붙은 메소드를 내부에서 실행하면 비동기적으로 동작하지 않나?</a><ul>
<li>요약: Spring AOP 의 컨셉과 다르다. 프록시 객체를 통해서 호출해야하기 때문이다.</li>
</ul>
</li>
<li><a href="https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop">Aspect Oriented Programming with Spring</a></li>
<li><a href="https://dzone.com/articles/spring-pitfalls-proxying"><strong>스프링에서 AOP 가 프록시하는 방법</strong></a><ul>
<li>요약: Java dynamic proxy, CGLIB, AspectJ weaving 3가지가 있다.<ul>
<li><strong>Java dynamic proxy</strong>: 인터페이스를 구현한 객체를 프록시화</li>
<li><strong>CGLIB</strong>: 원래 객체를 상속받는 방식</li>
<li><strong>AspectJ</strong>: 클래스 바이트 코드 자체를 조작<ul>
<li>Compile Time Weaving (CTW)</li>
<li>Load Time Weaving (LTW)</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<h2 id="async-어노테이션"><code>@Async</code> 어노테이션</h2>
<p>Executor 를 커스텀으로 빈으로 등록하지 않았으면,
Spring 이 자동으로 ThreadPoolTaskExecutor 를 기본값과 함께 컨테이너에 빈을 등록한다ThreadPoolTaskExecutor 은 @EnableAsync 와 Spring MVC 비동기 요청 프로세스를 동작하도록 해준다.</p>
<h3 id="요약">요약</h3>
<ul>
<li><strong>호출되어진 스레드의 로직은 호출한 스레드에 영향을 안준다.</strong><ul>
<li>Simply put, annotating a method of a bean with <em>@Async</em> will make it <strong>execute in a separate thread.</strong> In other words, the caller <strong>will not wait</strong> for the completion of the called method. - <a href="https://www.baeldung.com/spring-async">https://www.baeldung.com/spring-async</a></li>
</ul>
</li>
<li>메소드를 비동기 실행을 할 수 있도록 마크한다.<ul>
<li>type 레벨에도 사용할 수 있다.<ul>
<li>이 경우엔 모든 메소드가 async 로 동작한다</li>
</ul>
</li>
</ul>
</li>
<li><code>@Configuration</code> 이 붙은 어노테이션 밑에 <code>@EnableAsync</code> 를 <strong>반드시</strong> 넣어야한다.</li>
<li><code>@Async</code> 메소드 시그니쳐에는 어떤 파라미터 타입이 들어와도 상관없다.</li>
<li><strong>리턴타입은 void 혹은 <code>Futrue</code> 이어야한다.</strong><ul>
<li>Future<ul>
<li>CompletableFuture 같은 것을 사용하면 비동기된 메소드를 기다려 리턴값을 받을 수 있다.</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="더-알아보기">더 알아보기:</h3>
<ul>
<li><a href="notion://www.notion.so/whatap/%5B%3Chttps://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/Async.html%3E%5D(%3Chttps://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/scheduling/annotation/Async.html%3E)">Spring API 문서</a></li>
<li><a href="https://docs.spring.io/spring-boot/docs/2.7.7/reference/html/features.html#features.task-execution-and-scheduling">Spring 핵심 기능 - Task Execution and Scheduling</a></li>
</ul>
<h3 id="실습하면서-얻은-정보">실습하면서 얻은 정보:</h3>
<ul>
<li>private 메소드이면 안된다.<ul>
<li>why?<ul>
<li>public 이어야 <strong>프록시생성</strong> 할 수 있기 때문이다</li>
</ul>
</li>
</ul>
</li>
<li>동일 클래스에서 호출하면 안된다<ul>
<li>why?<ul>
<li>self-invocation doesn&#39;t work because <strong>it bypasses the proxy and calls the underlying method directly.</strong></li>
</ul>
</li>
</ul>
</li>
<li>클래스 내부에 Proxy 기반 동작하는 어노테이션 (@Transactional, @Async, @Cacheable, 커스텀어노테이션 등) 메소드가 있으면, 일반 bean 이 아니라 Proxy 로 래핑되서 bean 이 생성된다.<ul>
<li>Spring Context 가 Bean 후보들을 스캔하여 Bean 으로 생성할때 래핑하냐 마냐를 결정한다. 빈이 생성되는 과정에서 AbstractAutoProxyCreator.wrapIfNecessary() 함수가 호출되는데 이름에서 알 수 있듯이 필요한 경우에 Bean 으로 만들고자 하는 클래스를 Proxy 로 Wrap 하는 역할을 수행하는 함수이다.</li>
</ul>
</li>
</ul>
<h3 id="트러블-슈팅">트러블 슈팅</h3>
<p>아래 순서대로 호출했는데 <code>@Async</code> 가 동작하지 않음</p>
<pre><code>1. Controller 에서 서비스(bean) 객체 메소드 호출 
2. 내부 private 메소드 호출 
3. 다른 서비스(bean) `@Async` 메소드 호출
</code></pre><ul>
<li>위 순서를 테스트하면 정상적으로 async 가 동작하지만 실무에서 동작하지 않는 문제가 있었음.</li>
<li><code>@Async</code> 가 실행 되지 않는 원인은 해당 메소드의 클래스의 프록시 객체가 만들어지지 않았기 때문이다.<ul>
<li>원인은 아직도 모르겠음</li>
</ul>
</li>
<li>해당 클래스는 서비스 클래스이기 때문에 어찌됐던 간에 프록시를 만들어 사용하면 해결되었다.</li>
</ul>
<pre><code class="language-java">@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)</code></pre>
<ul>
<li>위 어노테이션을 붙이면 CGLIB 프록시 객체가 만들어진다.</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[윈도우10 spring boot cloud returns host.docker.internal for client host name ]]></title>
            <link>https://velog.io/@kyukim/%EC%9C%88%EB%8F%84%EC%9A%B010-spring-boot-cloud-returns-host.docker.internal-for-client-host-name</link>
            <guid>https://velog.io/@kyukim/%EC%9C%88%EB%8F%84%EC%9A%B010-spring-boot-cloud-returns-host.docker.internal-for-client-host-name</guid>
            <pubDate>Mon, 29 Aug 2022 07:00:00 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요
저번 3주차 코드리뷰때 eureka 서버에 등록된 인스턴스들 호스트가 왜 host.docker.internal 인지 물어보셔서 답변을 못드려서 찾아봤는데 공유해드리고 싶어서 메일드립니다.</p>
<p>아래 참고 링크를 보니 윈도우10에서 eureka에 인스턴스들 등록할때 IP를 기반으로 호스트를 조회하는데 이때 윈도우10 같은경우에 C:\Windows\System32\Drivers\etc\hosts 파일에 등록된 것을 기준으로 조회하는 것 같습니다.</p>
<p>이때 기존 빨간줄 친 부분은 윈도우에 docker 설치할때 등록되는 것 같은데 이 alias 가 무시되도록 다른 alias를 추가하니까 eureka에 뜨는 호스트 alias 또한 변경된 것을 확인할 수 있었습니다</p>
<p>참고: <a href="https://stackoverflow.com/a/63283687/14058876">https://stackoverflow.com/a/63283687/14058876</a></p>
<p><img src="https://velog.velcdn.com/images/kyukim/post/51c2e8bb-57a5-4c0c-bbd6-2ca1deabefce/image.png" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kyukim/post/4402b021-f954-4865-b9bf-0883fa612d65/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[Spring Cloud LoadBalancer VS Spring Cloud Gateway]]></title>
            <link>https://velog.io/@kyukim/Spring-Cloud-LoadBalancer-VS-Spring-Cloud-Gateway</link>
            <guid>https://velog.io/@kyukim/Spring-Cloud-LoadBalancer-VS-Spring-Cloud-Gateway</guid>
            <pubDate>Wed, 24 Aug 2022 13:15:05 GMT</pubDate>
            <description><![CDATA[<p>안녕하세요
토이프로젝트로 Spring Cloud로 마이크로서비스를 만들어보고있는데 로드밸런싱하는 것에 대해서 궁금한게 있습니다</p>
<p>spring cloud netflix ribbon (loadbalancer)랑 spring cloud netflix zuul (gateway) 이 두 개념이 너무 헷갈리는데요..</p>
<ol>
<li>ribbon을 client-side loadbalancer 라고 하는데 여기서 클라이언트라는 말은 RestTemplate 같은 http client 를 이용해서 데이터를 받는 서버를 클라이언트라고 하는 것일까요?</li>
</ol>
<p>예를 들어 주문서비스 -&gt; 재고서비스 형태로 RestTemplate으로 커뮤니케이션할때 ribbon client를 썼다면 주문서비스를 client-side loadbalancer 라고 하는 것인가요?</p>
<ol start="2">
<li>두 개념에 대해 제가 이해한 것은 아래 그림과 같은데요, gateway를 사용할 경우 eureka에 등록된 application id (한 서비스의 인스턴스 그룹 식별자)를 기준으로 로드밸런싱해줄 수 있고, ribbon 같은 경우엔 어플리케이션 내에서 http client 로 요청하는 특정 주소들에 대해서 로드밸런싱 해줄 수있다고 이해했습니다. 이게 맞을까요?</li>
</ol>
<p><a href="https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-ribbon.html">https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-ribbon.html</a> 를 보면 첫문장이 제가 질문하는 거에 대한 답변인거 같기도 하고,, 아닌거 같기도하고 자신이 없네요,,, 다른 분들의 답변을 들어보고 싶습니다</p>
<p><img src="https://velog.velcdn.com/images/kyukim/post/599c54bb-5d75-4512-9b9e-d48b72b78a50/image.jpg" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kyukim/post/4f262c6f-5c19-4c37-8196-096cb9a895c1/image.jpg" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kyukim/post/953a102d-4995-4def-abff-3f4d57ed2492/image.jpg" alt=""></p>
<p><img src="https://velog.velcdn.com/images/kyukim/post/80319b05-1943-42a8-8d39-9e2b03e626f8/image.jpg" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 Reflection 어노테이션 기반 DI 구현]]></title>
            <link>https://velog.io/@kyukim/%EC%9E%90%EB%B0%94-Reflection-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EA%B8%B0%EB%B0%98-DI-%EA%B5%AC%ED%98%84</link>
            <guid>https://velog.io/@kyukim/%EC%9E%90%EB%B0%94-Reflection-%EC%96%B4%EB%85%B8%ED%85%8C%EC%9D%B4%EC%85%98-%EA%B8%B0%EB%B0%98-DI-%EA%B5%AC%ED%98%84</guid>
            <pubDate>Wed, 10 Aug 2022 02:41:23 GMT</pubDate>
            <description><![CDATA[<pre><code class="language-java">package di;

import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;

public class ContainerService {

    // Method paramter로 넘기는 타입으로 리턴타입을 받는다.
    public static &lt;T&gt; T getObject(Class&lt;T&gt; classType){
        T instance = createInstance(classType);
        Arrays.stream(classType.getDeclaredFields()).forEach(f -&gt; {
            // 만약에 필드에 Inject 어노테이션이 붙어있다면
            if (f.getAnnotation(Inject.class) != null) {
                // 해당 Inject가 붙은 클래스의 타입을 가져와서
                Class&lt;?&gt; type = f.getType();
                // 해당퇴는 타입의 인스턴스를 만든다
                Object filedInstance = createInstance(type);
                f.setAccessible(true);
                try {
                    System.out.println(f.get(instance));
                    f.set(instance, filedInstance );
                    System.out.println(f.get(instance));
                } catch (IllegalAccessException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        return instance;
    }

    // 리플렉션을 통해 instance를 생성하는 메소드
    private static &lt;T&gt; T createInstance(Class&lt;T&gt; classType) {
        try {
            return classType.getDeclaredConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
    }
}
</code></pre>
]]></description>
        </item>
        <item>
            <title><![CDATA[너무나도 신기하고 재밌는 JPA]]></title>
            <link>https://velog.io/@kyukim/%EB%84%88%EB%AC%B4%EB%82%98%EB%8F%84-%EC%8B%A0%EA%B8%B0%ED%95%98%EA%B3%A0-%EC%9E%AC%EB%B0%8C%EB%8A%94-JPA</link>
            <guid>https://velog.io/@kyukim/%EB%84%88%EB%AC%B4%EB%82%98%EB%8F%84-%EC%8B%A0%EA%B8%B0%ED%95%98%EA%B3%A0-%EC%9E%AC%EB%B0%8C%EB%8A%94-JPA</guid>
            <pubDate>Sat, 06 Aug 2022 11:12:39 GMT</pubDate>
            <description><![CDATA[<h3 id="1">1</h3>
<p><img src="https://velog.velcdn.com/images/kyukim/post/43921e0b-95ec-47a3-802f-b385000bd536/image.jpg" alt="">
위 코드는 서비스 계층에서 <code>주문</code>을 담당하는 메소드이다.</p>
<p><code>Order</code>에는 <code>CascadeType.ALL</code>로 설정되어 있다
44번줄 <code>orderRepository.save(order)</code> 에서는 연관관계에 있는 <code>OrderItem</code>과 <code>Delivery</code>를 따로 persist해주지 않아도 같이 영속화 된다.</p>
<p>비즈니스 요구사항에 따라 달라질 수 있는 부분이지만, 중요한 점은 JPA에서 이런식으로 자동으로 연관관계가 있는 객체들을 같이 데이터 영속화를 할 수 있다는 것이다.</p>
<p>MyBatis로 한다면? <code>Order</code>, <code>Delivery</code>, <code>OrderItem</code>을 모두 따로 DAO에서(interface, .xml) 영속화 시키고 <code>return order.getId()</code>부분도 따로 <code>LAST_INSERT_ID()</code>등과 같이 디비 벤더에 맞는 함수를 또 작성ㅎ서 들고와야 할 것이다.</p>
<h3 id="2">2</h3>
<p><img src="https://velog.velcdn.com/images/kyukim/post/98b3b6f5-5a02-4537-b8dc-d14bfabda28e/image.jpg" alt="">
위 코드는 Order를 취소하는 것이다</p>
<p><code>order.cancel()</code>로 들어가보자.</p>
<p><img src="https://velog.velcdn.com/images/kyukim/post/677c188e-eedc-4356-ac65-b3f8294e8ed3/image.jpg" alt=""></p>
<p><code>orderItem.cancel()</code> 로 들어가보자.</p>
<p><img src="https://velog.velcdn.com/images/kyukim/post/03da9065-ea10-46c1-bb7c-4317a95571ce/image.jpg" alt=""></p>
<p>주문을 취소하는 로직에는 위와 같이 몇몇 정보들을 업데이트 해주어야한다.
JPA 같은 경우엔 그냥 <code>order.cancel()</code>만 때리면 된다.</p>
<p>MyBatis? 한줄 한줄 모두 일일이 쿼리를 작성해서 업데이트해주어야한다.</p>
<ol>
<li>먼저 상태비교를 위해 delivery.status 를 조회</li>
<li>OrderStatus를 CANCEL로 업데이트</li>
<li>마지막에 해당 OrderItem에 연관되어 있는 Item의 수량을 업데이트</li>
</ol>
<p>무려 3개 쿼리를 만들어주어야하고 이를 관리해주어야한다</p>
<p>MyBatis만 쓰던 나는 이게 얼마나 신기하고 기가막히는지 모르겠다.</p>
<hr>
<p>레퍼런스: 인프런 - 김영한님 JPA 활용 1편</p>
]]></description>
        </item>
        <item>
            <title><![CDATA[깃헙 로그인시 사용하는 토큰과 다른 기능사용시 사용하는 SSH Key 설정]]></title>
            <link>https://velog.io/@kyukim/%EA%B9%83%ED%97%99-%EB%A1%9C%EA%B7%B8%EC%9D%B8%EC%8B%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%ED%86%A0%ED%81%B0%EA%B3%BC-%EB%8B%A4%EB%A5%B8-%EA%B8%B0%EB%8A%A5%EC%82%AC%EC%9A%A9%EC%8B%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-SSH-Key-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@kyukim/%EA%B9%83%ED%97%99-%EB%A1%9C%EA%B7%B8%EC%9D%B8%EC%8B%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-%ED%86%A0%ED%81%B0%EA%B3%BC-%EB%8B%A4%EB%A5%B8-%EA%B8%B0%EB%8A%A5%EC%82%AC%EC%9A%A9%EC%8B%9C-%EC%82%AC%EC%9A%A9%ED%95%98%EB%8A%94-SSH-Key-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Fri, 22 Jul 2022 09:49:50 GMT</pubDate>
            <description><![CDATA[<ol>
<li><p>로그인시에 사용하는 Personal access tokens 은 셋팅-&gt;개발자셋팅으로 가면 확인할 수 있음. 발급하면 해당 토큰으로 로그인 가능</p>
</li>
<li><p>ssh_keygen을 통해서 생성한 public, private key 한쌍의 키를 사용할 수 있다. public key는 private key의 값과 salt(passphrase)를 넣어 만들어지므로 한쌍이다. 클라이언트(로컬)에서 SSH 연결 초기화를 하면 서버(깃헙)에서 랜덤메시지를 생성한다. 그것을 private key로 암호화하여 다시 서버로 전송한다. 그리고 그것을 public key로 복호화하여 동일한 메시지이면 인증이되는 것이다</p>
</li>
</ol>
<p><img src="https://velog.velcdn.com/images/kyukim/post/9e20a1f2-fd02-4c7d-89db-267ab2f94e19/image.png" alt=""></p>
<p>이미지 출처: <a href="https://brunch.co.kr/@sangjinkang/52">https://brunch.co.kr/@sangjinkang/52</a></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[자바 인터페이스 default method]]></title>
            <link>https://velog.io/@kyukim/%EC%9E%90%EB%B0%94-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-default-method</link>
            <guid>https://velog.io/@kyukim/%EC%9E%90%EB%B0%94-%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4-default-method</guid>
            <pubDate>Fri, 22 Jul 2022 01:33:53 GMT</pubDate>
            <description><![CDATA[<p>자바8 에서 인터페이스에 default method 를 사용가능하게 됐다</p>
<p>코드를 보자</p>
<p><img src="https://velog.velcdn.com/images/kyukim/post/d9afd55d-24c1-458e-866a-3cf47189625d/image.png" alt="">
<img src="https://velog.velcdn.com/images/kyukim/post/a73eb9d5-48b7-4401-924e-7c927176deba/image.png" alt=""></p>
<pre><code class="language-java">public interface Foo {
    default void print() {
        System.out.println(&quot;hello world&quot;);
    }
}</code></pre>
<pre><code class="language-java">public interface Bar extends Foo{
}</code></pre>
<pre><code class="language-java">public class Kyu implements Bar {
    public void printKyu() {
        System.out.println(&quot;Kyu&quot;);
    }
}</code></pre>
<pre><code class="language-java">public class Main {
    public static void main(String[] args) {
        Kyu kyu = new Kyu();

        kyu.print();
        kyu.printKyu();
    }
}</code></pre>
<p>결과</p>
<pre><code>hello world
Kyu</code></pre><p>더불어 <code>List&lt;?&gt;</code> 에서 바로 stream을 사용할 수 있는 것은</p>
<p>extend 하고 있는 Collection이 default 메소드로 아래를 구현하고 있기 때문이다</p>
<p><img src="https://velog.velcdn.com/images/kyukim/post/4c344b28-543f-46dd-a830-43068b840ac3/image.png" alt=""></p>
]]></description>
        </item>
        <item>
            <title><![CDATA[안드로이드 fcm 푸시 알림 아이콘 설정]]></title>
            <link>https://velog.io/@kyukim/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-fcm-%ED%91%B8%EC%8B%9C-%EC%95%8C%EB%A6%BC-%EC%95%84%EC%9D%B4%EC%BD%98-%EC%84%A4%EC%A0%95</link>
            <guid>https://velog.io/@kyukim/%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-fcm-%ED%91%B8%EC%8B%9C-%EC%95%8C%EB%A6%BC-%EC%95%84%EC%9D%B4%EC%BD%98-%EC%84%A4%EC%A0%95</guid>
            <pubDate>Thu, 30 Jun 2022 04:44:26 GMT</pubDate>
            <description><![CDATA[<p>app/src/main/AndroidManifest.xml</p>
<pre><code class="language-xml">
    &lt;application

        ...생략...

        &lt;meta-data
            android:name=&quot;com.google.firebase.messaging.default_notification_icon&quot;
            android:resource=&quot;@drawable/ic_noti&quot; /&gt;

        &lt;meta-data
            android:name=&quot;com.google.firebase.messaging.default_notification_color&quot;
            android:resource=&quot;@color/white&quot; /&gt;

    &lt;/application&gt;</code></pre>
<ul>
<li>AndroidManifest에서 위와같이 메타데이터를 설정해준다</li>
</ul>
<pre><code class="language-java">public class FCMService extends FirebaseMessagingService {

    /* ...생략... */

    @Override
    public void onMessageReceived(@NonNull RemoteMessage remoteMessage) {
        super.onMessageReceived(remoteMessage);

           /* ...생략... */

        Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setSmallIcon(R.drawable.foobar) // 이 부분을 android:resource라고 해놓은 부분과 동일하게 해야함
                .setContentTitle(title)
                .setContentText(msg)
                .setAutoCancel(true)
                .setSound(defaultSoundUri)
                .setContentIntent(pendingIntent);

        mManager.notify(requestID, builder.build());
    }
}
</code></pre>
<ul>
<li>그리고 <code>setSmallIcon</code> 부분을 셋팅해준다</li>
</ul>
]]></description>
        </item>
        <item>
            <title><![CDATA[트랜잭션이란?]]></title>
            <link>https://velog.io/@kyukim/%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%9D%B4%EB%9E%80</link>
            <guid>https://velog.io/@kyukim/%ED%8A%B8%EB%9E%9C%EC%9E%AD%EC%85%98%EC%9D%B4%EB%9E%80</guid>
            <pubDate>Tue, 28 Jun 2022 01:25:38 GMT</pubDate>
            <description><![CDATA[<p>트랜잭션이란?</p>
<p>1.</p>
<p>질의를 하면, 질의가 모두 실행되거나 모두 실행되지않는 논리 작업단위</p>
<p>절차의 시작이 있고 끝이 있을 것이다</p>
<p>하나를 단위로 한 논리 블록안에 절차들이 온전히 실행되거나 모두 실행되지 않아야한다</p>
<p>2.</p>
<p>이 단위들에 대해서는 원자성이 보장되어야한다
예) 입출금</p>
<p>TPS - Transaction per second</p>
<p>3.</p>
<p>원자성 - 
데이터는 결국에 디스크에 저장이 되는데, 덩어리로 연이어서 저장되는데</p>
<p>예를들어 뭔가 셀렉할때 실제로 그 데이터를 살펴볼까? 그게 아닐수 도 있다</p>
<p>왜냐? </p>
<p>트랜잭션 내에서 데이터가 변경이 일어날때 메모리에 복사본을 만들어놓는다</p>
<p>트랜잭션이 뭔가 야기가되면 메모리에서만 바뀌는 것이고, 영속화되는 파일에서는 바뀐게아니다</p>
<p>중요한건 실패를하면 메모리에 있는걸 날린다</p>
<p>완료되면 실제로 파일에 commit을 한다.</p>
<p>파일에 write하는 게 아니라 commit한다는 것은 바로 메모리에 복사본을 만들어두고 파일에 적용시키는 원리이기 때문이다</p>
<p>그런데 이게 원자성이 완벽하게 보장되냐?</p>
<p>디비 자체에서 원자성을 보장하긴 하지만 소프트웨어는 항상 버그가 있을 수 밖에 없다
때문에 DBMS 마다 격리수준을 정할 수 있다</p>
<p> 4.</p>
<p> 격리수준 -</p>
]]></description>
        </item>
    </channel>
</rss>