- generic [ref=f25e3]: - link "본문으로 건너뛰기" [ref=f25e4] [cursor=pointer]: - /url: "#main-content" - banner [ref=f25e5]: - generic [ref=f25e6]: - link "TechLog Studio" [ref=f25e7] [cursor=pointer]: - /url: /studio - text: TechLog - generic [ref=f25e8]: Studio - navigation "Studio 주 탐색" [ref=f25e10]: - link "작업본" [ref=f25e11] [cursor=pointer]: - /url: /studio/documents - link "게시 기록" [ref=f25e12] [cursor=pointer]: - /url: /studio/publications - link "새 문서" [ref=f25e13] [cursor=pointer]: - /url: /studio/documents/new - link "주제·프로젝트" [ref=f25e14] [cursor=pointer]: - /url: /studio/taxonomy - link "릴리즈" [ref=f25e15] [cursor=pointer]: - /url: /studio/releases - link "공개 사이트 보기" [ref=f25e16] [cursor=pointer]: - /url: / - button "로그아웃" [ref=f25e17] - main [ref=f25e18]: - generic [ref=f25e19]: - generic [ref=f25e20]: - region [ref=f25e21]: - generic [ref=f25e22]: - paragraph [ref=f25e23]: QUESTION · VERSION 6 - heading "문서 편집" [level=1] [ref=f25e24] - paragraph [ref=f25e25]: Round Trip과 Row Volume을 독립 측정할 것인가 - region [ref=f25e26]: - generic [ref=f25e27]: - paragraph [ref=f25e28]: DOCUMENT - heading "기본 정보" [level=2] [ref=f25e29] - generic [ref=f25e30]: - generic [ref=f25e31]: - generic [ref=f25e32]: 제목 - textbox "제목" [ref=f25e33]: Round Trip과 Row Volume을 독립 측정할 것인가 - generic [ref=f25e34]: - generic [ref=f25e35]: slug - textbox "slug" [ref=f25e36]: - /placeholder: 비우면 제목에서 만듭니다 (영문 소문자·숫자·하이픈) - text: isolate-round-trip-and-row-volume - generic [ref=f25e37]: - generic [ref=f25e38]: 요약 - textbox "요약" [ref=f25e39]: 현재 데이터셋은 N을 키우면 반환 부모 수, 자식 총 행수, 엔티티 생성량, DB 왕복이 동시에 늘어난다. 지연이 늘어난 원인을 어느 하나에 돌릴 수 없다. 변수를 하나씩 격리한 데이터셋을 만들지 결정하지 않았다. - generic [aria-hidden] [ref=f25e40]: 목록 카드에는 약 90자까지 보입니다 · 119 / 2000 - generic [ref=f25e41]: - generic [ref=f25e42]: Topic - combobox "Topic" [ref=f25e43]: - option "선택하지 않음" - option "JPA 피드 조회 성능" [selected] - option "OAuth/OIDC 인증 경계" - generic [ref=f25e44]: - generic [ref=f25e45]: Project - combobox "Project" [ref=f25e46]: - option "미지정" - option "Backend Clean Architecture" - option "KeyCloak Patterns" - option "Liner N + 1문제" [selected] - status [ref=f25e47] - group "축 — 고르지 않으면 이 주제의 공통 기록이 됩니다" [ref=f25e48]: - generic [ref=f25e50] [cursor=pointer]: - checkbox "파생 쿼리 그대로" [ref=f25e51] - generic [ref=f25e52]: 파생 쿼리 그대로 - generic [ref=f25e53] [cursor=pointer]: - checkbox "컬렉션 fetch join" [ref=f25e54] - generic [ref=f25e55]: 컬렉션 fetch join - generic [ref=f25e56] [cursor=pointer]: - checkbox "fetch join + 페이징" [ref=f25e57] - generic [ref=f25e58]: fetch join + 페이징 - group "관계" [ref=f25e59]: - generic [ref=f25e61]: - generic [ref=f25e62]: - generic [ref=f25e63]: 관계 1 대상 - combobox "관계 1 대상" [ref=f25e64]: - option "대상 선택" - option "Browser Token을 없애면서 BFF에 Session과 CSRF 책임이 생긴 과정" - option "Collection Fetch Join으로 N+1을 해결하다 만난 MultiBag예외와 행 폭증 문제" - option "Collection Fetch Join Pagination의 In-memory Paging" - option "DTO 변환 과정에서 발생한 Highlight 컬렉션 N+1" - option "Fetch 타입이 아닌 조회 방식으로 인한 N+1" [disabled] - option "Forward-Auth에서 Client가 보낸 Identity Header를 신뢰하면 안 되는 이유" - option "Projection 이후에도 1,509행을 읽은 Row Over-fetch" - option "Refresh Token 관리만 서버로 이전, Access Token은 여전히 Browser에 노출" - option "SPA에서 OAuth Token을 JavaScript Memory에 보관한 경우" - option "Visibility OR이 Keyset Index를 깨뜨린 문제" - option "Authorization Code와 PKCE가 보호하는 구간" - option "Bearer JWT가 인증된 principal이 되기까지" - option "Cookie로 인증하는 요청에서 CSRF token이 하는 일" - option "브라우저가 credential을 보관하는 위치와 그 성질" - option "Forward-Auth와 Nginx auth_request의 동작" - option "외부 IdP Brokering의 동작" - option "Authorization Code Flow의 Endpoint와 Credential 이동 기준" - option "BFF 인증 구조 설계 기준" - option "Feed Visibility Query Pattern" - option "Fetch Join · Batch · Projection 선택 기준" - option "Fetch Type과 Fetch Strategy 구분" - option "Forward-Auth에서 Identity Header를 신뢰하기 위한 조건" - option "외부 IdP 연동과 Application 인증 구조의 경계" - option "JPA N+1 정량 진단 기준" [selected] - option "Keyset Pagination 설계 기준" - option "OAuth/OIDC 인증 패턴 선택 기준" - option "OAuth Token과 Application Session을 구분하는 기준" - option "PostgreSQL Query Plan 측정 기준" - option "Public Client와 Confidential Client 구분 기준" - option "Top-N-per-group 선택 기준" - option "실제 동시 트래픽에서도 이 구조가 안정적인가" - option "서버 세션 기반 인증 구조는 다중 인스턴스에서 어떻게 운영할 것인가" - option "ANALYZE 이후 Cardinality Estimate는 어떻게 달라지는가" - option "BFF의 Session과 OAuth2AuthorizedClient를 어디에 저장할 것인가" - option "feed_visible을 Production CQRS로 승격할 것인가" - option "Forward-Auth 구조에서 Application Authorization을 어디까지 Edge에 둘 것인가" - option "Highlight 없는 FeedItem을 허용할 것인가" - option "Refresh Token Rotation과 다중 Replica 경쟁을 어떻게 처리할 것인가" - option "Round Trip과 Row Volume을 독립 측정할 것인가" - option "BFF가 OAuth Token을 관리하는 조건" - option "Collection Fetch Join과 Pagination을 같이 사용하지 않는다" - option "Entity Graph 조회에는 Batch Fetch를 사용한다" - option "Feed Pagination은 Keyset을 사용한다" - option "외부 IdP와의 연동이라도 별도의 인증 방식이 아니다." - option "Query Plan은 실제 PostgreSQL에서 측정한다" - option "Query Strategy는 FeedQueryPort 뒤에서 소유한다" - option "현재 Read Model은 CQRS-lite로 유지한다" - option "화면 조회는 Read Projection을 사용한다" - generic [ref=f25e65]: - generic [ref=f25e66]: 관계 1 이유 - textbox "관계 1 이유" [ref=f25e67]: 왕복과 행수를 다른 축으로 세는 기준이다. - generic [aria-hidden] [ref=f25e68]: 공개 화면의 「다음에 읽을 것」에 그대로 나갑니다. - generic [ref=f25e69]: - button "위로" [disabled] [ref=f25e70] - button "아래로" [ref=f25e71] - button "삭제" [ref=f25e72] - generic [ref=f25e73]: - generic [ref=f25e74]: - generic [ref=f25e75]: 관계 2 대상 - combobox "관계 2 대상" [ref=f25e76]: - option "대상 선택" - option "Browser Token을 없애면서 BFF에 Session과 CSRF 책임이 생긴 과정" - option "Collection Fetch Join으로 N+1을 해결하다 만난 MultiBag예외와 행 폭증 문제" - option "Collection Fetch Join Pagination의 In-memory Paging" - option "DTO 변환 과정에서 발생한 Highlight 컬렉션 N+1" - option "Fetch 타입이 아닌 조회 방식으로 인한 N+1" [selected] - option "Forward-Auth에서 Client가 보낸 Identity Header를 신뢰하면 안 되는 이유" - option "Projection 이후에도 1,509행을 읽은 Row Over-fetch" - option "Refresh Token 관리만 서버로 이전, Access Token은 여전히 Browser에 노출" - option "SPA에서 OAuth Token을 JavaScript Memory에 보관한 경우" - option "Visibility OR이 Keyset Index를 깨뜨린 문제" - option "Authorization Code와 PKCE가 보호하는 구간" - option "Bearer JWT가 인증된 principal이 되기까지" - option "Cookie로 인증하는 요청에서 CSRF token이 하는 일" - option "브라우저가 credential을 보관하는 위치와 그 성질" - option "Forward-Auth와 Nginx auth_request의 동작" - option "외부 IdP Brokering의 동작" - option "Authorization Code Flow의 Endpoint와 Credential 이동 기준" - option "BFF 인증 구조 설계 기준" - option "Feed Visibility Query Pattern" - option "Fetch Join · Batch · Projection 선택 기준" - option "Fetch Type과 Fetch Strategy 구분" - option "Forward-Auth에서 Identity Header를 신뢰하기 위한 조건" - option "외부 IdP 연동과 Application 인증 구조의 경계" - option "JPA N+1 정량 진단 기준" [disabled] - option "Keyset Pagination 설계 기준" - option "OAuth/OIDC 인증 패턴 선택 기준" - option "OAuth Token과 Application Session을 구분하는 기준" - option "PostgreSQL Query Plan 측정 기준" - option "Public Client와 Confidential Client 구분 기준" - option "Top-N-per-group 선택 기준" - option "실제 동시 트래픽에서도 이 구조가 안정적인가" - option "서버 세션 기반 인증 구조는 다중 인스턴스에서 어떻게 운영할 것인가" - option "ANALYZE 이후 Cardinality Estimate는 어떻게 달라지는가" - option "BFF의 Session과 OAuth2AuthorizedClient를 어디에 저장할 것인가" - option "feed_visible을 Production CQRS로 승격할 것인가" - option "Forward-Auth 구조에서 Application Authorization을 어디까지 Edge에 둘 것인가" - option "Highlight 없는 FeedItem을 허용할 것인가" - option "Refresh Token Rotation과 다중 Replica 경쟁을 어떻게 처리할 것인가" - option "Round Trip과 Row Volume을 독립 측정할 것인가" - option "BFF가 OAuth Token을 관리하는 조건" - option "Collection Fetch Join과 Pagination을 같이 사용하지 않는다" - option "Entity Graph 조회에는 Batch Fetch를 사용한다" - option "Feed Pagination은 Keyset을 사용한다" - option "외부 IdP와의 연동이라도 별도의 인증 방식이 아니다." - option "Query Plan은 실제 PostgreSQL에서 측정한다" - option "Query Strategy는 FeedQueryPort 뒤에서 소유한다" - option "현재 Read Model은 CQRS-lite로 유지한다" - option "화면 조회는 Read Projection을 사용한다" - generic [ref=f25e77]: - generic [ref=f25e78]: 관계 2 이유 - textbox [ref=f25e79] - generic [aria-hidden] [ref=f25e80]: 공개 화면의 「다음에 읽을 것」에 그대로 나갑니다. - generic [ref=f25e81]: - button "위로" [ref=f25e82] - button "아래로" [disabled] [ref=f25e83] - button "삭제" [ref=f25e84] - button "관계 추가" [ref=f25e85] - region [ref=f25e86]: - generic [ref=f25e87]: - paragraph [ref=f25e88]: QUESTION - heading "판단과 다음 검증" [level=2] [ref=f25e89] - generic [ref=f25e90]: - generic [ref=f25e91]: 질문 상태 - combobox "질문 상태" [ref=f25e92]: - option "아직 정하지 않음" - option "OPEN" [selected] - option "RESOLVED" - group "확인한 사실" [ref=f25e93]: - generic [ref=f25e95]: - generic [ref=f25e96]: - generic [ref=f25e97]: 확인한 사실 1 - textbox "확인한 사실 1" [ref=f25e98]: 하이라이트 개수는 순위 기반 편중 분포로 생성된다. 상한 500, 하한 1이다. - generic [ref=f25e99]: - button "위로" [disabled] [ref=f25e100] - button "아래로" [ref=f25e101] - button "삭제" [ref=f25e102] - generic [ref=f25e103]: - generic [ref=f25e104]: - generic [ref=f25e105]: 확인한 사실 2 - textbox "확인한 사실 2" [ref=f25e106]: 하이라이트 총량은 N에 정비례하지 않는다. N=10에서 1,285개인데 N=1,000에서 2,917개다. - generic [ref=f25e107]: - button "위로" [ref=f25e108] - button "아래로" [ref=f25e109] - button "삭제" [ref=f25e110] - generic [ref=f25e111]: - generic [ref=f25e112]: - generic [ref=f25e113]: 확인한 사실 3 - textbox "확인한 사실 3" [ref=f25e114]: 조회 수는 하이라이트 총량이 아니라 부모 수 N에 정비례한다. - generic [ref=f25e115]: - button "위로" [ref=f25e116] - button "아래로" [ref=f25e117] - button "삭제" [ref=f25e118] - generic [ref=f25e119]: - generic [ref=f25e120]: - generic [ref=f25e121]: 확인한 사실 4 - textbox "확인한 사실 4" [ref=f25e122]: N을 키우면 반환 부모 수, 자식 총 행수, 엔티티 생성량, 왕복이 함께 늘어난다. - generic [ref=f25e123]: - button "위로" [ref=f25e124] - button "아래로" [ref=f25e125] - button "삭제" [ref=f25e126] - generic [ref=f25e127]: - generic [ref=f25e128]: - generic [ref=f25e129]: 확인한 사실 5 - textbox "확인한 사실 5" [ref=f25e130]: 지연은 단일 스레드에서 7회 반복하고 앞 2회를 버린 뒤 5개 표본의 중앙값과 최댓값으로 기록했다. - generic [ref=f25e131]: - button "위로" [ref=f25e132] - button "아래로" [ref=f25e133] - button "삭제" [ref=f25e134] - generic [ref=f25e135]: - generic [ref=f25e136]: - generic [ref=f25e137]: 확인한 사실 6 - textbox "확인한 사실 6" [ref=f25e138]: 격리 데이터셋 세 종류를 계획했지만 아직 실행하지 않았다. - generic [ref=f25e139]: - button "위로" [ref=f25e140] - button "아래로" [disabled] [ref=f25e141] - button "삭제" [ref=f25e142] - button "확인한 사실 추가" [ref=f25e143] - group "가정" [ref=f25e144]: - generic [ref=f25e146]: - generic [ref=f25e147]: - generic [ref=f25e148]: 가정 1 - textbox "가정 1" [ref=f25e149]: 왕복 수와 전송 행수는 지연에 서로 다른 방식으로 기여한다. - generic [ref=f25e150]: - button "위로" [disabled] [ref=f25e151] - button "아래로" [ref=f25e152] - button "삭제" [ref=f25e153] - generic [ref=f25e154]: - generic [ref=f25e155]: - generic [ref=f25e156]: 가정 2 - textbox "가정 2" [ref=f25e157]: 부모 수만 바꾸고 자식 수를 고정하면 왕복의 기여를 분리할 수 있다. - generic [ref=f25e158]: - button "위로" [ref=f25e159] - button "아래로" [ref=f25e160] - button "삭제" [ref=f25e161] - generic [ref=f25e162]: - generic [ref=f25e163]: - generic [ref=f25e164]: 가정 3 - textbox "가정 3" [ref=f25e165]: 부모 수를 고정하고 자식 수만 바꾸면 과조회의 기여를 분리할 수 있다. - generic [ref=f25e166]: - button "위로" [ref=f25e167] - button "아래로" [disabled] [ref=f25e168] - button "삭제" [ref=f25e169] - button "가정 추가" [ref=f25e170] - group "남은 미지수" [ref=f25e171]: - generic [ref=f25e173]: - generic [ref=f25e174]: - generic [ref=f25e175]: 남은 미지수 1 - textbox "남은 미지수 1" [ref=f25e176]: 격리 데이터셋을 추가로 유지할 가치가 있는가. 시더와 테스트가 늘어난다. - generic [ref=f25e177]: - button "위로" [disabled] [ref=f25e178] - button "아래로" [ref=f25e179] - button "삭제" [ref=f25e180] - generic [ref=f25e181]: - generic [ref=f25e182]: - generic [ref=f25e183]: 남은 미지수 2 - textbox "남은 미지수 2" [ref=f25e184]: 부모 수만 바꾼 데이터셋에서 지연이 왕복 수에 선형으로 붙는가. - generic [ref=f25e185]: - button "위로" [ref=f25e186] - button "아래로" [ref=f25e187] - button "삭제" [ref=f25e188] - generic [ref=f25e189]: - generic [ref=f25e190]: - generic [ref=f25e191]: 남은 미지수 3 - textbox "남은 미지수 3" [ref=f25e192]: 자식 수만 바꾼 데이터셋에서 지연이 전송 행수에 어떻게 붙는가. - generic [ref=f25e193]: - button "위로" [ref=f25e194] - button "아래로" [ref=f25e195] - button "삭제" [ref=f25e196] - generic [ref=f25e197]: - generic [ref=f25e198]: - generic [ref=f25e199]: 남은 미지수 4 - textbox "남은 미지수 4" [ref=f25e200]: 두 기여를 분리해도 전략 선택이 달라지는가. 이미 배치와 프로젝션으로 둘 다 줄였다. - generic [ref=f25e201]: - button "위로" [ref=f25e202] - button "아래로" [ref=f25e203] - button "삭제" [ref=f25e204] - generic [ref=f25e205]: - generic [ref=f25e206]: - generic [ref=f25e207]: 남은 미지수 5 - textbox "남은 미지수 5" [ref=f25e208]: 편중 분포를 유지한 데이터셋과 격리 데이터셋을 모두 유지할 것인가, 격리 데이터셋으로 대체할 것인가. - generic [ref=f25e209]: - button "위로" [ref=f25e210] - button "아래로" [disabled] [ref=f25e211] - button "삭제" [ref=f25e212] - button "남은 미지수 추가" [ref=f25e213] - group "제약" [ref=f25e214]: - generic [ref=f25e216]: - generic [ref=f25e217]: - generic [ref=f25e218]: 제약 1 - textbox "제약 1" [ref=f25e219]: 현재 지연 값은 단일 스레드·warm cache 상대값이라 절대값 비교에 쓸 수 없다. 격리 데이터셋을 만들어도 이 한계는 그대로다. - generic [ref=f25e220]: - button "위로" [disabled] [ref=f25e221] - button "아래로" [ref=f25e222] - button "삭제" [ref=f25e223] - generic [ref=f25e224]: - generic [ref=f25e225]: - generic [ref=f25e226]: 제약 2 - textbox "제약 2" [ref=f25e227]: 실행하기 전에는 수치를 채우지 않는다. 예상값으로 표를 메우지 않는다. - generic [ref=f25e228]: - button "위로" [ref=f25e229] - button "아래로" [ref=f25e230] - button "삭제" [ref=f25e231] - generic [ref=f25e232]: - generic [ref=f25e233]: - generic [ref=f25e234]: 제약 3 - textbox "제약 3" [ref=f25e235]: 시더가 복잡해지면 기존 측정의 재현성에 영향을 줄 수 있다. 기존 데이터셋은 유지한 채 추가해야 한다. - generic [ref=f25e236]: - button "위로" [ref=f25e237] - button "아래로" [disabled] [ref=f25e238] - button "삭제" [ref=f25e239] - button "제약 추가" [ref=f25e240] - group "검토한 선택지" [ref=f25e241]: - generic [ref=f25e243]: - generic [ref=f25e244]: - generic [ref=f25e245]: 선택지 1 제목 - textbox "선택지 1 제목" [ref=f25e246]: 세 데이터셋을 모두 만든다 - generic [ref=f25e247]: - generic [ref=f25e248]: 선택지 1 설명 - textbox "선택지 1 설명" [ref=f25e249]: 부모 수만 바꾼 것, 자식 수만 바꾼 것, 편중을 유지한 것 세 가지를 유지한다. 각 변수의 기여를 따로 볼 수 있다. 시더와 테스트가 늘어나고 실행 시간도 길어진다. - generic [ref=f25e250]: - button "위로" [disabled] [ref=f25e251] - button "아래로" [ref=f25e252] - button "삭제" [ref=f25e253] - generic [ref=f25e254]: - generic [ref=f25e255]: - generic [ref=f25e256]: 선택지 2 제목 - textbox "선택지 2 제목" [ref=f25e257]: 편중 데이터셋만 유지하고 격리는 하지 않는다 - generic [ref=f25e258]: - generic [ref=f25e259]: 선택지 2 설명 - textbox "선택지 2 설명" [ref=f25e260]: 전략 선택이 이미 정해졌다면 원인 분해가 결정을 바꾸지 않는다. 현재 데이터셋으로 회귀만 지킨다. 나중에 지연 원인을 따져야 할 때 다시 만들어야 한다. - generic [ref=f25e261]: - button "위로" [ref=f25e262] - button "아래로" [ref=f25e263] - button "삭제" [ref=f25e264] - generic [ref=f25e265]: - generic [ref=f25e266]: - generic [ref=f25e267]: 선택지 3 제목 - textbox "선택지 3 제목" [ref=f25e268]: 필요할 때만 한시적으로 만든다 - generic [ref=f25e269]: - generic [ref=f25e270]: 선택지 3 설명 - textbox "선택지 3 설명" [ref=f25e271]: 특정 판단이 필요해지는 시점에 격리 데이터셋을 만들고 측정한 뒤 남기지 않는다. 측정 시점마다 시더를 다시 맞춰야 해서 재현성이 떨어진다. - generic [ref=f25e272]: - button "위로" [ref=f25e273] - button "아래로" [disabled] [ref=f25e274] - button "삭제" [ref=f25e275] - button "선택지 추가" [ref=f25e276] - generic [ref=f25e277]: - generic [ref=f25e278]: 다음 검증 - textbox "다음 검증" [ref=f25e279]: 1. 부모 수만 바꾼 데이터셋을 만든다. 부모마다 자식을 정확히 1개씩 둔다. 2. 부모 수를 고정하고 자식 수만 바꾼 데이터셋을 만든다. 3. 두 데이터셋에서 왕복 수, 전송 행수, 지연을 각각 측정한다. 4. 지연이 어느 변수에 어떻게 붙는지 확인한다. 5. 분해 결과가 이미 내린 전략 선택을 바꾸는지 본다. 바꾸지 않는다면 격리 데이터셋을 상시 유지할 필요가 있는지 다시 판단한다. - region [ref=f25e280]: - generic [ref=f25e281]: - paragraph [ref=f25e282]: LIVE - heading "즉시 미리보기" [level=2] [ref=f25e283] - generic [ref=f25e286]: - generic [ref=f25e287]: - navigation "문서 경로" [ref=f25e288]: - link "열린 질문" [ref=f25e289] [cursor=pointer]: - /url: /explore/questions - generic [aria-hidden] [ref=f25e290]: / - generic [ref=f25e291]: JPA 피드 조회 성능 - generic [aria-hidden] [ref=f25e292]: / - link "Liner N + 1문제" [ref=f25e293] [cursor=pointer]: - /url: /projects/liner-n-plus-1 - heading "Round Trip과 Row Volume을 독립 측정할 것인가" [level=1] [ref=f25e294] - paragraph [ref=f25e295]: 현재 데이터셋은 N을 키우면 반환 부모 수, 자식 총 행수, 엔티티 생성량, DB 왕복이 동시에 늘어난다. 지연이 늘어난 원인을 어느 하나에 돌릴 수 없다. 변수를 하나씩 격리한 데이터셋을 만들지 결정하지 않았다. - generic [ref=f25e296]: - generic [ref=f25e297]: - term [ref=f25e298]: 유형 - definition [ref=f25e299]: 열린 질문 - generic [ref=f25e300]: - term [ref=f25e301]: 프로젝트 - definition [ref=f25e302]: Liner N + 1문제 - generic [ref=f25e303]: - term [ref=f25e304]: 게시 - definition [ref=f25e305]: 게시 전 - paragraph [ref=f25e306]: OPEN - article [ref=f25e307]: - region [ref=f25e308]: - heading "확인한 사실" [level=2] [ref=f25e309] - list [ref=f25e310]: - listitem [ref=f25e311]: - paragraph [ref=f25e312]: 하이라이트 개수는 순위 기반 편중 분포로 생성된다. 상한 500, 하한 1이다. - listitem [ref=f25e313]: - paragraph [ref=f25e314]: 하이라이트 총량은 N에 정비례하지 않는다. N=10에서 1,285개인데 N=1,000에서 2,917개다. - listitem [ref=f25e315]: - paragraph [ref=f25e316]: 조회 수는 하이라이트 총량이 아니라 부모 수 N에 정비례한다. - listitem [ref=f25e317]: - paragraph [ref=f25e318]: N을 키우면 반환 부모 수, 자식 총 행수, 엔티티 생성량, 왕복이 함께 늘어난다. - listitem [ref=f25e319]: - paragraph [ref=f25e320]: 지연은 단일 스레드에서 7회 반복하고 앞 2회를 버린 뒤 5개 표본의 중앙값과 최댓값으로 기록했다. - listitem [ref=f25e321]: - paragraph [ref=f25e322]: 격리 데이터셋 세 종류를 계획했지만 아직 실행하지 않았다. - region [ref=f25e323]: - heading "가정" [level=2] [ref=f25e324] - list [ref=f25e325]: - listitem [ref=f25e326]: - paragraph [ref=f25e327]: 왕복 수와 전송 행수는 지연에 서로 다른 방식으로 기여한다. - listitem [ref=f25e328]: - paragraph [ref=f25e329]: 부모 수만 바꾸고 자식 수를 고정하면 왕복의 기여를 분리할 수 있다. - listitem [ref=f25e330]: - paragraph [ref=f25e331]: 부모 수를 고정하고 자식 수만 바꾸면 과조회의 기여를 분리할 수 있다. - region [ref=f25e332]: - heading "남은 미지수" [level=2] [ref=f25e333] - list [ref=f25e334]: - listitem [ref=f25e335]: - paragraph [ref=f25e336]: 격리 데이터셋을 추가로 유지할 가치가 있는가. 시더와 테스트가 늘어난다. - listitem [ref=f25e337]: - paragraph [ref=f25e338]: 부모 수만 바꾼 데이터셋에서 지연이 왕복 수에 선형으로 붙는가. - listitem [ref=f25e339]: - paragraph [ref=f25e340]: 자식 수만 바꾼 데이터셋에서 지연이 전송 행수에 어떻게 붙는가. - listitem [ref=f25e341]: - paragraph [ref=f25e342]: 두 기여를 분리해도 전략 선택이 달라지는가. 이미 배치와 프로젝션으로 둘 다 줄였다. - listitem [ref=f25e343]: - paragraph [ref=f25e344]: 편중 분포를 유지한 데이터셋과 격리 데이터셋을 모두 유지할 것인가, 격리 데이터셋으로 대체할 것인가. - region [ref=f25e345]: - heading "제약" [level=2] [ref=f25e346] - list [ref=f25e347]: - listitem [ref=f25e348]: - paragraph [ref=f25e349]: 현재 지연 값은 단일 스레드·warm cache 상대값이라 절대값 비교에 쓸 수 없다. 격리 데이터셋을 만들어도 이 한계는 그대로다. - listitem [ref=f25e350]: - paragraph [ref=f25e351]: 실행하기 전에는 수치를 채우지 않는다. 예상값으로 표를 메우지 않는다. - listitem [ref=f25e352]: - paragraph [ref=f25e353]: 시더가 복잡해지면 기존 측정의 재현성에 영향을 줄 수 있다. 기존 데이터셋은 유지한 채 추가해야 한다. - region [ref=f25e354]: - heading "검토한 선택지" [level=2] [ref=f25e355] - list [ref=f25e356]: - listitem [ref=f25e357]: - heading "세 데이터셋을 모두 만든다" [level=3] [ref=f25e358] - paragraph [ref=f25e359]: 부모 수만 바꾼 것, 자식 수만 바꾼 것, 편중을 유지한 것 세 가지를 유지한다. 각 변수의 기여를 따로 볼 수 있다. - paragraph [ref=f25e360]: 시더와 테스트가 늘어나고 실행 시간도 길어진다. - listitem [ref=f25e361]: - heading "편중 데이터셋만 유지하고 격리는 하지 않는다" [level=3] [ref=f25e362] - paragraph [ref=f25e363]: 전략 선택이 이미 정해졌다면 원인 분해가 결정을 바꾸지 않는다. 현재 데이터셋으로 회귀만 지킨다. - paragraph [ref=f25e364]: 나중에 지연 원인을 따져야 할 때 다시 만들어야 한다. - listitem [ref=f25e365]: - heading "필요할 때만 한시적으로 만든다" [level=3] [ref=f25e366] - paragraph [ref=f25e367]: 특정 판단이 필요해지는 시점에 격리 데이터셋을 만들고 측정한 뒤 남기지 않는다. - paragraph [ref=f25e368]: 측정 시점마다 시더를 다시 맞춰야 해서 재현성이 떨어진다. - region [ref=f25e369]: - paragraph [ref=f25e370]: Next - heading "다음 검증" [level=2] [ref=f25e371] - paragraph [ref=f25e372]: 1. 부모 수만 바꾼 데이터셋을 만든다. 부모마다 자식을 정확히 1개씩 둔다. - paragraph [ref=f25e373]: 2. 부모 수를 고정하고 자식 수만 바꾼 데이터셋을 만든다. - paragraph [ref=f25e374]: 3. 두 데이터셋에서 왕복 수, 전송 행수, 지연을 각각 측정한다. - paragraph [ref=f25e375]: 4. 지연이 어느 변수에 어떻게 붙는지 확인한다. - paragraph [ref=f25e376]: 5. 분해 결과가 이미 내린 전략 선택을 바꾸는지 본다. 바꾸지 않는다면 격리 데이터셋을 상시 유지할 필요가 있는지 다시 판단한다. - region [ref=f25e377]: - paragraph [ref=f25e378]: Next - heading "다음에 읽을 것" [level=2] [ref=f25e379] - list [ref=f25e380]: - listitem [ref=f25e381]: - link "검증 기록 Fetch 타입이 아닌 조회 방식으로 인한 N+1" [ref=f25e382] [cursor=pointer]: - /url: /cases/eager-toone-nplus1-without-access - generic [ref=f25e383]: 검증 기록 - strong [ref=f25e385]: Fetch 타입이 아닌 조회 방식으로 인한 N+1 - generic [aria-hidden] [ref=f25e386]: ↗ - complementary [ref=f25e387]: - heading "작업 상태" [level=2] [ref=f25e388] - status "편집 상태" [ref=f25e389]: 저장되지 않음 - generic [ref=f25e390]: - generic [ref=f25e391]: - term [ref=f25e392]: 저장 버전 - definition [ref=f25e393]: "6" - generic [ref=f25e394]: - term [ref=f25e395]: 종류 - definition [ref=f25e396]: 열린 질문 - paragraph [ref=f25e397]: 불완전한 초안도 저장할 수 있습니다. Ctrl+S 로도 저장합니다. 게시를 누르면 채워야 할 칸을 그 자리에 표시합니다. - generic [ref=f25e398]: - button "저장" [ref=f25e399] - button "게시" [ref=f25e400] - paragraph [ref=f25e401]