<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>꾸준하게</title>
    <link>https://changha-dev.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Mon, 27 Jul 2026 11:44:19 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>changha.</managingEditor>
    <image>
      <title>꾸준하게</title>
      <url>https://tistory1.daumcdn.net/tistory/4072192/attach/49e8a7426a3742169e295e7ce36b6c51</url>
      <link>https://changha-dev.tistory.com</link>
    </image>
    <item>
      <title>인턴 생활 한 달 후기</title>
      <link>https://changha-dev.tistory.com/217</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;6월 초에 입사했으니 이제 한 달 하고 일주일 정도 지났다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;초반 1~2주는 도메인 익히는데 문서 읽는 것에 대부분 보냈다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB 조회해보고, 백오피스 기능들 보고, 직접 사용자 입장에서 작동시켜보고.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2주정도 됐을 때 백오피스 추가 기능들을 만들기 시작했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지라에서 티켓을 발급해서 FE/BE 나누어서 만들었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 개발 자체는 클로드가 만드니 어려울게 없다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 어떻게 하면 효율적으로 빠르고, 정확하게 만들 수 있을지를 고민을 많이 했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 좋았던점이 팀원들이 리뷰를 해주는 것이었는데 클로드의 코드가 난무하는 상황에서 직접 내 코드를 확인하고 코멘트를 달아주니까 더 신경을 쓰게 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ide에도 관심이 많았는데 팀원들은 대부분 cmux를 사용하시는 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;병렬 세션으로 통신이 가능하다는 이점이 있기에 여기로 넘어오신분이 많다고 들었다. (이번 주말에 Orca를 써보고 있는데 cmux의 상위버전이라는 느낌이 들었다. 특히 git 결합과 수정 파일을 클로드 앱처럼 보면서 코멘트로 달 수 있다는 것이 유용하다.)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입사하면서 한 달 동안 성장한 부분이 협업 플로우인 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;워크 트리, 태그 개념, 브랜치 전략.. 등등&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이드 플젝 할 때는 나 혼자서 거의 해서 신경 쓸 것이 없었고 예전에 팀프로젝트해도&amp;nbsp;&lt;br /&gt;큰 어려움이 없었던 것 같다. 왜냐면 hotfix, rebase, sqaush and merge, cherry pick 이런 개념을 내가 잘 모른채로 했기 때문에..ㅋㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런 상황이 잘 일어나지도 않았고..&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 깃 협업 관련해서 많이 알게되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 문서화 또한 중요하다고 생각이 들었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내가 추출한 결과물을 어떻게 사람들에게 잘 전달할 건지가 중요했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;노션을 회사에서 사용하는데 그냥 mcp연결해서 보고서 만들라고 너무 장황하고&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;간결하게 할 수 있는데도 너무 풀어서 설명하니까 글자의 파도에 먹혀 피로감이 누적되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 이거를 개선하고자 토스의 테크니컬라이팅을 참고하여 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;skill 을 만들어놓으니 훨씬 깔끔했다. (같은 인턴도 써보고 괜찮다고 해서 뿌듯..)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또 백오피스 새로운 기능이 추가되었을 때 제품팀에서 이해하기 어려운 부분이 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이거를 스크린샷 포함해서 문서화하고 싶었는데 Playwright를 사용하면 가능할 것 같아 적용하니 알잘딱깔센으로 위의 스킬과 결합하여 잘 만들어 주었다. 필요할 때 유용히 쓸 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 이번 한 달은 단순히 개발을 하는 것이 중요한게 아니라 팀원들에게 어떻게 전달할지, 업무능력을 키울 수 있을지 고민하는 시간이 되었던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;6048&quot; data-origin-height=&quot;8064&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/euMTAY/dJMcacX6pPy/mtk6qMnrr1KR8pzBzBC6J0/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/euMTAY/dJMcacX6pPy/mtk6qMnrr1KR8pzBzBC6J0/img.jpg&quot; data-alt=&quot;내 자리&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/euMTAY/dJMcacX6pPy/mtk6qMnrr1KR8pzBzBC6J0/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FeuMTAY%2FdJMcacX6pPy%2Fmtk6qMnrr1KR8pzBzBC6J0%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;496&quot; height=&quot;661&quot; data-origin-width=&quot;6048&quot; data-origin-height=&quot;8064&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;내 자리&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7월 말에 봉천역 근처로 이사도 하게 됐다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공릉을 이제 벗어나게 되는데, 오래 살기도 했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거의 안가본 카페가 없고, 근처 맛집도 많이 갔으니...&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 직장 다니니 새로운 곳으로 떠나고 싶다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 한 달은 와딩 프로젝트 앱스토어에 배포되면 유저들 모으고 피드백 받으면서 더 발전시키려고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 k8s 공부도 회사 다니다보니 이해가 필요하다고 느껴서 틈틈이 하려고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;무더운 7월도 화이팅!!&lt;/p&gt;</description>
      <author>changha.</author>
      <guid isPermaLink="true">https://changha-dev.tistory.com/217</guid>
      <comments>https://changha-dev.tistory.com/217#entry217comment</comments>
      <pubDate>Sun, 12 Jul 2026 14:15:28 +0900</pubDate>
    </item>
    <item>
      <title>DDIA 5주차</title>
      <link>https://changha-dev.tistory.com/216</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번주차는 7장이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜잭션 부분.. 정합성 측면에서 정말 중요하다고 느낀다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최근 회사에서도 트랜잭션 관련하여 오류 하나가 있었는데 더욱 중요하다고 느껴졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 장을 통해 다양한 트랜잭션이 막을 수 있는 문제들이 무엇이 있고 어떻게 해결하는지 알게되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;완화된 격리 수준&amp;nbsp;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Read Committed&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 기본적인 트랜잭션 격리 수준. 두 가지를 보장한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc; background-color: #ffffff; color: #212529; text-align: start;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;커밋된 데이터만 읽을 수 있다 (더티 읽기 방지)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;커밋 전 값과 커밋 안 된 값 둘 다 유지한다. 읽기 요청 오면 old 값 반환. 커밋 후에야 새로운 값이 보인다. -&amp;gt; 이게 undo log 버전 체인의 목적이다.&amp;nbsp; &amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;커밋된 데이터만 덮어쓸 수 있다 (더티 쓰기 방지)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Update 시 해당 행에 X Lock 잡고 커밋/롤백까지 유지한다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;뒤에 온 쓰기는 락 해제까지 대기 -&amp;gt; 커밋된 값 위에만 쓰게 된다.&amp;nbsp;&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-07-07 오후 7.41.14.png&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;162&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/yoY19/dJMcabSkA9L/tZubEkP8zDCm7a4llnJJ6k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/yoY19/dJMcabSkA9L/tZubEkP8zDCm7a4llnJJ6k/img.png&quot; data-alt=&quot;더티쓰기 예제&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/yoY19/dJMcabSkA9L/tZubEkP8zDCm7a4llnJJ6k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FyoY19%2FdJMcabSkA9L%2FtZubEkP8zDCm7a4llnJJ6k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1312&quot; height=&quot;162&quot; data-filename=&quot;스크린샷 2026-07-07 오후 7.41.14.png&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;162&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;더티쓰기 예제&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;언두로그 조심해야 할 점&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;트랜잭션이 오랜 시간 실행될 수록 언두 로그의 양은 급격히 증가할 수 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 공간적인 측면 안좋음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 스캔하는 시간 측면 안좋음&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;INSERST / UPDATE(DELETE 포함) 문장 언두 로그 각각 분리&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 전자 : MVCC에 사용 안되므로 (이전 버전에는 존재하지 않으니)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 후자 : MVCC에 사용됨&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언두 로그가 저장되는 공간 : 언두 테이블스페이스&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 언두 테이블 스페이스 : 1개 이상 128개 이하의 롤백 세그먼트&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;롤백 세그먼트 : 1개 이상의 언두 슬롯을 가짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- InnoDB 페이지 크기를 16바이트로 나눈 값의 개수만큼의 언두 슬롯을 가짐&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 트랜잭션당 언두 슬롯 최대 4개&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-07-07 오후 7.52.40.png&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;394&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bjzcRu/dJMcad3Fd0K/ibjoAGhSD361FZyMcOaNy1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bjzcRu/dJMcad3Fd0K/ibjoAGhSD361FZyMcOaNy1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bjzcRu/dJMcad3Fd0K/ibjoAGhSD361FZyMcOaNy1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbjzcRu%2FdJMcad3Fd0K%2FibjoAGhSD361FZyMcOaNy1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;402&quot; height=&quot;195&quot; data-filename=&quot;스크린샷 2026-07-07 오후 7.52.40.png&quot; data-origin-width=&quot;812&quot; data-origin-height=&quot;394&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;언두 로그 슬롯이 꽉&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;차면?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 트랜잭션을 시작할 수 없음.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;981&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xnvhh/dJMb991ml5Q/qmjM8eDSgYHtSSgULvlL8k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xnvhh/dJMb991ml5Q/qmjM8eDSgYHtSSgULvlL8k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xnvhh/dJMb991ml5Q/qmjM8eDSgYHtSSgULvlL8k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxnvhh%2FdJMb991ml5Q%2FqmjM8eDSgYHtSSgULvlL8k%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1024&quot; height=&quot;981&quot; data-origin-width=&quot;1024&quot; data-origin-height=&quot;981&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;nbsp;&lt;/h3&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Snapshot Isolation(MVCC)&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;MySQL 은 MVCC를 격리 수준마다 어떻게 사용하는지&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;각&amp;nbsp;행에&amp;nbsp;숨은&amp;nbsp;컬럼:&amp;nbsp;DB_TRX_ID(이&amp;nbsp;행&amp;nbsp;마지막&amp;nbsp;수정한&amp;nbsp;txid),&amp;nbsp;DB_ROLL_PTR(undo&amp;nbsp;log&amp;nbsp;포인터&amp;nbsp;=&amp;nbsp;옛&amp;nbsp;버전&amp;nbsp;체인).&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 수정 시 옛 버전을 undo log에 쌓음 &amp;rarr; 버전 체인.&lt;br /&gt;-&amp;nbsp;Read&amp;nbsp;View(스냅샷):&amp;nbsp;&quot;지금&amp;nbsp;어떤&amp;nbsp;txid까지&amp;nbsp;커밋된&amp;nbsp;걸로&amp;nbsp;볼지&quot;&amp;nbsp;스냅샷.&amp;nbsp;이게&amp;nbsp;가시성&amp;nbsp;판단&amp;nbsp;기준.&lt;br /&gt;-&amp;nbsp;규칙:&amp;nbsp;Read&amp;nbsp;View보다&amp;nbsp;나중&amp;nbsp;txid가&amp;nbsp;만든&amp;nbsp;버전은&amp;nbsp;안&amp;nbsp;보이고,&amp;nbsp;undo&amp;nbsp;체인&amp;nbsp;따라&amp;nbsp;보이는&amp;nbsp;옛&amp;nbsp;버전으로&amp;nbsp;내려감.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-07-03 오후 11.27.03.png&quot; data-origin-width=&quot;1596&quot; data-origin-height=&quot;312&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dbRi8P/dJMcagzjZfj/vxePA4kaAGoHW6ProFjKGK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dbRi8P/dJMcagzjZfj/vxePA4kaAGoHW6ProFjKGK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dbRi8P/dJMcagzjZfj/vxePA4kaAGoHW6ProFjKGK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdbRi8P%2FdJMcagzjZfj%2FvxePA4kaAGoHW6ProFjKGK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1596&quot; height=&quot;312&quot; data-filename=&quot;스크린샷 2026-07-03 오후 11.27.03.png&quot; data-origin-width=&quot;1596&quot; data-origin-height=&quot;312&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;RC:&amp;nbsp;문장&amp;nbsp;하나&amp;nbsp;끝나고&amp;nbsp;다른&amp;nbsp;트랜잭션이&amp;nbsp;커밋하면,&amp;nbsp;다음&amp;nbsp;SELECT는&amp;nbsp;그&amp;nbsp;값&amp;nbsp;봄&amp;nbsp;&amp;rarr;&amp;nbsp;같은&amp;nbsp;걸&amp;nbsp;두&amp;nbsp;번&amp;nbsp;읽어도&amp;nbsp;값&amp;nbsp;바뀔&amp;nbsp;수&amp;nbsp;있음.&lt;br /&gt;-&amp;nbsp;RR:&amp;nbsp;첫&amp;nbsp;SELECT&amp;nbsp;순간&amp;nbsp;스냅샷&amp;nbsp;박제&amp;nbsp;&amp;rarr;&amp;nbsp;트랜잭션&amp;nbsp;동안&amp;nbsp;남이&amp;nbsp;뭘&amp;nbsp;커밋하든&amp;nbsp;안&amp;nbsp;흔들림.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-07-03 오후 11.32.02.png&quot; data-origin-width=&quot;1584&quot; data-origin-height=&quot;1052&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bqPLOQ/dJMcajv4OgG/KiG6F81ujl049jwi84rveK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bqPLOQ/dJMcajv4OgG/KiG6F81ujl049jwi84rveK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bqPLOQ/dJMcajv4OgG/KiG6F81ujl049jwi84rveK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbqPLOQ%2FdJMcajv4OgG%2FKiG6F81ujl049jwi84rveK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;685&quot; height=&quot;455&quot; data-filename=&quot;스크린샷 2026-07-03 오후 11.32.02.png&quot; data-origin-width=&quot;1584&quot; data-origin-height=&quot;1052&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;background-color: #f3c000;&quot;&gt;SI로도 못막는 것&lt;/span&gt;&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Lost Update&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;Lost Update는 무엇인가?&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 두 트랜잭션이 같은 행을 읽고 -&amp;gt; 계산 -&amp;gt; 쓰기 하는데, 한 쪽 수정이 덮여 사라지는 것. 스냅샷 격리로도 안 먹힌다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-07-03 오후 11.46.35.png&quot; data-origin-width=&quot;1542&quot; data-origin-height=&quot;518&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bbFS8Y/dJMcaiDV13S/dX5qAQF4psyuLUcV9GX1Ak/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bbFS8Y/dJMcaiDV13S/dX5qAQF4psyuLUcV9GX1Ak/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bbFS8Y/dJMcaiDV13S/dX5qAQF4psyuLUcV9GX1Ak/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbbFS8Y%2FdJMcaiDV13S%2FdX5qAQF4psyuLUcV9GX1Ak%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1542&quot; height=&quot;518&quot; data-filename=&quot;스크린샷 2026-07-03 오후 11.46.35.png&quot; data-origin-width=&quot;1542&quot; data-origin-height=&quot;518&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;Lost Update는 어떻게 막을까?&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-07-03 오후 11.47.01.png&quot; data-origin-width=&quot;1542&quot; data-origin-height=&quot;632&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/lPR1T/dJMcadJidJr/0qp1LjNmUII59uj5skvAMk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/lPR1T/dJMcadJidJr/0qp1LjNmUII59uj5skvAMk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/lPR1T/dJMcadJidJr/0qp1LjNmUII59uj5skvAMk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FlPR1T%2FdJMcadJidJr%2F0qp1LjNmUII59uj5skvAMk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1542&quot; height=&quot;632&quot; data-filename=&quot;스크린샷 2026-07-03 오후 11.47.01.png&quot; data-origin-width=&quot;1542&quot; data-origin-height=&quot;632&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 쓰기 스큐 &amp;amp; 팬텀&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;쓰기 스큐란&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 읽어서 조건 검사 -&amp;gt; 그 결과 믿고 씀. 근데 검사~쓰기 사이 다른 트랜잭션이 전제를 깸.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 스냅샷 격리로도 못 막음.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;스냅샷 격리로도 막지 못하는 이유&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스냅샷 격리로는 서로 다른 행에 대한 동시 쓰기는 막지 못함.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;쓰기&amp;nbsp;스큐는&amp;nbsp;Alice&amp;rarr;Alice행,&amp;nbsp;Bob&amp;rarr;Bob행.&amp;nbsp;쓰는&amp;nbsp;행이&amp;nbsp;겹치지&amp;nbsp;않음&amp;nbsp;&amp;rarr;&amp;nbsp;SI&amp;nbsp;입장에선&amp;nbsp;&quot;충돌&amp;nbsp;없음&quot;&amp;nbsp;&amp;rarr;&amp;nbsp;둘&amp;nbsp;다&amp;nbsp;정상&amp;nbsp;커밋.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-07-03 오후 11.22.54.png&quot; data-origin-width=&quot;1562&quot; data-origin-height=&quot;1430&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Cuc8J/dJMcafArEyB/VA24Vt71ZVqLXkqSq5E580/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Cuc8J/dJMcafArEyB/VA24Vt71ZVqLXkqSq5E580/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Cuc8J/dJMcafArEyB/VA24Vt71ZVqLXkqSq5E580/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCuc8J%2FdJMcafArEyB%2FVA24Vt71ZVqLXkqSq5E580%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;640&quot; height=&quot;586&quot; data-filename=&quot;스크린샷 2026-07-03 오후 11.22.54.png&quot; data-origin-width=&quot;1562&quot; data-origin-height=&quot;1430&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;갱신 손실 vs 쓰기 스큐&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 갱신 손실: 두 트랜잭션이 같은 행 수정 -&amp;gt; DB가 충돌 감지 기능.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 쓰기 스큐: 같은 조건 읽고 다른 행에 씀 -&amp;gt; 충돌 없음.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;의사 당직 vs 회의실 예약 - 같은 쓰기 스큐지만 차이가 있음&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;둘 다 쓰기 스큐지만 막는 방법이 다르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;팬텀개념이 들어감.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 팬텀: 한 트랜잭션 쓰기가 다른 트랜잭션 WHERE 결과를 바꿈&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;b&gt;1. 의사 당직 &amp;mdash; (잠글 행이 있다)&lt;/b&gt;&lt;br /&gt;-&amp;nbsp;검사하는&amp;nbsp;대상:&amp;nbsp;on_call&amp;nbsp;=&amp;nbsp;true인&amp;nbsp;행들&amp;nbsp;=&amp;nbsp;Alice,&amp;nbsp;Bob.&amp;nbsp;이미&amp;nbsp;테이블에&amp;nbsp;존재함.&lt;br /&gt;-&amp;nbsp;그래서&amp;nbsp;잠글&amp;nbsp;게&amp;nbsp;있다.&lt;/p&gt;
&lt;pre id=&quot;code_1783005244577&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT * FROM doctors WHERE on_call = true FOR UPDATE;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;-&amp;nbsp;Alice가&amp;nbsp;먼저&amp;nbsp;이&amp;nbsp;락을&amp;nbsp;잡으면,&amp;nbsp;Bob은&amp;nbsp;같은&amp;nbsp;행을&amp;nbsp;잠그려다&amp;nbsp;대기한다.&lt;br /&gt;-&amp;nbsp;Alice&amp;nbsp;커밋&amp;nbsp;후&amp;nbsp;Bob이&amp;nbsp;다시&amp;nbsp;조회&amp;nbsp;&amp;rarr;&amp;nbsp;이제&amp;nbsp;당직&amp;nbsp;1명&amp;nbsp;&amp;rarr;&amp;nbsp;&quot;빠지면&amp;nbsp;안&amp;nbsp;되겠다&quot;&amp;nbsp;판단.&lt;br /&gt;-&amp;nbsp;즉,&amp;nbsp;읽는&amp;nbsp;순간&amp;nbsp;존재하는&amp;nbsp;행을&amp;nbsp;잠가버리니&amp;nbsp;다른&amp;nbsp;트랜잭션이&amp;nbsp;끼어들&amp;nbsp;틈이&amp;nbsp;없다.&amp;nbsp;FOR&amp;nbsp;UPDATE&amp;nbsp;한&amp;nbsp;줄로&amp;nbsp;해결.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. 회의실 예약 &amp;mdash; (잠글 행이 없다)&lt;/b&gt;&lt;br /&gt;- 검사하는 대상: &quot;그 시간대에 예약이 없는지&quot;. 즉 행의 부재를 확인한다.&lt;/p&gt;
&lt;pre id=&quot;code_1783005233605&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT count(*) FROM bookings
WHERE room_id = 5 AND time_range OVERLAPS ('12:00','13:00');  -- 0
-- &quot;비었네&quot; &amp;rarr; INSERT&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;-&amp;nbsp;검사&amp;nbsp;결과가&amp;nbsp;0개.&amp;nbsp;없는&amp;nbsp;행은&amp;nbsp;잠글&amp;nbsp;수&amp;nbsp;없다.&amp;nbsp;FOR&amp;nbsp;UPDATE를&amp;nbsp;걸어도&amp;nbsp;잠글&amp;nbsp;대상&amp;nbsp;자체가&amp;nbsp;없음.&lt;br /&gt;-&amp;nbsp;두&amp;nbsp;트랜잭션이&amp;nbsp;동시에&amp;nbsp;&quot;비었네&quot;를&amp;nbsp;확인&amp;nbsp;&amp;rarr;&amp;nbsp;각자&amp;nbsp;INSERT&amp;nbsp;&amp;rarr;&amp;nbsp;이중&amp;nbsp;예약.&lt;br /&gt;-&amp;nbsp;여기가&amp;nbsp;락으로&amp;nbsp;못&amp;nbsp;막는&amp;nbsp;&lt;b&gt;순수&amp;nbsp;팬텀&amp;nbsp;지점&lt;/b&gt;이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결책&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 충돌 구체화&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - 잠글 행이 없기에 미리 만들어둔다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - 방x시간 슬롯 조합을 time_slots 테이블에 전부 insert 해놓고, 예약 시 그 슬롯 행을 FOR UPDATRE로 잠금.&lt;/p&gt;
&lt;pre id=&quot;code_1783091065199&quot; class=&quot;sql&quot; data-ke-language=&quot;sql&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;SELECT * FROM time_slots
WHERE room_id=5 AND slot='12:00' FOR UPDATE;  -- 이제 잠글 실재 행 있음&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp; &amp;nbsp; - 단점 : 슬롯 조합 미리 다 생성해야 됨. 동시성 제어가 데이터 모델로 새어나옴.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;직렬성 - SSI&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSI(Serializable Snapshot Isolation) - 직렬성 스냅샷 격리&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 스냅샷 격리 성능은 그대로 두고, 직렬성까지 보장하는 방법.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 낙관적 동시성 제어. Postgres SERIALIZABLE이 이걸 씀.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;배경&amp;nbsp;&amp;mdash;&amp;nbsp;기존&amp;nbsp;직렬성&amp;nbsp;구현의&amp;nbsp;문제&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직렬성&amp;nbsp;보장&amp;nbsp;방법은&amp;nbsp;원래&amp;nbsp;셋:&lt;br /&gt;-&amp;nbsp;실제&amp;nbsp;직렬&amp;nbsp;실행&amp;nbsp;&amp;mdash;&amp;nbsp;싱글&amp;nbsp;스레드로&amp;nbsp;하나씩.&amp;nbsp;단순하지만&amp;nbsp;처리량&amp;nbsp;한계.&lt;br /&gt;-&amp;nbsp;2단계&amp;nbsp;잠금(2PL)&amp;nbsp;&amp;mdash;&amp;nbsp;읽고&amp;nbsp;쓰는&amp;nbsp;것마다&amp;nbsp;락.&amp;nbsp;확실하지만&amp;nbsp;느림.&amp;nbsp;락&amp;nbsp;대기&amp;middot;데드락&amp;nbsp;많음.&lt;br /&gt;&lt;b&gt;-&amp;nbsp;SSI&amp;nbsp;&amp;mdash;&amp;nbsp;위&amp;nbsp;둘의&amp;nbsp;단점&amp;nbsp;극복하려고&amp;nbsp;나옴.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;비관 vs 낙관 (2PL vs SSI)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 2PL(비관적): &quot;충돌 날지 모르니 미리 잠그고 기다린다.&quot; 충돌 안 나도 락 비용 냄. 느림.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- SSI(낙관적): &quot;일단 다 진행시킨다. 커밋할 때 진짜 충돌났는지 검사해서, 났으면 그 트랜잭션 취소하고 재시도.&quot;&lt;br /&gt;-&amp;nbsp;충돌이&amp;nbsp;드문&amp;nbsp;워크로드면&amp;nbsp;SSI가&amp;nbsp;훨씬&amp;nbsp;빠름.&amp;nbsp;충돌&amp;nbsp;잦으면&amp;nbsp;재시도&amp;nbsp;폭증해서&amp;nbsp;손해.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;어떻게&amp;nbsp;동작하나&amp;nbsp;(핵심&amp;nbsp;아이디어)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;-&amp;nbsp;스냅샷&amp;nbsp;격리&amp;nbsp;위에서&amp;nbsp;돎&amp;nbsp;&amp;rarr;&amp;nbsp;각&amp;nbsp;트랜잭션은&amp;nbsp;자기&amp;nbsp;시점&amp;nbsp;스냅샷을&amp;nbsp;봄&amp;nbsp;(읽기&amp;nbsp;락&amp;nbsp;없음,&amp;nbsp;빠름).&lt;br /&gt;-&amp;nbsp;문제는&amp;nbsp;쓰기&amp;nbsp;스큐:&amp;nbsp;&quot;내가&amp;nbsp;읽은&amp;nbsp;전제가&amp;nbsp;커밋&amp;nbsp;시점엔&amp;nbsp;이미&amp;nbsp;낡았을&amp;nbsp;수&amp;nbsp;있다.&quot;&lt;br /&gt;-&amp;nbsp;SSI는&amp;nbsp;이걸&amp;nbsp;감지함:&lt;br /&gt;&amp;nbsp;&amp;nbsp;-&amp;nbsp;트랜잭션이&amp;nbsp;읽은&amp;nbsp;데이터를&amp;nbsp;다른&amp;nbsp;트랜잭션이&amp;nbsp;나중에&amp;nbsp;바꿨는지&amp;nbsp;추적.&lt;br /&gt;&amp;nbsp;&amp;nbsp;-&amp;nbsp;커밋&amp;nbsp;시점에&amp;nbsp;&quot;내&amp;nbsp;판단&amp;nbsp;근거가&amp;nbsp;된&amp;nbsp;읽기가&amp;nbsp;무효화됐나?&quot;&amp;nbsp;검사.&lt;br /&gt;&amp;nbsp;&amp;nbsp;-&amp;nbsp;무효화됐으면&amp;nbsp;=&amp;nbsp;직렬&amp;nbsp;실행이었다면&amp;nbsp;불가능한&amp;nbsp;결과&amp;nbsp;&amp;rarr;&amp;nbsp;abort.&lt;br /&gt;- 즉 &quot;낡은 전제 위에서 커밋하려는 트랜잭션&quot;을 잡아 죽인다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;장단점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;nbsp;장점:&amp;nbsp;읽기에&amp;nbsp;락&amp;nbsp;안&amp;nbsp;검(스냅샷&amp;nbsp;격리&amp;nbsp;속도)&amp;nbsp;+&amp;nbsp;직렬성&amp;nbsp;보장.&amp;nbsp;한&amp;nbsp;트랜잭션이&amp;nbsp;다른&amp;nbsp;걸&amp;nbsp;블록&amp;nbsp;안&amp;nbsp;함.&lt;br /&gt;-&amp;nbsp;단점:&amp;nbsp;커밋&amp;nbsp;실패(abort)&amp;nbsp;&amp;rarr;&amp;nbsp;재시도&amp;nbsp;로직&amp;nbsp;필요.&amp;nbsp;충돌&amp;nbsp;잦으면&amp;nbsp;abort&amp;nbsp;비율&amp;uarr;&amp;nbsp;성능&amp;darr;.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;회사에서 생긴 일&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이슈 - @Modifying(clearAutomatically=true) 영속성 컨텍스트 유실&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;문제 상황&lt;/p&gt;
&lt;pre id=&quot;code_1783088205778&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;// 1. 엔티티 저장 (영속성 컨텍스트에 올라감)
val classAttend = buildAttend(command, course, nowDate, isWaiting, 0)
classAttendProvider.save(classAttend)

// 2. 쿠폰 컬럼 set 후 2차 save (아직 DB 반영 전, 컨텍스트에만 dirty 상태)
if (command.couponSeq != null) {
    classAttend.couponSeq = command.couponSeq
    ...
    classAttendProvider.save(classAttend)
}

// 3. 벌크 UPDATE
@Modifying(clearAutomatically = true)
@Query(&quot;update LivePaymentJpaEntity e set e.attendSeq = :attendSeq where e.merchantUid = :orderId&quot;)
fun updateAttendSeqByMerchantUid(...)
&amp;rarr; 3번 실행 시 앞서 변경한 영속성 컨텍스트 내용이 날아감.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;br /&gt;해결&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1783088273012&quot; class=&quot;kotlin&quot; data-ke-language=&quot;kotlin&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Modifying(clearAutomatically = true, flushAutomatically = true)
- flushAutomatically = true &amp;rarr; 벌크 실행 전에 대기 중인 변경을 DB로 flush.
- 순서 바뀜: flush(dirty&amp;rarr;DB) &amp;rarr; 벌크 UPDATE &amp;rarr; clear. 유실 없음.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접적으로 이번 장과 연관 있는 격리 문제는 아니지만&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 지금 어느 계층에서 어떤 상태인지 파악하는 것이 비슷해서 가져왔다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 장은 트랜잭션으로 막을 수 있는 어떤 문제들이 있는지 파악했고 특히 회의실 예약 예제에서 충돌 구체화는 평소 생각하지 못했던 거라 알게 되어 좋았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>changha.</author>
      <guid isPermaLink="true">https://changha-dev.tistory.com/216</guid>
      <comments>https://changha-dev.tistory.com/216#entry216comment</comments>
      <pubDate>Fri, 3 Jul 2026 23:47:12 +0900</pubDate>
    </item>
    <item>
      <title>DDIA 4주차</title>
      <link>https://changha-dev.tistory.com/215</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번주차는 6장과 7장이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;7장은 추가 문서로 나중에 다시 쓰겠다.(6장만 이번주 하는 줄 착각했다..ㅎ)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;6장 - 파티셔닝&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;왜 파티셔닝하나&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복제가 같은 데이터를 여러 노드에 복사하는 것이라면, 파티셔닝은 데이터를 쪼개서 노드마다 다른 조각을 두는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적은 확장성. 실제 시스템에서는 복제와 파티셔닝을 결합해서 쓴다. (각 파티션을 다시 여러 노드에 복재해 장애 내성까지 확보한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심 목표:&amp;nbsp; 부하를 고르게 분산하는 것. 이게 깨지면 skew(쏠림) 이며, 부하가 몰린 파티션은 hot spot.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;기본키 기준 파티셔닝 (키-값 데이터를 나누는 방식)&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-06-26 오후 11.17.01.png&quot; data-origin-width=&quot;1334&quot; data-origin-height=&quot;350&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/baHwv3/dJMcabxUG1D/gU8WXD1Z8xnvAwY2eDqqh1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/baHwv3/dJMcabxUG1D/gU8WXD1Z8xnvAwY2eDqqh1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/baHwv3/dJMcabxUG1D/gU8WXD1Z8xnvAwY2eDqqh1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbaHwv3%2FdJMcabxUG1D%2FgU8WXD1Z8xnvAwY2eDqqh1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1334&quot; height=&quot;350&quot; data-filename=&quot;스크린샷 2026-06-26 오후 11.17.01.png&quot; data-origin-width=&quot;1334&quot; data-origin-height=&quot;350&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시 방식의 절충안이 &lt;b&gt;복합 키&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) 카산드라는 키의 앞부분으로 해시 분산, 뒷부분으로 파티션 안에서 정렬을 유지한다. (한 사용자의 이벤트를 시간순으로 하면 범위 스캔으로 풀림)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시로도 못 막는 것 - 단일 hot key.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ex) 특정 유명인의 게시글에 팔로워가 몰리는 상황.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해시를 해도 그 키는 한 파티션에 묶이게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 애플리케이션이 키에 랜덤 prefix/suffix를 붙여 인위적으로 쪼개는 식으로 대응해야한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(tradeoff &amp;lt;-&amp;gt;&amp;nbsp; 읽을 때 여러 키를 합쳐야 한다.)&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;보조 인덱스 파티셔닝&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보조 인덱스는 PK가 아닌 값으로 검색을 위한 것이다. 이 인덱스도 파티셔닝되면 나눠지게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-06-26 오후 11.22.20.png&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;388&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ewhS7Z/dJMcaccxOWt/LG0D9VKjGu9tYWrLK0oVM1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ewhS7Z/dJMcaccxOWt/LG0D9VKjGu9tYWrLK0oVM1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ewhS7Z/dJMcaccxOWt/LG0D9VKjGu9tYWrLK0oVM1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FewhS7Z%2FdJMcaccxOWt%2FLG0D9VKjGu9tYWrLK0oVM1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1312&quot; height=&quot;388&quot; data-filename=&quot;스크린샷 2026-06-26 오후 11.22.20.png&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;388&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TgU0X/dJMcahycyNF/4gzJflufDHhe6eolfzWFSK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TgU0X/dJMcahycyNF/4gzJflufDHhe6eolfzWFSK/img.png&quot; data-origin-width=&quot;878&quot; data-origin-height=&quot;450&quot; data-is-animation=&quot;false&quot; width=&quot;499&quot; height=&quot;256&quot; style=&quot;width: 45.5596%; margin-right: 10px;&quot; data-widthpercent=&quot;46.1&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TgU0X/dJMcahycyNF/4gzJflufDHhe6eolfzWFSK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTgU0X%2FdJMcahycyNF%2F4gzJflufDHhe6eolfzWFSK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;878&quot; height=&quot;450&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/kXIbZ/dJMcaiDSbkk/D6qvxPjivI5bwJrtR40hT1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/kXIbZ/dJMcaiDSbkk/D6qvxPjivI5bwJrtR40hT1/img.png&quot; data-origin-width=&quot;1280&quot; data-origin-height=&quot;561&quot; data-is-animation=&quot;false&quot; style=&quot;width: 53.2776%;&quot; data-widthpercent=&quot;53.9&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/kXIbZ/dJMcaiDSbkk/D6qvxPjivI5bwJrtR40hT1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FkXIbZ%2FdJMcaiDSbkk%2FD6qvxPjivI5bwJrtR40hT1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1280&quot; height=&quot;561&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로컬 인덱스는 쓰기를 한 곳에 모아 쓰기 증폭을 줄이는 대신 읽기를 분산해서 가져와야된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;글로벌 인덱스는 읽기를 모으는 대신 쓰기를 증폭시킨다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;점선은 쓰기 흐름. 문서 893 저장할 때 따라가는 경로 (파티션 0으로도 가야됨)&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;리밸런싱&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터,노드가 변하면 파티션을 재배치해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 부하가 다시 고르게 분산된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 옮기는 데이터가 최소이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 리밸런싱 중에도 서비스가 계속되는 것.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특징&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;해시 mod N은 쓰면 안된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;N이 바뀌면 모든 키의 소속이 바뀌어 이동 비용이 크다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;고정 개수 파티션
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노드 수보다 훨씬 많은 파티션을 미리 만들어 노드당 여러 개를 할당&lt;/li&gt;
&lt;li&gt;장점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이동 최소: 노드 추가/제거 시 파티션 통째로 이사. 키 재해싱 x&lt;/li&gt;
&lt;li&gt;운영 단순: 키 -&amp;gt; 파티션 매핑 불변.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;이종 노드 대응: 성능 좋은 서버엔 파티션 많이, 약한 서버엔 적게 할당 가능.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;372&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/beOCoT/dJMcahLIqtV/H1qVCasDWmVwKnU8b3aVek/img.webp&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/beOCoT/dJMcahLIqtV/H1qVCasDWmVwKnU8b3aVek/img.webp&quot; data-alt=&quot;고정 개수 파티션 이미지&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/beOCoT/dJMcahLIqtV/H1qVCasDWmVwKnU8b3aVek/img.webp&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbeOCoT%2FdJMcahLIqtV%2FH1qVCasDWmVwKnU8b3aVek%2Fimg.webp&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;663&quot; height=&quot;372&quot; data-origin-width=&quot;663&quot; data-origin-height=&quot;372&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;고정 개수 파티션 이미지&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;동적 파티셔닝
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파티션이 너무 커지면 split, 작아지면 merge.&lt;/li&gt;
&lt;li&gt;초기 빈 DB의 단일 파티션 쏠림을 막으려면 pre-splitting(사전 분할)을 씀.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;pre-splitting: 첫 데이터 들어오기 전에 파티션 경계를 미리 그어두는 것.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;노드 수 비례
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;노드 당 고정 개수의 파티션.&lt;/li&gt;
&lt;li&gt;노드를 추가하면 기존 파티션을 무작위로 쪼개 가져간다.&amp;nbsp;&lt;/li&gt;
&lt;li&gt;장점
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;파티션 크기 안정: 노드 늘면 파티션도 늘어 -&amp;gt; 파티션당 데이터량 일정 유지&lt;/li&gt;
&lt;li&gt;데이터 양 예측 불필요: 고정 개수는 처음에 파티션 몇 개 할 건지 잘 정해야 함. 이거는 노드 수에 자동 연동&lt;/li&gt;
&lt;li&gt;확장 자연스러움: 노드 추가 = 새 노드가 기존 파티션 몇 개를 무작위로 쪼개 가져감.&amp;nbsp;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;요청 라우팅&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 키는 어디에 있나를 클라이언트가 알아야된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세가지 접근&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 아무 노드에나 보내면 그 노드가 적절한 노드로 포워딩&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 라우팅 계층을 둔다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 클라이언트가 직접 파티션 배치를 앎.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 배치 정보 합의를 위해 Zookeeper같은 코디네이션 서비스를 쓰거나,&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gossip 프로토콜로 노드끼리 상태를 퍼트린다.(카산드라)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gossip 프로토콜: 노드끼리 소문내서 클러스터 매핑 공유, 중앙 조정자 없이 라우팅 함.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;느낀점&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핫스팟 키 부분이 흥미로웠다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DB단이 아니더라도 서버단에서도 충분히 일어날 수 있는 일인데&amp;nbsp;&lt;br /&gt;랜덤으로 prefix/suffix 로 추가하는 것이 간단하고 이해가 바로 됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 복합키를 통해 분산과 정렬 이점을 챙기는 방법도 처음 알게 되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 장은 아는 DB 지식을 조합하여 해결할 수 있다는 것을 느끼게 한 장이었다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;</description>
      <author>changha.</author>
      <guid isPermaLink="true">https://changha-dev.tistory.com/215</guid>
      <comments>https://changha-dev.tistory.com/215#entry215comment</comments>
      <pubDate>Fri, 26 Jun 2026 23:31:40 +0900</pubDate>
    </item>
    <item>
      <title>DDIA 3주차</title>
      <link>https://changha-dev.tistory.com/214</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이번 주차는 꽤 재밌는 5장, 복제이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;여기부터 본격적인 분산환경에서 발생하는 문제들을 다루고 해결책을 설명한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;복제가 필요한 이유 &lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/h3&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;지연 시간 감소 - 데이터를 사용자와 지리적으로 가깝게 둔다.&lt;/li&gt;
&lt;li&gt;내결함성 - 일부 노드가 고장 나도 시스템이 계속 동작하도록 한다. &amp;nbsp;&lt;/li&gt;
&lt;li&gt;확장성 - 읽기 질의를 처리하는 장비 수를 늘려 확장한다.&amp;nbsp;&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터가 안 변하면 복제는 그냥 한 번 복사하면 끝이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복제가 어려운 건 변경을 어떻게 전파하느냐의 문제가 있기 때문이다.&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;문제와 해결&lt;/span&gt;&lt;/h2&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR'; color: #000000;&quot;&gt;오래된 값일 때 어떻게 해야될까?&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;문제는 리더의 변경이 팔로워에 즉시 도달하지 않는다는 데 있다. 리더에 쓴 내용이 팔로워에 반영되기까지의 시간차를 &lt;b&gt;복제 지연&lt;/b&gt;이라 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;&lt;span style=&quot;color: #000000;&quot;&gt;이 시스템이 보장하는 것은 최종적 일관성 뿐이다. 쓰기를 멈추면 팔로워들은 언젠가는 리더를 따라잡지만, 그 갭이 생각보다 클 수도 있다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;아래 세 가지 문제는 모두 이 복제 지연 하나에서 갈라져 나온다. 각각이 깨뜨리는 보장이 다르고, 그래서 해결책도 다르다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;  문제 1 - 내가 방금 쓴 글이 새로고침하니까 사라졌다.&lt;/b&gt;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;상황:&lt;/b&gt; 사용자가 자신의 프로필을 수정(리더에 쓰기)하고 즉시 새로고침(팔로워에서 읽기)을 한다. 하지만 팔로워에 아직 변경사항이 반영되지 않아 사용자는 수정전의 옛날 정보를 본다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;390&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xGhN7/dJMcadiaWGR/ZWSidKJWhgwRN4K87Us9W0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xGhN7/dJMcadiaWGR/ZWSidKJWhgwRN4K87Us9W0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xGhN7/dJMcadiaWGR/ZWSidKJWhgwRN4K87Us9W0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FxGhN7%2FdJMcadiaWGR%2FZWSidKJWhgwRN4K87Us9W0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;922&quot; height=&quot;390&quot; data-origin-width=&quot;922&quot; data-origin-height=&quot;390&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #f6e199; color: #000000;&quot;&gt;해결책: 쓰기 후 읽기 일관성&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;방법 1 - 사용자가 수정한 내용을 읽을 때는 리더에서 읽는다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;보통 마이페이지나 프로필 정보는 소유자만 읽는다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;항상 사용자 프로필은 리더에서 읽고 다른 사용자의 프로필은 팔로워에서 읽는 방식.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;방법 2 - 마지막 갱신 시각을 찾아서 마지막 갱신 후 1분 동안은 리더에서 모든 읽기 수행.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;팔로워에서는 복제 지연을 모니터링해서 리더보다 1분 이상 늦은 모든 팔로워에 대한 질의를 금지한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;방법 3 - 복제 서버가 아직 최신이 아니면 다른 복제 서버가 읽기를 처리한다. 또는 복제 서버가 따라잡을 때까지 질의 대기.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;클라이언트는 가장 최근 타임스탬프 기억 가능하다. 시스템은 사용자 읽기를 위한 복제 서버가 최소한 해당 타임스탬프까지 갱신을 반영하게 할 수 있다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  문제 2 - 시간이 거꾸로 흘러간다.&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;b&gt;상황:&amp;nbsp;&lt;/b&gt;사용자가&amp;nbsp;페이지를&amp;nbsp;여러&amp;nbsp;번&amp;nbsp;새로고침한다.&amp;nbsp;첫&amp;nbsp;읽기는&amp;nbsp;빠른&amp;nbsp;팔로워&amp;nbsp;A에&amp;nbsp;도달해&amp;nbsp;최신&amp;nbsp;글을&amp;nbsp;봤는데,&amp;nbsp;두&amp;nbsp;번째&amp;nbsp;읽기는&amp;nbsp;느린&amp;nbsp;팔로워&amp;nbsp;B에&amp;nbsp;도달해&amp;nbsp;다시&amp;nbsp;옛날&amp;nbsp;상태로&amp;nbsp;돌아간다.&amp;nbsp;사용자&amp;nbsp;눈에는&amp;nbsp;시간이&amp;nbsp;거꾸로&amp;nbsp;가는&amp;nbsp;것처럼&amp;nbsp;보인다.&lt;/span&gt;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;문제&amp;nbsp;1과&amp;nbsp;달리,&amp;nbsp;여기서&amp;nbsp;깨지는&amp;nbsp;건&amp;nbsp;&quot;내가&amp;nbsp;쓴&amp;nbsp;것&quot;이&amp;nbsp;아니라&amp;nbsp;읽기들&amp;nbsp;사이의&amp;nbsp;시간&amp;nbsp;순서다.&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;936&quot; data-origin-height=&quot;463&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bN9kiS/dJMcaiDLgEq/xtkNKj4q9m70eu9yVqZVHK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bN9kiS/dJMcaiDLgEq/xtkNKj4q9m70eu9yVqZVHK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bN9kiS/dJMcaiDLgEq/xtkNKj4q9m70eu9yVqZVHK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbN9kiS%2FdJMcaiDLgEq%2FxtkNKj4q9m70eu9yVqZVHK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;936&quot; height=&quot;463&quot; data-origin-width=&quot;936&quot; data-origin-height=&quot;463&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #f6e199; color: #000000;&quot;&gt;해결책: 단조 읽기&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;한 번 최신 데이터를 본 사용자는, 그 뒤로 더 오래된 데이터를 보지 않도록 보장한다. (시간이 뒤로 가지 않게)&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;방법 - 각 사용자의 읽기가 항상 동일한 복제 서버에서 수행되게 한다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;사용자 ID의 해시를 기반으로 복제 서버를 고정한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;  문제 3 - 답변이 질문보다 먼저 올라왔다.&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;상황:&lt;/b&gt; 데이터가 여러 샤드에 나뉘어 저장돼 있다. A가 질문을 쓰고, B가 그에 답변을 단다. 그런데 관찰자에게는 B의 답변이 담긴 샤드가 먼저 복제되고 A의 질문이 담긴 샤드가 나중에 복제되어, 답변이 질문보다 먼저 보인다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;질문과 답변 사이에 인과성이 있는데, 그 인과 순서가 뒤집힌 것이다. (샤드별로 복제 지연이 달라 생기는 문제)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;796&quot; data-origin-height=&quot;491&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bkux7P/dJMcaaFMnL9/nIyWDHr9uiC1jDsQmUKNmk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bkux7P/dJMcaaFMnL9/nIyWDHr9uiC1jDsQmUKNmk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bkux7P/dJMcaaFMnL9/nIyWDHr9uiC1jDsQmUKNmk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbkux7P%2FdJMcaaFMnL9%2FnIyWDHr9uiC1jDsQmUKNmk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;796&quot; height=&quot;491&quot; data-origin-width=&quot;796&quot; data-origin-height=&quot;491&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #f6e199; color: #000000;&quot;&gt;해결책: 일관된 순서로 쓰기&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000;&quot;&gt;인과성이 있는 쓰기들은 항상 쓰여진 순서대로 읽히도록 보장한다.&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;방법 - 서로 인과관계가 있는 쓰기는 동일한 샤드에 기록되게 한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;이렇게 하면 한 샤드 안에서는 순서가 보존되므로 역전이 일어나지 않는다.&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;한 샤드로 들어온다는 것 : 단일 리더 + 순서 로그 + 순차 적용&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;color: #000000; text-align: start;&quot;&gt;정리&lt;/span&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-06-19 오후 11.26.38.png&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;314&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/WXRMe/dJMcafmLV5R/pjsmxyVrDtMIiZdD8S62u1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/WXRMe/dJMcafmLV5R/pjsmxyVrDtMIiZdD8S62u1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/WXRMe/dJMcafmLV5R/pjsmxyVrDtMIiZdD8S62u1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FWXRMe%2FdJMcafmLV5R%2FpjsmxyVrDtMIiZdD8S62u1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1126&quot; height=&quot;314&quot; data-filename=&quot;스크린샷 2026-06-19 오후 11.26.38.png&quot; data-origin-width=&quot;1126&quot; data-origin-height=&quot;314&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;느낀점&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서도 단일 DB로 이루어진것이 아니라 read/write DB로 이루어져있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;과거 단일 DB로 했던 적이 있는데, 이 때 SPOF문제로 DDos로 DB CPU가 치솟았을 때 웹서버까지 장애가 전파됐었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;read/write의 구분은 readOnly 트랜잭션은 조회 전용 DB로 보내서 구분하고 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그러면 동기 or 비동기 중에 무엇을 택했나 하면&amp;nbsp;비동기 방식을 택했다. (유저 알람 배치 복제 지연 개선)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 비동기는 복제 지연이 생기게 되는데, 실제로 지연이 7~8초로 터졌었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 해결하기 위해 쓰기 배치 단위를 작게 쪼개고, Insert/Update를 별도 Kafka 토픽으로 비동기 2단계 분리 하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;복제 지연 목표는 1~2초 이내였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 문제가 없냐 하면 아니다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 2단계 분리를 하였다고했는데 Insert(알림 저장), Update(뱃지 카운트) 부분에서 새로운 정합성 문제를 만들게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;뱃지 숫자와 실제 알림이 안 맞는 구간이 생길 수 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;예를 들어,&lt;span&gt; 알림 목록엔 떴는데 뱃지는 아직 0, 또는 반대. 이 부분이 위에서 기술한 인과성 문제랑 일치하는 것 같다. (알림 생성 -&amp;gt; 뱃지 증가)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;&lt;span&gt;다만 이정도는 허용가능한 불일치라서 진행했을 것 같다. &lt;/span&gt;&lt;/span&gt;&lt;/p&gt;</description>
      <author>changha.</author>
      <guid isPermaLink="true">https://changha-dev.tistory.com/214</guid>
      <comments>https://changha-dev.tistory.com/214#entry214comment</comments>
      <pubDate>Fri, 19 Jun 2026 23:27:03 +0900</pubDate>
    </item>
    <item>
      <title>DDIA 2주차</title>
      <link>https://changha-dev.tistory.com/213</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;이번 주차는 4장 부호화와 발전에 대해 알아보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style1&quot;&gt;&lt;span style=&quot;font-family: 'Noto Serif KR';&quot;&gt;&amp;nbsp;애플리케이션은 필연적으로 시간에 따라 변한다.&amp;nbsp;&lt;/span&gt;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 문장으로 시작한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;입사한지 일주일정도 돼서 먼저 기존 소스코드를 파악하는 데 대부분의 시간을 보내고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 다시 소스코드나 ERD 구조를 하나하나 깊게 보는 것은 정말 오랜만이었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보면서 느낀게 위 첫문장과 일치하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필드가 추가되기도 하고 어떤 테이블은 이제 사용하지 않기도 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 장에서는 이 변화에 대응하기 위해 어떻게 할 수 있을지 해결책을 알려주었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 호환성 개념을 알아야 됐다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하위 호환성: 새로운 코드는 예전 코드가 기록한 데이터를 읽을 수 있어야 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상위 호환성: 예전 코드는 새로운 코드가 기록한 데이터를 읽을 수 있어야 한다. (이게 더 어렵다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;크게 1부와 2부로 나눌 수 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1부는 부호화 형식들에 대해 비교하였고 2부는 데이터 플로, 즉 프로세스간 데이터를 전달하는 어떤 방법이 있는지 소개하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 회사 문서에서 ProtoBuf나 Avro에 대해 언급한 적이 있어서 이 쪽에 대해 좀 더 알아보았다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1부: 부호화 형식 비교&amp;nbsp;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;메모리 속 객체를 바이트열로 바꾸는 방법들을 호환성 관점에서 바라보았다. (이를 부호화라고 한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;- 언어 내장 직렬화&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;언어 종속적인 문제, 보안 문제, 버전 관리의 부재&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;- 텍스트 형식&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사람이 읽기 좋고 널리 쓰이지만, 숫자 정밀도 문제(json의 큰 정수), 이진 데이터 미지원, 스키마가 선택적이라는 약점&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;숫자 정밀도 문제&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- XML과 CSV는 숫자 123과 문자열 &quot;123&quot;을 구분할 수 없다. (외부 스키마 없이는)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- JSON에서는 2^53보다 큰 정수부터는 정확히 표현이 안됨(JavaScript에서 파싱하는 순간 값이 미세하게 변함)&lt;/p&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ceZSvU/dJMcabLnLqc/Bv9pJpoSZdl9Myr7bjommK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ceZSvU/dJMcabLnLqc/Bv9pJpoSZdl9Myr7bjommK/img.png&quot; style=&quot;width: 50.8715%; margin-right: 10px;&quot; width=&quot;607&quot; height=&quot;276&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;596&quot; data-filename=&quot;스크린샷 2026-06-13 오전 12.24.35.png&quot; data-widthpercent=&quot;51.5&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ceZSvU/dJMcabLnLqc/Bv9pJpoSZdl9Myr7bjommK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FceZSvU%2FdJMcabLnLqc%2FBv9pJpoSZdl9Myr7bjommK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1312&quot; height=&quot;596&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bnr9Hb/dJMb99Ufwb1/W2IlLAnYp8WxERNaLkk46K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bnr9Hb/dJMb99Ufwb1/W2IlLAnYp8WxERNaLkk46K/img.png&quot; style=&quot;width: 47.906%;&quot; width=&quot;553&quot; height=&quot;267&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1306&quot; data-origin-height=&quot;630&quot; data-filename=&quot;스크린샷 2026-06-13 오전 12.24.52.png&quot; data-widthpercent=&quot;48.5&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bnr9Hb/dJMb99Ufwb1/W2IlLAnYp8WxERNaLkk46K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbnr9Hb%2FdJMb99Ufwb1%2FW2IlLAnYp8WxERNaLkk46K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1306&quot; height=&quot;630&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;그래서&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-06-13 오전 12.26.42.png&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;1080&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/SAZmR/dJMcaiwUejL/33sfKzKjc16rUXkmXYNT91/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/SAZmR/dJMcaiwUejL/33sfKzKjc16rUXkmXYNT91/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/SAZmR/dJMcaiwUejL/33sfKzKjc16rUXkmXYNT91/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSAZmR%2FdJMcaiwUejL%2F33sfKzKjc16rUXkmXYNT91%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;617&quot; height=&quot;508&quot; data-filename=&quot;스크린샷 2026-06-13 오전 12.26.42.png&quot; data-origin-width=&quot;1312&quot; data-origin-height=&quot;1080&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이진 데이터 미지원&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 텍스트 형식이라, 이미지나 암호화된 바이트 같은 이진 데이터는 담을 수 없음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Base64로 인코딩해서 문자열로 넣는 건 가능 (다만 데이터 크기 33% 증가)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스키마 선택적&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- XML Schema, JSON Schema 존재하긴 하지만 실제로는 잘 안쓰는 곳이 많다고 함&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;- 이진 스키마 기반 형식&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스키마를 명시적으로 정의하고, 필드 태그 번호(Thrift/Protocol Buffers)나 읽기&amp;bull;쓰기 스키마 분리(Avro)를 통해 스키마를 안전하게 발전 시킬 수 있다. (스키마 발전이란: 스키마는 필연적으로 시간이 지남에 따라 변한다)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 각 프레임워크의 차이점과 언제 써야되는지 궁금했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;1. Thirft / Protocol Buffers&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;스키마에 각 필드마다 태그 번호를 명시한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 태그 번호는 절대 바꾸면 안된다. (이유: 부호화된 바이트열에는 필드 이름이 없고 태그 번호만 있어서, 태그 번호가 필드의 정체성)&lt;/p&gt;
&lt;pre id=&quot;code_1781270337146&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# thrift
struct Person {
  1: required string userName,
  2: optional i64 favoriteNumber,
  3: optional list&amp;lt;string&amp;gt; interests
}

# protocol buffers
message Person {
  required string user_name = 1; // 정확히 1번
  optional int64 favorite_number = 2; // 0번 또는 1번
  repeated string interests = 3; // 0번 이상 몇 번이든
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부호화된 바이트열에는 필드 이름이 아니라 태그 번호가 들어간다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;두 프레임워크 큰 차이는 없고 Protocol Buffers는 리스트 전용 타입이 없고 repeated표시자를 쓴다는 것 정도이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 필드 이름 변경: 자유&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 새 필드 추가:&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 새 코드 -&amp;gt; (읽기) 옛 데이터 : 뒤를 돌아봄 = 하위 호환(required 금지)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 옛 코드 -&amp;gt; (읽기) 새 데이터 : 앞을 내다봄 = 상위 호환(모르는 태그 건너뛰기로 자동)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 필드 삭제: optional 필드만 삭제 가능하고, 그 태그 번호는 영원히 재사용 금지&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;2. Avro (하둡 생태계에서 출발)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특이점: 태그 번호가 없음&lt;/p&gt;
&lt;pre id=&quot;code_1781270777534&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;record Person {
  string userName;
  union { null, long } favoriteNumber = null;
  array&amp;lt;string&amp;gt; interests;
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;부호화된 바이트열에는 태그, 필드 이름, 타입 정보 없음&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단지 &lt;b&gt;값들만 스키마에 정의된 순서대로&lt;/b&gt; 붙어 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;유니온&amp;nbsp;타입이란:&amp;nbsp;&quot;이&amp;nbsp;필드는&amp;nbsp;이&amp;nbsp;타입들&amp;nbsp;중&amp;nbsp;하나일&amp;nbsp;수&amp;nbsp;있다.&quot;고&amp;nbsp;선언하는&amp;nbsp;방식.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;pre id=&quot;code_1781271734504&quot; class=&quot;d&quot; style=&quot;background-color: #f8f8f8; color: #383a42; text-align: start;&quot; data-ke-type=&quot;codeblock&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;union { null, long } favoriteNumber = null;
해석 -&amp;gt; favoriteNumber는 null이거나 long이다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;왜 필요한가?&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;Thirft나 Protobuf는 optional/required 표시자가 있지만, Avro는 없음&amp;nbsp;&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;대신 더 엄격한 원칙이 있음 -&amp;gt; null도 하나의 타입이다.&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Protobuf/Thrift 에서는 어떤 필드든 그냥 비워둘 수 있음(optional이면)&lt;/li&gt;
&lt;li&gt;Avro에서는 필드 타입이 long이라고 선언했으면 무조건 long 이어야 함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;null을 포함하고 싶으면 유니온에 null을 명시적으로 포함시켜야 함
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Thirft/Protobuf optional: 필드가 바이트열에서 빠질 수 있음 -&amp;gt; 결과적으로 null 처럼 보임&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Avro union with null: 필드는 항상 있지만 그 값이 null일 수 있음&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;그래서&lt;/p&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;유니온 타입은 optional을 표현하는 수단이자, 한 필드에 여러 타입을 허용하는 장치.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;읽기 스키마와 쓰기 스키마&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;쓰기 스키마&lt;/b&gt;: 그 바이트열을 &lt;b&gt;만들 당시&lt;/b&gt; 부호화에 사용된 스키마. 데이터와 함께 과거에 박제된 것이라, 지금 와서 바꿀 수 없습니다. &quot;이 바이트열이 어떤 구조로 쓰였는가&quot;의 답.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;읽기 스키마&lt;/b&gt;: 지금 읽으려는 &lt;b&gt;애플리케이션 코드가 기대하는&lt;/b&gt; 스키마. 코드에 컴파일되어 있는 것. &quot;내 코드가 어떤 모양의 객체를 원하는가&quot;의 답.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Avro는 바이트열에 아무것도 없이 값만 나열되어 있어서, 쓰기 스키마를 정확히 알아야 어디서 바이트들을 끊어 읽을지 알 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 쓰기 스키마로 바이트열을 해석하고&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 읽기 스키마로 비교해서 변환한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;문제점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;쓰기 스키마를 어떻게 전달하느냐&lt;/b&gt;는 문제가 생긴다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;해결책1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상황: 많은 레코드가 있는 대용량 파일일 때&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 레코드가 같은 스키마인 경우여서 파일 맨 앞에 쓰기 스키마를 딱 한 번만 넣으면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Avro가 이를 위해 표준화한 파일 형식이 &lt;b&gt;객체 컨테이너 파일이다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;해결책2&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상황: 개별적으로 기록된 레코드를 가진 데이터베이스&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;각각 레코드들이 서로 다른 시점에 서로 다른 버전으로 쓰였을 때는 해결책1번이 유효하지 않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때는 아래처럼 해결&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1082&quot; data-origin-height=&quot;684&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dqXi6J/dJMb99NrtpX/6lLnz367VDiPLTrYBeKRS0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dqXi6J/dJMb99NrtpX/6lLnz367VDiPLTrYBeKRS0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dqXi6J/dJMb99NrtpX/6lLnz367VDiPLTrYBeKRS0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdqXi6J%2FdJMb99NrtpX%2F6lLnz367VDiPLTrYBeKRS0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1082&quot; height=&quot;684&quot; data-origin-width=&quot;1082&quot; data-origin-height=&quot;684&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이런식으로 레지스트리에 스키마를 등록해서 컨슈머 쪽에서 조회해서 사용하도록 하면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 스키마는 캐싱해서 사용할 수 있으니 I/O 비용은 첫 조회때만 발생한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;해결책3&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상황: 두 프로세스가 지속적인 양방향 연결로 통신하는 경우&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;연결을 맺는 시점에 &quot;우리 이 스키마 버전으로 대화하자&quot;고 합의, 연결이 살아있는 동안 그 스키마를 쓰면 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Avro RPC가 이렇게 동작함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>changha.</author>
      <guid isPermaLink="true">https://changha-dev.tistory.com/213</guid>
      <comments>https://changha-dev.tistory.com/213#entry213comment</comments>
      <pubDate>Fri, 12 Jun 2026 23:39:40 +0900</pubDate>
    </item>
    <item>
      <title>DDIA 1주차</title>
      <link>https://changha-dev.tistory.com/212</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 중심 애플리케이션 설계 1,2,3장&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용이 꽤 많아 개인적으로 인상적인 부분들 위주로 작성하였다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1장 - 신뢰할 수 있고 확장 가능하며 유지보수하기 쉬운 애플리케이션&amp;nbsp;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기억에 남는 문구&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;단일 도구로는 더 이상 데이터 처리와 저장 모두를 만족시킬 수 없는 과도하고 광범위한 요구사항을 가지고 있다.&amp;nbsp;&lt;br /&gt;이를 해결하기 위해 작업은 단일 도구에서 효율적으로 수행할 수 있는 테스크로 나누고 다양한 도구들을 애플리케이션 코드를 이용해서 서로 연결한다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비동기 테스크 - Message Queue&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검색 요청 - Elastic Search&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 요청 - Cache&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;느낀점:&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하나의 도구로 모든 상황을 해결하려고 하는 것이 아니라 각각의 용도에 맞는 도구를 선택하고 서로 연결해서 사용해야된다고 느꼈다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나 또한 사이드 프로젝트에서 캐시를 활용한 경험이 있다. 별도로 Redis같은 외부 저장소를 사용한 건 아니고 로컬 메모리를 사용했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;똑같은 자원을 어떻게 사용하는지에 따라 최적화를 할 수 있다는 것을 느꼈다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;신뢰성 - 무언가 잘못되더라도 시스템이 올바르게 동작해야 함&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;기억에 남는 문구&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결함: 일부 컴포넌트가 잘못됨&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장애: 시스템 전체가 멈춤&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 서로 같지 않음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결함이 생겨도 장애로 이어지지 않게 하는 것이 핵심이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;느낀점:&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결국 운영을 하다보면 결함이 발생하기 마련이다. 그 결함을 미리 어떻게 대처하는지에 따라 큰 장애로 이어지는지 여부를 결정하는 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 벌크 헤드 패턴을 들 수 있겠다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부 io연결 작업에서 공용 스레드 풀을 사용하면 급작스러운 io작업에 모든 스레드가 소진되어 전체 시스템이 마비될 수도 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사전에 &lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;독립적인 스레드풀을 만들어 놓았다면 이정도의 장애는 발생하지 않았을 것이다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2장 - 데이터 모델과 질의 언어&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;데이터 모델 종류&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 관계형 모델&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 문서 모델&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 그래프 기반 데이터 모델&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;관계형 모델과 문서 모델&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관계형의 경쟁자는 많았지만 결국은 관계형이 대세가 되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2010년대에 NoSQL 등장하면서 관계형에 비해 더 나은 지역성을 가졌다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;여기서 지역성이란?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련 정보를 한 곳에 모을 수 있는 건데&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관계형은 각 흩어진 위치에 I/O 작업을 통해 합치는 반면&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NoSQL은 JSON 표현으로 한 번에 볼 수 있음&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 I/O작업은 결국 시간이 더 걸리게 되는 요인이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;ID필드와 텍스트 필드 비교&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;왜 텍스트 대신 ID를 쓰나?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;텍스트 직접 저장 시 문제:&lt;/p&gt;
&lt;div&gt;
&lt;div&gt;
&lt;pre class=&quot;bash&quot; style=&quot;color: #14181f;&quot; data-ke-language=&quot;bash&quot;&gt;&lt;code&gt;&quot;San Francisco&quot; &amp;rarr; &quot;SF&quot; 로 이름 변경 시
&amp;rarr; 3개 행 전부 수정해야 함
&amp;rarr; 하나라도 빠뜨리면 불일치 발생&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결:&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-06-05 오후 9.27.12.png&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;896&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/1O436/dJMcaicy3nM/KgARJp8GkioyQhBhYCYcd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/1O436/dJMcaicy3nM/KgARJp8GkioyQhBhYCYcd0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/1O436/dJMcaicy3nM/KgARJp8GkioyQhBhYCYcd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F1O436%2FdJMcaicy3nM%2FKgARJp8GkioyQhBhYCYcd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;339&quot; height=&quot;440&quot; data-filename=&quot;스크린샷 2026-06-05 오후 9.27.12.png&quot; data-origin-width=&quot;690&quot; data-origin-height=&quot;896&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ID로 저장 시 테이블 값이 있는 1곳만 수정하면 된다. 그 이외에 참조된 곳은 의미없는 아이디만 참조하기에 신경쓰지 않아도 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 곧 DB 정규화의 핵심이다!&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 &lt;b&gt;정규화란&lt;/b&gt;&lt;br /&gt;중복을 제거하고 데이터를 한 곳에서만 관리하는 것 (다만 join 은 DB I/O를 유발하니 트레이드오프가 있다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;선언형과 명령형의 차이점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;선언형&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;목표를 달성하기 위한 방법이 아니라 &lt;b&gt;알고자 하는 데이터의 패턴&lt;/b&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;결과가 충족해야 하는 조건과 데이터를 어떻게 변환(정렬, 그룹화, 집계)할지를 저장하기만 하면 됨&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령형&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;b&gt;특정 순서, 특정 연산&lt;/b&gt;을 수행하게끔 컴퓨터에게 지시함&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;선언형 장점&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;자동 최적화: DB 엔진(옵티마이저)이 내부적으로 스스로 최적화함&lt;/li&gt;
&lt;li&gt;병렬 실행: 결과의 패턴만 정의하였으니 과정은 병렬화 가능&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/oS2pq/dJMcagMz80A/FVKEUAIqHseOVA5VZeiYK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/oS2pq/dJMcagMz80A/FVKEUAIqHseOVA5VZeiYK0/img.png&quot; data-alt=&quot;선언형 예시 - CSS&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/oS2pq/dJMcagMz80A/FVKEUAIqHseOVA5VZeiYK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FoS2pq%2FdJMcagMz80A%2FFVKEUAIqHseOVA5VZeiYK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;369&quot; height=&quot;492&quot; data-origin-width=&quot;3024&quot; data-origin-height=&quot;4032&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;선언형 예시 - CSS&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;명령형의 단점&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;코드 복잡성: 한 눈에 봐도 가독성이 좋지 않다. 순차적으로 depth들어가면서 이해를 하기에&lt;/li&gt;
&lt;li&gt;상태 관리 어려움: selected 클래스 삭제되어도 파란색 배경은 자동 삭제되지 않고 삭제 코드를 또 작성해야된다.&lt;/li&gt;
&lt;li&gt;유지보수 및 확장성: 새로운 api 사용하려면 코드를 처음부터 다시 작성해야 되는 번거로움&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3023&quot; data-origin-height=&quot;2445&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/rgZVB/dJMcabLieVF/ZwNKkKNuc9iUI7UxYJMmx1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/rgZVB/dJMcabLieVF/ZwNKkKNuc9iUI7UxYJMmx1/img.png&quot; data-alt=&quot;명령형 예시 - JavaScript&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/rgZVB/dJMcabLieVF/ZwNKkKNuc9iUI7UxYJMmx1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FrgZVB%2FdJMcabLieVF%2FZwNKkKNuc9iUI7UxYJMmx1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;564&quot; height=&quot;456&quot; data-origin-width=&quot;3023&quot; data-origin-height=&quot;2445&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;명령형 예시 - JavaScript&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3장 - 저장소와 검색&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LSM 트리가 무엇이고 어떻게 동작하는지 중점적으로 알아보았다.&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;1. 로그 구조화와 SS 테이블&lt;/span&gt;&lt;/h4&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;로그 구조화: 모든 쓰기를 순차적으로 추가(append-only)하는 방식으로 데이터를 저장&lt;/b&gt;&lt;/h4&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;[전통적 방식 - 제자리 수정 (in-place update)] &lt;br /&gt;디스크: [A=1] [B=2] [C=3] &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp;&amp;darr; B를 5로 수정 &lt;br /&gt;디스크: [A=1] [B=5] [C=3] &amp;larr; 해당 위치를 직접 덮어씀 &lt;br /&gt;&lt;br /&gt;[로그 구조화 방식 - append-only] &lt;br /&gt;디스크: [A=1] [B=2] [C=3] [B=5] &amp;larr; 끝에 추가만 함 &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;uarr; 최신값&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;SS 테이블: 정렬된 키-값 쌍을 디스크에 저장하는 불변 파일 형식&lt;/b&gt;&lt;/h4&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;4283&quot; data-origin-height=&quot;1914&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/xhyn8/dJMcaaZP9Pn/f7AKE7IcaepR8K8SyFTShk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/xhyn8/dJMcaaZP9Pn/f7AKE7IcaepR8K8SyFTShk/img.png&quot; data-alt=&quot;인메모리 색인을 가진 SS테이블&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/xhyn8/dJMcaaZP9Pn/f7AKE7IcaepR8K8SyFTShk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fxhyn8%2FdJMcaaZP9Pn%2Ff7AKE7IcaepR8K8SyFTShk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;658&quot; height=&quot;294&quot; data-origin-width=&quot;4283&quot; data-origin-height=&quot;1914&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;인메모리 색인을 가진 SS테이블&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;b&gt;인메모리 색인이 필요한 이유&amp;nbsp;&lt;/b&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;색인이 없으면 특정 키를 찾을 때 O(N)으로 동작함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;키와 디스크 위치를 모르기 때문에 몇 번째 바이트에 뭐가 있는지 계산이 불가하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인메모리에 &lt;b&gt;key와 offset(key의 바이트 단위)&lt;/b&gt;을 저장한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, 전체를 안하고 일부 키만 저장해도 된다.&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;메모리 색인 (Sparse) &lt;br /&gt;apple &amp;rarr; offset 0 &lt;br /&gt;dog &amp;rarr; offset 200 &lt;br /&gt;monkey &amp;rarr; offset 500 &lt;br /&gt;&lt;br /&gt;&quot;cat&quot; 검색 &lt;br /&gt;&amp;rarr; apple(0) &amp;lt; cat &amp;lt; dog(200) &lt;br /&gt;&amp;rarr; offset 0부터 200까지만 스캔하면 됨 &lt;br /&gt;&amp;rarr; 전체 파일이 아닌 아주 작은 범위만 탐색&lt;br /&gt;&lt;br /&gt;이렇게 전체가 아니라 그 사이만 스캔하면 되니까 효율적이게 된다.&amp;nbsp;&lt;br /&gt;그러면 메모리 비용 &amp;lt;-&amp;gt; 색인 범위간의 트레이드 오프인 것 같다.&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;유입되는 쓰기는 임의 순서로 발생한다. 데이터를 키로 정렬하려면 어떻게 해야될까?&amp;nbsp;&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;실제 쓰기 요청이 들어오는 순서 (임의 순서) &lt;br /&gt;&lt;br /&gt;1번째 쓰기: zebra = 100 &lt;br /&gt;2번째 쓰기: apple = 200 &lt;br /&gt;3번째 쓰기: mango = 300 &lt;br /&gt;4번째 쓰기: banana = 400&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 SSTable은 정렬이 필요하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;문제&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;들어오는 쓰기: zebra &amp;rarr; apple &amp;rarr; mango &amp;rarr; banana (뒤죽박죽) &lt;br /&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;darr;&amp;nbsp;&lt;br /&gt;SSTable 저장: apple &amp;rarr; banana &amp;rarr; mango &amp;rarr; zebra (정렬 필요)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디스크에 바로 쓰면서 동시에 정렬을 유지하는 것은 어렵다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중간에 넣으려면 뒤의 데이터를 전부 밀어야 함...&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MemTable (메모리의 Red-Black Tree 활용)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 임의 순서로 들어오는 쓰기를 메모리에서 정렬한 뒤, 정렬된 채로 디스크에 한 번에 쓴다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;그렇다면 문제는 없나?&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아니다. 만약 DB가 고장나면 아직 디스크로 기록되지 않고 멤테이블에 있는 가장 최신 쓰기는 손실된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하기 위해서는 이전 절과 같이 매번 쓰기를 즉시 추가할 수 있는 분리된 로그를 디스크 상에 유지해야 한다.&amp;nbsp;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;&lt;span style=&quot;background-color: #f6e199;&quot;&gt;2. LSM 트리&amp;nbsp;&lt;/span&gt;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정렬된 파일 병합과 컴팩션 원리를 기반으로 하는 저장소 엔진을 LSM 저장소 엔진이라고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;핵심 구성 요소&lt;/b&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;1. MemTable(메모리)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 쓰기 요청을 가장 먼저 받는 곳&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;2. WAL(Write-Ahead Log)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: MemTable이 날아가도 복구를 위한 보험&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;3. SSTable(디스크)&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 키가 정렬된 불변 파일&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;쓰기 흐름&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;쓰기 요청 &lt;br /&gt;&amp;darr; &lt;br /&gt;① WAL에 append (디스크, 순서 무관) &lt;br /&gt;&amp;darr; &lt;br /&gt;② MemTable에 삽입 (메모리, 자동 정렬) &lt;br /&gt;&amp;darr; 가득 차면 &lt;br /&gt;③ SSTable로 Flush (디스크, 정렬된 상태) &lt;br /&gt;&amp;darr; L0가 쌓이면 &lt;br /&gt;④ Compaction (병합&amp;middot;정렬&amp;middot;중복제거)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;읽기 흐름&lt;/b&gt;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;읽기 요청 &lt;br /&gt;&amp;darr; &lt;br /&gt;① MemTable 확인 (가장 최신) &lt;br /&gt;&amp;darr; 없으면 &lt;br /&gt;② L0 SSTable 확인 &lt;br /&gt;&amp;darr; 없으면 &lt;br /&gt;③ L1 &amp;rarr; L2 &amp;rarr; ... 순서로 탐색&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;성능 최적화 - 1&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 LSM 트리의 문제점&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: DB에 존재하지 않는 키를 찾는 경우에 느릴 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;이유&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 존재하지 않는 걸 확인하기 위해 가장 오래된 세그먼트까지 거슬어 올라가야 하기에&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;해결책&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;블룸필터: 선택한 키가 존재하지 않는다는 것을 매우 빠르게 판별하는 자료구조&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;준비물: 비트 배열 + 해시 함수&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-06-05 오후 8.23.16.png&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;382&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tpzkR/dJMcabLidxj/A41pyIwleUY6TgEqVZAkRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tpzkR/dJMcabLidxj/A41pyIwleUY6TgEqVZAkRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tpzkR/dJMcabLidxj/A41pyIwleUY6TgEqVZAkRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtpzkR%2FdJMcabLidxj%2FA41pyIwleUY6TgEqVZAkRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;489&quot; height=&quot;308&quot; data-filename=&quot;스크린샷 2026-06-05 오후 8.23.16.png&quot; data-origin-width=&quot;606&quot; data-origin-height=&quot;382&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/DARk2/dJMcahSavIc/C29kpCb5PMEm3D2l0X2qr0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/DARk2/dJMcahSavIc/C29kpCb5PMEm3D2l0X2qr0/img.png&quot; style=&quot;width: 44.3013%; margin-right: 10px;&quot; width=&quot;403&quot; height=&quot;457&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;568&quot; data-origin-height=&quot;644&quot; data-filename=&quot;스크린샷 2026-06-05 오후 8.23.33.png&quot; data-widthpercent=&quot;44.85&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/DARk2/dJMcahSavIc/C29kpCb5PMEm3D2l0X2qr0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FDARk2%2FdJMcahSavIc%2FC29kpCb5PMEm3D2l0X2qr0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;568&quot; height=&quot;644&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/5EoXI/dJMcai4Acss/nZShxC4TjrHyQ9qAAJPXK0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/5EoXI/dJMcai4Acss/nZShxC4TjrHyQ9qAAJPXK0/img.png&quot; style=&quot;width: 54.4762%;&quot; width=&quot;439&quot; height=&quot;405&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;590&quot; data-origin-height=&quot;544&quot; data-filename=&quot;스크린샷 2026-06-05 오후 8.23.44.png&quot; data-widthpercent=&quot;55.15&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/5EoXI/dJMcai4Acss/nZShxC4TjrHyQ9qAAJPXK0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F5EoXI%2FdJMcai4Acss%2FnZShxC4TjrHyQ9qAAJPXK0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;590&quot; height=&quot;544&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;

&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-06-05 오후 8.24.07.png&quot; data-origin-width=&quot;1062&quot; data-origin-height=&quot;1340&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cJSMIj/dJMcahkqt8j/Q1Vk4HdiPRhvwdDwmpEJRK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cJSMIj/dJMcahkqt8j/Q1Vk4HdiPRhvwdDwmpEJRK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cJSMIj/dJMcahkqt8j/Q1Vk4HdiPRhvwdDwmpEJRK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcJSMIj%2FdJMcahkqt8j%2FQ1Vk4HdiPRhvwdDwmpEJRK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;569&quot; height=&quot;718&quot; data-filename=&quot;스크린샷 2026-06-05 오후 8.24.07.png&quot; data-origin-width=&quot;1062&quot; data-origin-height=&quot;1340&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;

&lt;h4 data-ke-size=&quot;size20&quot;&gt;결론&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비트 중 하나라도 0&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 확실히 없음&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모두 1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 있을 수도 있음&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 실제로 SSTable 탐색 필요&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size18&quot;&gt;&lt;b&gt;성능 최적화 - 2&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compaction 전략: 두 가지로 나뉜다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;목적&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시간이 지나면 옛날 값들이 쌓이게 된다.(새 값으로 추가되거나, 삭제됐지만 아직 파일에 존재하는 경우)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 크기계층 컴팩션&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;: 작은 SSTable -&amp;gt; 큰 SSTable에 병합&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;처음 &lt;br /&gt;[1MB] [1MB] [1MB] [1MB] &amp;larr; 작은 것 4개 쌓임 &lt;br /&gt;&amp;darr; 병합 &lt;br /&gt;[4MB] &amp;larr; 하나로 합침 &lt;br /&gt;&lt;br /&gt;또 쌓임 &lt;br /&gt;[4MB] [4MB] [4MB] [4MB] &amp;larr; 중간 것 4개 쌓임 &lt;br /&gt;&amp;darr; 병합 &lt;br /&gt;[16MB] &amp;larr; 또 하나로 합침 &lt;br /&gt;&lt;br /&gt;계속 반복 &lt;br /&gt;[16MB] [16MB] [16MB] [16MB] &lt;br /&gt;&amp;darr; &lt;br /&gt;[64MB]&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰기 속도가 빠르다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 그냥 새 파일 만들고 나중에 합치면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점:&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;같은 키가 여러 파일에 존재 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;-&amp;gt; 읽을 때 여러 파일 확인 필요&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;병합 직전에 디스크 공간 일시적으로 2배 필요하다는 단점이 있다.&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;불변이기에 동일한 데이터를 변경하고 싶을 때 수정할 수 없고 써야 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이는 SSTable A, B로 나뉠 수 있다.&lt;/p&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-06-05 오후 8.36.47.png&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;1076&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/BLt6f/dJMb997HLfB/uwNw2odhGGbsrp5zWpOsc1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/BLt6f/dJMb997HLfB/uwNw2odhGGbsrp5zWpOsc1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/BLt6f/dJMb997HLfB/uwNw2odhGGbsrp5zWpOsc1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FBLt6f%2FdJMb997HLfB%2FuwNw2odhGGbsrp5zWpOsc1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;502&quot; height=&quot;707&quot; data-filename=&quot;스크린샷 2026-06-05 오후 8.36.47.png&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;1076&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 레벨 컴팩션&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;: 키 범위를 더 작은 SSTable로 나누고 오래된 데이터는 개별 레벨로 이동&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;L0 (키 범위 겹쳐도 됨, Flush된 파일들) &lt;br /&gt;[a,z,m,b...] [c,k,g,p...] &lt;br /&gt;&lt;br /&gt;&amp;darr; L0가 일정 개수 차면 &lt;br /&gt;&lt;br /&gt;L1으로 내려보내면서 키 범위 재정렬 &lt;br /&gt;[a-g] [h-n] [o-t] [u-z] &lt;br /&gt;&amp;rarr; 겹치는 파일만 골라서 부분 병합 &lt;br /&gt;&lt;br /&gt;&amp;darr; L1이 차면 &lt;br /&gt;&lt;br /&gt;L2로 내려보냄 (L1보다 10배 큰 용량) &lt;br /&gt;[a-c][d-f][g-i]...(더 잘게 쪼개짐)&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;장점:&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 빠름 (분리된 구역만 확인하면 되니까)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단점:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쓰기 느림 (Compaction할 때마다 같은 데이터를 반복해서 새로 써야 되기 때문에)&lt;/p&gt;
&lt;div data-ke-type=&quot;moreLess&quot; data-text-more=&quot;더보기&quot; data-text-less=&quot;닫기&quot;&gt;&lt;a class=&quot;btn-toggle-moreless&quot;&gt;더보기&lt;/a&gt;
&lt;div class=&quot;moreless-content&quot;&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-06-05 오후 8.43.19.png&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;306&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/HUi2o/dJMcageHtc6/HGBHxeyGMb3Eobl1kksys1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/HUi2o/dJMcageHtc6/HGBHxeyGMb3Eobl1kksys1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/HUi2o/dJMcageHtc6/HGBHxeyGMb3Eobl1kksys1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FHUi2o%2FdJMcageHtc6%2FHGBHxeyGMb3Eobl1kksys1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;602&quot; height=&quot;241&quot; data-filename=&quot;스크린샷 2026-06-05 오후 8.43.19.png&quot; data-origin-width=&quot;764&quot; data-origin-height=&quot;306&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;LSM 트리의 장단점&amp;nbsp;&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;장점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. 쓰기 처리량이 높다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;B 트리: &lt;br /&gt;데이터 수정 시 해당 페이지를 &lt;br /&gt;디스크에서 찾아서 &amp;rarr; 덮어씀 &lt;br /&gt;&amp;rarr; 랜덤 쓰기 발생 (느림) &lt;br /&gt;&lt;br /&gt;LSM 트리: &lt;br /&gt;항상 순차적으로 SSTable 파일을 씀, 즉 여러 페이지를 덮어 쓰는 것이 아님&lt;br /&gt;&amp;rarr; 순차 쓰기 (빠름)&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;순차 쓰기가 왜 빠를까?&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HDD의 매커니즘과 관련있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-06-05 오후 9.43.15.png&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;834&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/doKYKw/dJMcacXE6J2/QPAkX4iGkCcogiuR6AKoNk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/doKYKw/dJMcacXE6J2/QPAkX4iGkCcogiuR6AKoNk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/doKYKw/dJMcacXE6J2/QPAkX4iGkCcogiuR6AKoNk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdoKYKw%2FdJMcacXE6J2%2FQPAkX4iGkCcogiuR6AKoNk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;609&quot; height=&quot;398&quot; data-filename=&quot;스크린샷 2026-06-05 오후 9.43.15.png&quot; data-origin-width=&quot;1276&quot; data-origin-height=&quot;834&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SSD은 물리적 헤드가 없어서 랜덤/순차 차이가 분명하진 않다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 낮은 쓰기 증폭과 파편화 감소는 SSD의 경우 훨씬 유리하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;3005&quot; data-origin-height=&quot;805&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bxn7cQ/dJMcaaZQdzV/gbSrDmHuUral7HK2NEZnkk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bxn7cQ/dJMcaaZQdzV/gbSrDmHuUral7HK2NEZnkk/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bxn7cQ/dJMcaaZQdzV/gbSrDmHuUral7HK2NEZnkk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbxn7cQ%2FdJMcaaZQdzV%2FgbSrDmHuUral7HK2NEZnkk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;677&quot; height=&quot;181&quot; data-origin-width=&quot;3005&quot; data-origin-height=&quot;805&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. 쓰기 증폭이 더 낮다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;B 트리: &lt;br /&gt;데이터 쓰기 전 로그 한 번 &lt;br /&gt;+ 트리에 실제 쓰기 한 번 &lt;br /&gt;+ 페이지 분리 시 또 쓰기 &lt;br /&gt;&amp;rarr; 최소 두 번 이상 기록 &lt;br /&gt;&lt;br /&gt;LSM 트리: &lt;br /&gt;Compaction이 있지만 &lt;br /&gt;순차적으로 한번에 씀 &lt;br /&gt;&amp;rarr; 상대적으로 쓰기 증폭 낮음&amp;nbsp;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;LSM 트리에서 쓰기 증폭이 있다고 했는데 왜 B트리보다는 낮을까?&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bmVQ7s/dJMcabxFf7S/AuuIIAfloQmx6UaV54dAYk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bmVQ7s/dJMcabxFf7S/AuuIIAfloQmx6UaV54dAYk/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;658&quot; data-origin-height=&quot;568&quot; data-filename=&quot;스크린샷 2026-06-05 오후 9.50.15.png&quot; style=&quot;width: 50.0317%; margin-right: 10px;&quot; data-widthpercent=&quot;50.62&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bmVQ7s/dJMcabxFf7S/AuuIIAfloQmx6UaV54dAYk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbmVQ7s%2FdJMcabxFf7S%2FAuuIIAfloQmx6UaV54dAYk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;658&quot; height=&quot;568&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FpDix/dJMcaiKiFjb/IjhpAfCJf2jpVFgJrIdefK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FpDix/dJMcaiKiFjb/IjhpAfCJf2jpVFgJrIdefK/img.png&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;782&quot; data-origin-height=&quot;692&quot; data-filename=&quot;스크린샷 2026-06-05 오후 9.50.22.png&quot; style=&quot;width: 48.8055%;&quot; data-widthpercent=&quot;49.38&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FpDix/dJMcaiKiFjb/IjhpAfCJf2jpVFgJrIdefK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFpDix%2FdJMcaiKiFjb%2FIjhpAfCJf2jpVFgJrIdefK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;782&quot; height=&quot;692&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 압축률이 좋다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;B 트리: &lt;br /&gt;페이지 단위로 저장 &lt;br /&gt;&amp;rarr; 페이지가 꽉 안 차면 &lt;br /&gt;남는 공간 낭비 (파편화) &lt;br /&gt;&lt;br /&gt;LSM 트리: &lt;br /&gt;SSTable을 꽉꽉 채워서 씀 &lt;br /&gt;+ Compaction 시 불필요한 데이터 제거 &lt;br /&gt;&amp;rarr; 디스크 공간 효율적&amp;nbsp;&lt;br /&gt;&amp;rarr; B 트리보다 더 적은 파일 생성&lt;/blockquote&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;단점&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1. Compaction이 읽기/쓰기 성능에 영향&amp;nbsp;&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;Compaction 진행 중: &lt;br /&gt;디스크 자원을 Compaction이 점유 &lt;br /&gt;&amp;darr; &lt;br /&gt;새로운 읽기/쓰기 요청이 &lt;br /&gt;디스크 쓰려고 대기해야 함 &lt;br /&gt;&amp;darr; &lt;br /&gt;응답 시간이 &lt;b&gt;길어짐&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;반면 Btree는&amp;nbsp;&lt;br /&gt;데이터 요청 -&amp;gt; 해당 페이지 찾아서 -&amp;gt; 바로 덮어씀 -&amp;gt; 끝&amp;nbsp;&lt;br /&gt;이 과정에서 백그라운드 작업이 없고 항상 일정한 속도임&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2. Compaction이 유입 쓰기 속도를 못 따라갈 수 있다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;쓰기가 매우 빠르게 들어오면: &lt;br /&gt;Compaction이 처리하는 속도 &amp;lt; 쓰기 유입 속도 &lt;br /&gt;&amp;darr; &lt;br /&gt;SSTable 파일이 계속 쌓임 &lt;br /&gt;&amp;darr; &lt;br /&gt;디스크 공간 부족 &lt;b&gt;위험&lt;/b&gt;&lt;br /&gt;읽기 시 확인할 파일 증가 &amp;rarr; 읽기도 &lt;b&gt;느려짐&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&amp;rarr; 명시적 모니터링 필요!&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;3. 같은 키의 다중 복사본 존재&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style3&quot;&gt;B 트리: &lt;br /&gt;각 키가 색인의 한 곳에만 정확히 존재 &lt;br /&gt;&amp;rarr; 트랜잭션 처리 쉬움&amp;nbsp;&lt;br /&gt;&lt;br /&gt;LSM 트리: &lt;br /&gt;Compaction 전까지 &lt;br /&gt;여러 SSTable에 같은 키 존재 가능 &lt;br /&gt;&amp;rarr; 트랜잭션 처리 복잡&amp;nbsp;&lt;br /&gt;&amp;rarr; 강력한 트랜잭션이 필요하면 &lt;br /&gt;B 트리가 훨씬 유리&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>changha.</author>
      <guid isPermaLink="true">https://changha-dev.tistory.com/212</guid>
      <comments>https://changha-dev.tistory.com/212#entry212comment</comments>
      <pubDate>Fri, 5 Jun 2026 20:55:58 +0900</pubDate>
    </item>
    <item>
      <title>백엔드 인턴 최종합격</title>
      <link>https://changha-dev.tistory.com/211</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;드디어 나도 최종합격을 하며&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;첫 회사로 스타트업 백엔드 인턴으로 들어가게 됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;br /&gt;작년 9월부터 면접을 보기 시작했으니 9개월 만에 취업성공을 했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;백엔드는 워낙 포화상태라 더더 힘들었던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 AI로 인해 시장에서 요구되는 능력치는 날이 갈수록 올라갔다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;코딩테스트, 과제역량, CS 기본기 등등 프로젝트말고도 준비해야 될 것은 많았다. (취준생들 정말 응원합니다)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;스크린샷 2026-05-31 오후 4.32.36.png&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;1486&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cc3gm6/dJMcac4lJHl/h9wz24tfstq0tLJEMiZPvk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cc3gm6/dJMcac4lJHl/h9wz24tfstq0tLJEMiZPvk/img.png&quot; data-alt=&quot;올 해 상반기 지원한 내역들 중 일부&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cc3gm6/dJMcac4lJHl/h9wz24tfstq0tLJEMiZPvk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fcc3gm6%2FdJMcac4lJHl%2Fh9wz24tfstq0tLJEMiZPvk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;314&quot; height=&quot;509&quot; data-filename=&quot;스크린샷 2026-05-31 오후 4.32.36.png&quot; data-origin-width=&quot;916&quot; data-origin-height=&quot;1486&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;올 해 상반기 지원한 내역들 중 일부&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;도움 되었던 것은&amp;nbsp;&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 세미나에 온라인 참석을 한 적이 있다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;토스,무신사,채널톡을 합격하신 분이 강연자였는데,&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;빅테크 세미나들을 많이보고 그중 하나를 골라서 프로젝트로 만들어서 이력서에 녹였다는 것이었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 말대로 나도 똑같이 하진 않았지만 빅테크 세미나(유튜브나 기술블로그)들을 최대한 많이 보고 노션에 정리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예전에는 기본기만 잘하라고 했지만 현재는 그것만으로는 시장에서 메리트가 부족하다고 생각하고 느꼈다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imagegridblock&quot;&gt;
  &lt;div class=&quot;image-container&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/75Z6j/dJMb997DQ4v/khSctL5r3CTijX5IwYuYz1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/75Z6j/dJMb997DQ4v/khSctL5r3CTijX5IwYuYz1/img.png&quot; style=&quot;width: 52.2679%; margin-right: 10px;&quot; width=&quot;605&quot; data-widthpercent=&quot;52.88&quot; data-filename=&quot;스크린샷 2026-05-31 오후 4.39.09.png&quot; data-origin-height=&quot;1544&quot; data-origin-width=&quot;1454&quot; data-is-animation=&quot;false&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/75Z6j/dJMb997DQ4v/khSctL5r3CTijX5IwYuYz1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F75Z6j%2FdJMb997DQ4v%2FkhSctL5r3CTijX5IwYuYz1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1454&quot; height=&quot;1544&quot;/&gt;&lt;/span&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bi9bcX/dJMcaiKeMrn/DRaUa2iu7Wm23nZwMCulTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bi9bcX/dJMcaiKeMrn/DRaUa2iu7Wm23nZwMCulTK/img.png&quot; style=&quot;width: 46.5693%;&quot; width=&quot;637&quot; height=&quot;759&quot; data-widthpercent=&quot;47.12&quot; data-is-animation=&quot;false&quot; data-origin-width=&quot;1324&quot; data-origin-height=&quot;1578&quot; data-filename=&quot;스크린샷 2026-05-31 오후 4.45.03.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bi9bcX/dJMcaiKeMrn/DRaUa2iu7Wm23nZwMCulTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbi9bcX%2FdJMcaiKeMrn%2FDRaUa2iu7Wm23nZwMCulTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1324&quot; height=&quot;1578&quot;/&gt;&lt;/span&gt;&lt;/div&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;특히 서류에서는 훅이 필요한 것 같다. 눈에 띌만한 요소.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나는 그게 &lt;b&gt;10만건 데이터 처리 파이프라인 구축이었던&lt;/b&gt; 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정에서 어떤 시행착오가 있었는지 잘 정리하는 게 필요했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러면 면접에서 어떻게 했는지, 그 과정에서 고민한 지점이 무엇 인지들을 물어보게 된다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;어떻게 이 회사에 합격했을까 하면&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1차와 2차 면접에서 그동안 내가 했던 적극성이 잘 전달되었던 것 같다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 사이드 플젝(나르지지) 기획 부터 팀 모집, GDSC 활동(면접에서 처음으로 알아주신 분이 계셨다..), 기술에 대한 호기심(ai관련 동향이나 내가 공부했다고 한 것에 대해 질문했을 때 답변)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서 이러한 적극적인 사람을 필요로했고 운 좋게 내가 선발되었던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;앞으로의 방향성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지금까지는 동아리, 팀 프로젝트만 많이 했어서, 회사에서의 워크플로우가 궁금하고 많이 배우고 싶다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;개인적으로는&amp;nbsp;AI 관련하여 그동안 적극적으로 정보를 최신화하면서 따라가지 못했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;회사에서도 전사적 AX로 방향을 설정한 만큼, 이와 관련해서 많이 공부해보려고 한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한 대규모 데이터 처리에 대해서도 개인적으로 관심이 있어서 공부해볼 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그동안 고생했다 나!!&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;20260514_174134.jpg&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;4000&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Zp5bQ/dJMcahEzvIq/gUKfFPuhXriq2hZuSAJcAk/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Zp5bQ/dJMcahEzvIq/gUKfFPuhXriq2hZuSAJcAk/img.jpg&quot; data-alt=&quot;잘 찍었다고 느낀 종로 사진&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Zp5bQ/dJMcahEzvIq/gUKfFPuhXriq2hZuSAJcAk/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FZp5bQ%2FdJMcahEzvIq%2FgUKfFPuhXriq2hZuSAJcAk%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;512&quot; height=&quot;683&quot; data-filename=&quot;20260514_174134.jpg&quot; data-origin-width=&quot;3000&quot; data-origin-height=&quot;4000&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;잘 찍었다고 느낀 종로 사진&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>changha.</author>
      <guid isPermaLink="true">https://changha-dev.tistory.com/211</guid>
      <comments>https://changha-dev.tistory.com/211#entry211comment</comments>
      <pubDate>Sun, 31 May 2026 17:05:24 +0900</pubDate>
    </item>
    <item>
      <title>면접스터디 4차 회고</title>
      <link>https://changha-dev.tistory.com/210</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;다같이 계속 하다보니 서로 사는얘기하는 빈도도 늘어나는 것 같다~ ㅋㅋ&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다같은 취준생 입장이다 보니 공감대 형성이 잘 되는 것 같아서 좋다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번에 나는 preparedstatement 와 statement차이점에 대해 정리하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.notion.so/319025901eb8802a98cbf0abeda56183?v=319025901eb880a2a618000c2d63983e&amp;amp;p=35dfa15530f78008b75dd9f6e49f8c48&amp;amp;pm=s&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.notion.so/319025901eb8802a98cbf0abeda56183?v=319025901eb880a2a618000c2d63983e&amp;amp;p=35dfa15530f78008b75dd9f6e49f8c48&amp;amp;pm=s&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1779709177786&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;우리의 애플리케이션에서 PreparedStatement는 어떻게 동작하고 있는가 | Notion&quot; data-og-description=&quot;이 글의 목적:&quot; data-og-host=&quot;www.notion.so&quot; data-og-source-url=&quot;https://www.notion.so/319025901eb8802a98cbf0abeda56183?v=319025901eb880a2a618000c2d63983e&amp;amp;p=35dfa15530f78008b75dd9f6e49f8c48&amp;amp;pm=s&quot; data-og-url=&quot;https://www.notion.so/PreparedStatement-35dfa15530f78008b75dd9f6e49f8c48&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/dYtnMY/dJMb9cBOhb0/n6ADuIcBKY9bAvwS09dG3K/img.png?width=790&amp;amp;height=525&amp;amp;face=0_0_790_525,https://scrap.kakaocdn.net/dn/bamuo1/dJMb9eTVNzB/bNIGXa3CKhcZBn1QK3ekN1/img.png?width=790&amp;amp;height=525&amp;amp;face=0_0_790_525&quot;&gt;&lt;a href=&quot;https://www.notion.so/319025901eb8802a98cbf0abeda56183?v=319025901eb880a2a618000c2d63983e&amp;amp;p=35dfa15530f78008b75dd9f6e49f8c48&amp;amp;pm=s&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.notion.so/319025901eb8802a98cbf0abeda56183?v=319025901eb880a2a618000c2d63983e&amp;amp;p=35dfa15530f78008b75dd9f6e49f8c48&amp;amp;pm=s&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/dYtnMY/dJMb9cBOhb0/n6ADuIcBKY9bAvwS09dG3K/img.png?width=790&amp;amp;height=525&amp;amp;face=0_0_790_525,https://scrap.kakaocdn.net/dn/bamuo1/dJMb9eTVNzB/bNIGXa3CKhcZBn1QK3ekN1/img.png?width=790&amp;amp;height=525&amp;amp;face=0_0_790_525');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;우리의 애플리케이션에서 PreparedStatement는 어떻게 동작하고 있는가 | Notion&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;이 글의 목적:&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.notion.so&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 이 부분까지 깊게 볼 일이 없어서 잘 모르는 부분이긴 했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 평소에 사용하는 db io와 연관되어 있어서 중요한 부분이라고 생각이 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 최근에 이 용어를 접한건 jdbc batch write 부분을 개선하면서 접했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;어떤부분인가하면&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래사진처럼 대용량 필드 쿼리를 만들때 아래처럼 PreparedStatement 인덱스를 직접 증가시키며 매핑했어야 됐다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;1568&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/FkN3V/dJMcaaZH1jX/PkN8AaU0ivkR2RI7cohiy0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/FkN3V/dJMcaaZH1jX/PkN8AaU0ivkR2RI7cohiy0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/FkN3V/dJMcaaZH1jX/PkN8AaU0ivkR2RI7cohiy0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FFkN3V%2FdJMcaaZH1jX%2FPkN8AaU0ivkR2RI7cohiy0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;375&quot; height=&quot;361&quot; data-origin-width=&quot;1630&quot; data-origin-height=&quot;1568&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이부분에 대한 해결책은&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.notion.so/Batch-Insert-35dfa15530f781b7a7bef1e50e483825&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.notion.so/Batch-Insert-35dfa15530f781b7a7bef1e50e483825&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1779709444592&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;대량 필드 Batch Insert 매핑 개선 | Notion&quot; data-og-description=&quot;문제&quot; data-og-host=&quot;same-robin-1ff.notion.site&quot; data-og-source-url=&quot;https://www.notion.so/Batch-Insert-35dfa15530f781b7a7bef1e50e483825&quot; data-og-url=&quot;https://same-robin-1ff.notion.site/Batch-Insert-35dfa15530f781b7a7bef1e50e483825&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bbAYAX/dJMb9b3YgH9/xdWnK5fFvOLwM7iw0JdUHK/img.png?width=1626&amp;amp;height=486&amp;amp;face=0_0_1626_486,https://scrap.kakaocdn.net/dn/RfbbP/dJMb9kT9bRE/M3aqkgfSp32pKulmNDsgCK/img.png?width=1626&amp;amp;height=486&amp;amp;face=0_0_1626_486&quot;&gt;&lt;a href=&quot;https://www.notion.so/Batch-Insert-35dfa15530f781b7a7bef1e50e483825&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.notion.so/Batch-Insert-35dfa15530f781b7a7bef1e50e483825&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bbAYAX/dJMb9b3YgH9/xdWnK5fFvOLwM7iw0JdUHK/img.png?width=1626&amp;amp;height=486&amp;amp;face=0_0_1626_486,https://scrap.kakaocdn.net/dn/RfbbP/dJMb9kT9bRE/M3aqkgfSp32pKulmNDsgCK/img.png?width=1626&amp;amp;height=486&amp;amp;face=0_0_1626_486');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;대량 필드 Batch Insert 매핑 개선 | Notion&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;문제&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;same-robin-1ff.notion.site&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 정리했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;본론으로 돌아와서 내가 정리한 블로그 내용의 핵심은&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;쿼리문 생성과 io 작업을 줄이기 위해 캐시를 사용하는데 이 부분을 최적화 하기위해 관련 파라미터들을 true 설정해야 좋다는 것이었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅈㅎ님은&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;mysql db x-lock의 순서와 관련한 칼럼을 소개해주셨다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;fifo방식으로 락이 풀릴까에 대한 궁금증이 있었는데 이 부분은 fifo가 아닌 cats 알고리즘에 의해 작동되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;cats는 &lt;b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;Contention-Aware Transaction Scheduling의 약자&lt;/span&gt;&lt;/b&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;로&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span style=&quot;background-color: #ffffff; color: #000000; text-align: start;&quot;&gt;해당 트랜잭션이 블로킹하고 있는 트랜잭션 수에 따라 가중치를 할당해 대기 우선 순위를 지정한다.&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 부분에 대해서는 생각해본적이 없었는데 새로 알게되어 재밌었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>changha.</author>
      <guid isPermaLink="true">https://changha-dev.tistory.com/210</guid>
      <comments>https://changha-dev.tistory.com/210#entry210comment</comments>
      <pubDate>Mon, 25 May 2026 20:54:31 +0900</pubDate>
    </item>
    <item>
      <title>[책 후기] 데이터 중심 애플리케이션 설계</title>
      <link>https://changha-dev.tistory.com/209</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;586&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/ble0Pe/dJMcaaLZXQs/sIB90V2hKddWwMjyzXcYIK/img.jpg&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/ble0Pe/dJMcaaLZXQs/sIB90V2hKddWwMjyzXcYIK/img.jpg&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/ble0Pe/dJMcaaLZXQs/sIB90V2hKddWwMjyzXcYIK/img.jpg&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fble0Pe%2FdJMcaaLZXQs%2FsIB90V2hKddWwMjyzXcYIK%2Fimg.jpg&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;403&quot; height=&quot;516&quot; data-origin-width=&quot;458&quot; data-origin-height=&quot;586&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 책은 작년 여름에 인프런 멘토한테 추천받았던 책이다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 회사에서 교과서로 취급받는 정도로 유명하고 좋은 책이다라고 들었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나도 처음에 도서관에서 우연히 발견했을 때 제목부터 흥미로워서 빌렸다가 1~2장에서 그만둔 기억이 있었다. (글을 읽어도 이해가 잘 안돼서..)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽어야지 하다가 올 해 3월부터 완독을 목표로 시작하였다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용은&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;5~9장이 나한테 큰 도움이 되었던 것 같다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분산환경에서 어떤 문제가 발생하고 해결책들이 있는지 이 과정들을 자세히 설명해주었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;4월에 1회독 마무리를 했지만 뭔가 배운것이 제대로 머릿속에 정리가 안 된 것 같아서 노션에 정리하기도 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.notion.so/35afa15530f7804ebf24c5e2e6c7a192&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.notion.so/35afa15530f7804ebf24c5e2e6c7a192&lt;/a&gt; (노션 정리 링크)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;분산환경에서 통신할 때 각 시간까지 고려해야 된다는 부분도 알려주었는데 이부분은 아직 제대로 이해를 못 한 것 같긴하다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;10장 11장은 배치와 스트림 부분으로써, 2회독을 해도 제대로 써보지 않았기에 읽는데 힘들었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래도 말로만 듣던 hadoop, kafka, zookeeper에 대해 대략적으로 어떤 용도로 쓰이는지 처음 알게 되었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;작가가 추상화 수준에서 db, os를 비교한 부분이 인상깊었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터를 저장하고 처리하고 질의하는 용도로&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;db = 레코드 형태&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;os = 파일 형태&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;로 정보를 관리한다는 부분&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;unix = 하드웨어 추상화를 프로그래머에게 제공&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;rdb = 고수준 추상화를 프로그래머에게 제공 (&lt;span style=&quot;color: #333333; text-align: start;&quot;&gt;디스크 상의 자료 구조, 동시성, 장애 복구 복잡성을 감춤)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 책은 앞으로도 읽을 때마다 배울점이 생기는 책일 것 같다.&amp;nbsp;&lt;/p&gt;</description>
      <category>테크/책 후기</category>
      <author>changha.</author>
      <guid isPermaLink="true">https://changha-dev.tistory.com/209</guid>
      <comments>https://changha-dev.tistory.com/209#entry209comment</comments>
      <pubDate>Fri, 8 May 2026 12:28:43 +0900</pubDate>
    </item>
    <item>
      <title>면접 스터디 3차 회고</title>
      <link>https://changha-dev.tistory.com/208</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;늦었지만 저번주에 진행한 면접 스터디 내용을 복기하려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅈㅎ님과 나 이렇게 발표했다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ㅈㅎ님 내용은 토스 테크 블로그의 &lt;a href=&quot;https://toss.tech/article/34481&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://toss.tech/article/34481&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1777886502062&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;캐시를 적용하기 까지의 험난한 길 (TPS 1만 안정적으로 서비스하기)&quot; data-og-description=&quot;TPS가 평균 1만, 최대 2만까지 늘어난 약관(terms) 서버에 안정적인 서비스 제공을 위해 캐시를 적용한 이야기를 들려드리려고 해요.&quot; data-og-host=&quot;toss.tech&quot; data-og-source-url=&quot;https://toss.tech/article/34481&quot; data-og-url=&quot;https://toss.tech/article/34481&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/QR75r/dJMb83kwH1A/EfkWEhKkYrYYt1kFTj5FF0/img.jpg?width=2251&amp;amp;height=1183&amp;amp;face=0_0_2251_1183,https://scrap.kakaocdn.net/dn/AKKhl/dJMb89yhltW/JaHElqxKRi0BkwuiK9RQTK/img.jpg?width=2251&amp;amp;height=1183&amp;amp;face=0_0_2251_1183,https://scrap.kakaocdn.net/dn/bozLIA/dJMb9jgA2t3/ueXLRspfIla8hHKUfxdLwK/img.jpg?width=2251&amp;amp;height=1183&amp;amp;face=0_0_2251_1183&quot;&gt;&lt;a href=&quot;https://toss.tech/article/34481&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://toss.tech/article/34481&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/QR75r/dJMb83kwH1A/EfkWEhKkYrYYt1kFTj5FF0/img.jpg?width=2251&amp;amp;height=1183&amp;amp;face=0_0_2251_1183,https://scrap.kakaocdn.net/dn/AKKhl/dJMb89yhltW/JaHElqxKRi0BkwuiK9RQTK/img.jpg?width=2251&amp;amp;height=1183&amp;amp;face=0_0_2251_1183,https://scrap.kakaocdn.net/dn/bozLIA/dJMb9jgA2t3/ueXLRspfIla8hHKUfxdLwK/img.jpg?width=2251&amp;amp;height=1183&amp;amp;face=0_0_2251_1183');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;캐시를 적용하기 까지의 험난한 길 (TPS 1만 안정적으로 서비스하기)&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;TPS가 평균 1만, 최대 2만까지 늘어난 약관(terms) 서버에 안정적인 서비스 제공을 위해 캐시를 적용한 이야기를 들려드리려고 해요.&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;toss.tech&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 아티클을 발표해주셨다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용은&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;약관 서비스를 캐시 처리를 통해 db 부하를 감소시키려는 목적이었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;조건: Strong Consistency를 유지해야 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;약관은 개인정보와 밀접하여 철회한 이후에도 개인정보 공유가 되는 문제가 발생하면 안되기에&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전략: Look-aside 패턴&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유: db의 정보가 자주 변경되지 않았기에&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만약 db 정보가 자주 변경된다면 다른 방식 고민&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;만료처리: @EnitityListner + @TransactionalEventListener 사용&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용: db에 커밋된 이후에 캐시가 만료되도록&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이유: db commit전에 만료처리를 하는 경우 다른 요청에서 commit 전 데이터를 적재 할 수 있음&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한계: after_commit이어도 cache evict 처리가 실패해도 rollback이 안됨&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결: circuit breaker 활용&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용: evict 실패 시, force open 하여 db 조회하도록 함&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: commit 이후 cache를 evcit하는 찰나의 순간에 kafka event를 consume한 곳에서 잘못된 캐시 조회&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결: kafka event를 cache evict 처리 이후에 발생하도록&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제: 그래도 commit 이후 cache evict 처리 되기전에 조회하면?&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결: 정책으로 해결(약관 동의 여부는 값이 db에 commit 되는 순간 바로 다음 요청에 db에 저장된 값이 응답되어야 한다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;캐시를 도입하며 기술적으로 트러블을 해결하려고 하기보다는 로직 순서와 정책 정의를 통해 문제를 풀 수 있는 유용한 case였다고 느꼈다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;나&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.notion.so/348fa15530f780e08540ec667c89b5f5?source=copy_link&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://www.notion.so/348fa15530f780e08540ec667c89b5f5?source=copy_link&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1777887457472&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;재고 관리 시스템 | Notion&quot; data-og-description=&quot;정해진 재고 만큼만 주문&quot; data-og-host=&quot;www.notion.so&quot; data-og-source-url=&quot;https://www.notion.so/348fa15530f780e08540ec667c89b5f5?source=copy_link&quot; data-og-url=&quot;https://www.notion.so/348fa15530f780e08540ec667c89b5f5&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/bsC2EG/dJMb8957m8w/8un1qrtwZeQB2MgZgnfwvK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/npENP/dJMb8957m8v/rPmuUoEBdb6gEBfK6WJwLk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630&quot;&gt;&lt;a href=&quot;https://www.notion.so/348fa15530f780e08540ec667c89b5f5?source=copy_link&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.notion.so/348fa15530f780e08540ec667c89b5f5?source=copy_link&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/bsC2EG/dJMb8957m8w/8un1qrtwZeQB2MgZgnfwvK/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630,https://scrap.kakaocdn.net/dn/npENP/dJMb8957m8v/rPmuUoEBdb6gEBfK6WJwLk/img.png?width=1200&amp;amp;height=630&amp;amp;face=0_0_1200_630');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;재고 관리 시스템 | Notion&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;정해진 재고 만큼만 주문&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.notion.so&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;면접에 대비하기 위해 재고관리 시스템을 구축해보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 포인트는 동시성 제어, transaction, 정합성(tx outbox pattern) 3가지였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 배운것은 분산락을 사용하더라도 예외 케이스(타임아웃)이 발생할 수 있기에&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;낙관적락을 같이 쓸 수도 있겠구나라는 것을 얻었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 재고와 같이 내구성이 중요한 도메인은 redis와 같이 메모리단 보다 db단에서 정합성 관리를 해야된다는&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비즈니스적 중요성을 함께 생각할 수 있었다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <author>changha.</author>
      <guid isPermaLink="true">https://changha-dev.tistory.com/208</guid>
      <comments>https://changha-dev.tistory.com/208#entry208comment</comments>
      <pubDate>Mon, 4 May 2026 18:40:39 +0900</pubDate>
    </item>
  </channel>
</rss>