Skip to content

perf: FK 컬럼에 누락된 인덱스 5건 추가 - #244

Merged
unam98 merged 1 commit into
mainfrom
perf/add-missing-fk-indexes
Aug 12, 2026
Merged

perf: FK 컬럼에 누락된 인덱스 5건 추가#244
unam98 merged 1 commit into
mainfrom
perf/add-missing-fk-indexes

Conversation

@unam98

@unam98unam98 commented Aug 12, 2026

Copy link
Copy Markdown
Collaborator

작업 배경

  • 백엔드 성능 최적화 지점을 찾던 중, PostgreSQL은 MySQL(InnoDB)과 달리 FK 컬럼을 자동으로 인덱싱하지 않는다는 점을 확인
  • 실제로 pg_indexes로 조회해보니 course.user_id, record.user_id, user_stamp.user_id, scrap.public_course_id, heart_rate_sample.record_health_data_id 5개 FK 컬럼에 인덱스가 없었음
  • 처음엔 Course.path(geometry) 컬럼에 GiST 공간 인덱스가 필요하다고 가정했으나, 코드 전체를 grep한 결과 공간 쿼리(ST_* 함수)를 실제로 사용하는 곳이 없어 기각 — FK 컬럼 누락이 진짜 후보였음

변경 사항

영역내용
Course.java@Table(indexes = {@Index(columnList = "user_id")}) 추가
Record.java@Table(indexes = {@Index(columnList = "user_id")}) 추가
UserStamp.java@Table(indexes = {@Index(columnList = "user_id")}) 추가
Scrap.java기존 uniqueConstraintsindexes = {@Index(columnList = "public_course_id")} 추가
HeartRateSample.java@Table(indexes = {@Index(columnList = "record_health_data_id")}) 추가

ddl-auto: update로 스키마를 관리하므로 별도 마이그레이션 없이 배포 시 자동 반영됨.

선택지 및 근거

  • 대안 1 (기각): 전체 FK 컬럼에 무조건 인덱스 추가 — 쓰기 비용이 늘어나므로 실제 조회 패턴에서 쓰이는 FK만 선별
  • 대안 2 (기각): Course.path(geometry)에 GiST 공간 인덱스 추가 — grep으로 공간 쿼리 미사용 확인 후 기각. 불필요한 인덱스는 쓰기 성능만 깎아먹음
  • 채택: 실제 리포지토리에서 FK 기준으로 조회되는 컬럼에만 한정

영향 범위

  • 마이페이지 코스/기록 목록, 유저 스탬프 조회, 스크랩 여부 확인, 심박수 샘플 조회 등 FK 기준 조회 전반
  • 스키마 변경(인덱스 추가)만 있고 API 응답/동작은 동일 — 런타임 영향 없음, 조회 속도만 개선

검증 매트릭스

30만 건 규모 synthetic 데이터로 EXPLAIN (ANALYZE, BUFFERS) 직접 측정 (Docker 로컬 Postgres, 인덱스 추가 전/후 비교):

대상BeforeAfter실행 계획 변화
course.user_id52.516ms3.258msSeq Scan → Bitmap Heap Scan (idx_course_user_id)
record.user_id11.081ms2.704msParallel Seq Scan → Bitmap Heap Scan (idx_record_user_id)
user_stamp.user_id15.984ms1.835msSeq Scan → Index Scan (idx_user_stamp_user_id)
scrap.public_course_id3.696ms1.233msSeq Scan → Bitmap Heap Scan (idx_scrap_public_course_id)
heart_rate_sample.record_health_data_id8.507ms0.429msParallel Seq Scan → Bitmap Heap Scan (idx_heart_rate_sample_record_health_data_id)

측정 후 synthetic 데이터는 전량 삭제, 실 데이터 baseline 행 수 복원 확인 완료.

Test Plan

  • 로컬 전체 테스트 스위트 254개 통과 (./gradlew test, 실 Postgres/Redis 컨테이너 기준)
  • EXPLAIN (ANALYZE, BUFFERS)로 인덱스 전/후 실측 비교

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Performance
    • Improved database query performance for courses, records, heart-rate samples, scraps, and user stamps.
    • Added indexes to frequently searched relationships and identifiers.

PostgreSQL은 MySQL(InnoDB)과 달리 FK 컬럼을 자동으로 인덱싱하지 않는다.
course/record/user_stamp/scrap/heart_rate_sample의 FK 컬럼에 인덱스가
없어 관련 조회가 Seq Scan으로 처리되고 있었다.
EXPLAIN (ANALYZE, BUFFERS)로 30만 건 규모 합성 데이터에서 실측:
- course.user_id: 52.516ms → 3.258ms (Seq Scan → Bitmap Heap Scan)
- record.user_id: 11.081ms → 2.704ms (Parallel Seq Scan → Bitmap Heap Scan)
- user_stamp.user_id: 15.984ms → 1.835ms (Seq Scan → Index Scan)
- scrap.public_course_id: 3.696ms → 1.233ms (Seq Scan → Bitmap Heap Scan)
- heart_rate_sample.record_health_data_id: 8.507ms → 0.429ms (Parallel Seq Scan → Bitmap Heap Scan)
@coderabbitai

coderabbitaiBot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: c06f9a71-a755-4556-bee8-5b5d5f0d5249

📥 Commits

Reviewing files that changed from the base of the PR and between b4c6ae3 and 4f7f917.

📒 Files selected for processing (5)
  • src/main/java/org/runnect/server/course/entity/Course.java
  • src/main/java/org/runnect/server/health/entity/HeartRateSample.java
  • src/main/java/org/runnect/server/record/entity/Record.java
  • src/main/java/org/runnect/server/scrap/entity/Scrap.java
  • src/main/java/org/runnect/server/user/entity/UserStamp.java

📝 Walkthrough

Walkthrough

Changes

Database indexes

Layer / File(s)Summary
Entity index mappings
src/main/java/org/runnect/server/{course,health,record,scrap,user}/entity/*.java
Added named JPA table indexes for user_id, record_health_data_id, and public_course_id while preserving the existing Scrap unique constraint.

Estimated code review effort: 1 (Trivial) | ~5 minutes

Suggested reviewers:yusuhwa-ve, rinrinpark, funnysunny08

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check nameStatusExplanation
Description Check✅ PassedCheck skipped - CodeRabbit’s high-level summary is enabled.
Title check✅ PassedThe title clearly and concisely describes the addition of five missing indexes on foreign-key columns.
Docstring Coverage✅ PassedNo functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check✅ PassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes check✅ PassedCheck skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/add-missing-fk-indexes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@unam98
unam98 merged commit 81126d5 into mainAug 12, 2026
2 checks passed
@unam98
unam98 deleted the perf/add-missing-fk-indexes branch August 12, 2026 08:27
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@unam98@alh0409