-
Notifications
You must be signed in to change notification settings - Fork 1
[Perf] Vector 저장 TPS 및 JDBC Batch Size 비교 #156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
6c02304
docs: #155 Vector 저장 Batch benchmark 설계
Gimini-3 d1dbb34
test: #155 Vector 저장 TPS Batch benchmark 구현
Gimini-3 e83ce8b
docs: #155 Vector 저장 Batch 실측 결과 기록
Gimini-3 7c8a02d
Merge remote-tracking branch 'origin/develop' into feature/155
Gimini-3 7dc31e7
docs: #155 벤치마크 문서 리뷰 반영
Gimini-3 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
124 changes: 124 additions & 0 deletions
124
docs/design/gimin-#155-vector-storage-batch-benchmark.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| # Vector 저장 TPS·Batch Size 비교 Benchmark 설계 | ||
|
|
||
| - 작성일: 2026-08-11 | ||
| - 상태: 구현 및 로컬 실측 완료 | ||
|
|
||
| ## 1. 배경 | ||
|
|
||
| DocGrid는 `vector(1024)`와 HNSW 검색 구조, Exact·HNSW 검색 지연과 Recall을 검증했다. 그러나 실제 | ||
| Vector 저장 경로는 기능·원자성만 확인했고, 데이터 규모와 JDBC Batch Size가 저장 처리량에 미치는 | ||
| 영향은 측정하지 않았다. 검색이 빨라도 Embedding 결과를 DB에 넣는 단계가 느리면 전체 인덱싱 처리량의 | ||
| 병목이 될 수 있다. | ||
|
|
||
| 이번 Benchmark는 실제 PostgreSQL 17 + pgvector에서 제품과 같은 1024차원 Vector·HNSW 구조를 | ||
| 사용해 1천·1만·10만 건 저장 TPS와 Batch 호출 수, 저장 공간을 비교한다. | ||
|
|
||
| ## 2. 목표와 성공 기준 | ||
|
|
||
| - 1,000·10,000·100,000건을 같은 Vector Pool과 Schema에서 비교한다. | ||
| - JDBC Batch Size 1·100·500·1,000을 같은 Transaction 경계로 비교한다. | ||
| - Vector 생성 시간은 측정에서 제외하고 Bind·Batch 전송·DB 반영·Commit을 포함한다. | ||
| - 모든 Profile에서 Row 수, `vector_dims=1024`, HNSW Index와 Batch 실행 횟수를 검증한다. | ||
| - Profile별 Warm-up 뒤 본 측정 2회를 실행해 Duration·TPS 분포를 기록한다. | ||
| - 결과를 재현 가능한 JSON과 `docs/test-results/` 문서로 남긴다. | ||
| - 제품 Table, Migration, Repository와 운영 설정은 변경하지 않는다. | ||
|
|
||
| ## 3. 비교 Profile | ||
|
|
||
| | 축 | 값 | | ||
| |---|---| | ||
| | 저장 건수 | `1,000`, `10,000`, `100,000` | | ||
| | JDBC Batch Size | `1`, `100`, `500`, `1,000` | | ||
| | Vector | Dense 1024차원, Seed 고정 Normalized Vector Pool | | ||
| | Index | `vector_cosine_ops`, HNSW `m=16`, `ef_construction=64` | | ||
| | Transaction | Profile Round당 한 Transaction, 마지막 Commit 포함 | | ||
| | Warm-up | Profile별 최대 1,000건 1회 | | ||
| | 본 측정 | Profile별 2회 | | ||
|
|
||
| 총 12개 Profile을 측정한다. Batch Size가 Row 수보다 큰 경우 한 번의 `executeBatch()`로 처리한다. | ||
|
|
||
| ## 4. 측정 경계 | ||
|
|
||
| ### 4.1 포함 | ||
|
|
||
| 1. PreparedStatement Parameter Bind | ||
| 2. `addBatch()` | ||
| 3. 설정한 크기마다 `executeBatch()` | ||
| 4. PostgreSQL `vector(1024)` 저장과 HNSW Online Index 갱신 | ||
| 5. Transaction Commit | ||
|
|
||
| ### 4.2 제외 | ||
|
|
||
| - BGE-M3 추론과 HTTP 전송 | ||
| - Chunk 생성과 JPA Entity 변환 | ||
| - Connection 획득과 Table·Index 생성 | ||
| - Vector 값 생성과 문자열 직렬화 | ||
| - HNSW 신규 Index 일괄 구축 시간 | ||
| - 검색 지연과 Recall | ||
|
|
||
| Vector 문자열은 측정 전에 1,024개를 결정적으로 만들어 재사용한다. 저장 자체가 아닌 Random Vector | ||
| 생성·정규화·문자열 변환 비용이 TPS에 섞이지 않게 하기 위해서다. | ||
|
|
||
| ## 5. 격리와 실행 순서 | ||
|
|
||
| - Test마다 고유 Schema를 사용하고 종료 시 Schema 전체를 삭제한다. | ||
| - 전용 Probe Table만 생성하며 제품 `embeddings` Table은 사용하지 않는다. | ||
| - HNSW Index는 빈 Table에 먼저 생성해 Online Insert 비용을 모든 Profile에 동일하게 포함한다. | ||
| - Warm-up과 각 측정 Round 앞에서 `TRUNCATE`해 같은 빈 Table 상태에서 시작한다. | ||
| - 데이터 규모마다 Batch Size 시작 순서를 회전해 실행 순서 편향을 줄인다. | ||
| - 전용 Gradle Task는 직렬로 실행하고 일반 `test`에서는 제외한다. | ||
|
|
||
| ## 6. 지표와 불변식 | ||
|
|
||
| | 지표 | 계산·검증 | | ||
| |---|---| | ||
| | Duration | 첫 Bind 직전부터 Commit 완료까지 | | ||
| | TPS | `rowCount / durationSeconds` | | ||
| | Batch 실행 수 | 실제 `executeBatch()` 호출 횟수 | | ||
| | 기대 Batch 수 | `ceil(rowCount / batchSize)` | | ||
| | Row 수 | Profile Round 종료 뒤 정확히 입력 건수와 일치 | | ||
| | Vector 차원 | `min(vector_dims)=max(vector_dims)=1024` | | ||
| | Table·Index 크기 | `pg_table_size`, `pg_relation_size` | | ||
| | HNSW 계약 | `pg_indexes.indexdef`에 전용 Index와 `USING hnsw` 존재 | | ||
|
|
||
| `executeBatch()` 결과에 `EXECUTE_FAILED`가 있거나 기대 행 수와 결과 수가 다르면 즉시 실패한다. 측정 | ||
| 오류를 낮은 TPS처럼 기록하지 않는다. | ||
|
|
||
| ## 7. 결과 해석 원칙 | ||
|
|
||
| 1. 같은 데이터 규모 안에서 Batch Size별 TPS와 Batch 호출 감소를 비교한다. | ||
| 2. 1천→1만→10만 건에서 같은 Batch Size의 TPS가 어떻게 변하는지 확인한다. | ||
| 3. 가장 높은 TPS 하나만 고르지 않고 100·500·1,000의 개선 폭이 작아지는 지점을 찾는다. | ||
| 4. 로컬 Docker 절대 TPS는 운영 SLO가 아니라 상대 비교 기준선으로 사용한다. | ||
| 5. 결과만으로 제품 Repository 저장 방식을 변경하지 않고 Pipeline E2E에서 다시 검증한다. | ||
|
|
||
| ## 8. 실행 방법 | ||
|
|
||
| ```bash | ||
| docker compose up -d postgres | ||
| DB_SSLMODE=disable ./gradlew vectorStoragePerformanceTest | ||
| ``` | ||
|
|
||
| Smoke 실행은 다음처럼 규모와 Round를 줄인다. | ||
|
|
||
| ```bash | ||
| DB_SSLMODE=disable ./gradlew vectorStoragePerformanceTest \ | ||
| -Dvector.storage.performance.sizes=1000 \ | ||
| -Dvector.storage.performance.batch-sizes=1,100 \ | ||
| -Dvector.storage.performance.measured-runs=1 | ||
| ``` | ||
|
|
||
| 기본 결과는 `build/reports/vector-storage/vector-storage-latest.json`에 생성한다. | ||
|
|
||
| 실측 결과와 원본 데이터는 다음 파일에 보존한다. | ||
|
|
||
| - `docs/test-results/gimin-#155-vector-storage-batch-benchmark.md` | ||
| - `docs/test-results/gimin-vector-storage-batch-benchmark-data.json` | ||
|
|
||
| ## 9. 완료 조건 | ||
|
|
||
| - 전용 Task가 12개 Profile을 실제 PostgreSQL에서 완료한다. | ||
| - 1천·1만·10만 건과 Batch 1·100·500·1,000 결과가 모두 기록된다. | ||
| - Row·차원·HNSW·Batch 호출 불변식이 자동 검증된다. | ||
| - 일반 회귀 테스트가 실제 대량 적재 없이 성공한다. | ||
| - JSON과 최종 결과 문서에 환경·수치·결론·한계가 기록된다. | ||
111 changes: 111 additions & 0 deletions
111
docs/test-results/gimin-#155-vector-storage-batch-benchmark.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| # Vector 저장 TPS·Batch Size 비교 결과 | ||
|
|
||
| - 측정일: 2026-08-11 | ||
| - 결과: 성공 | ||
| - 원본 데이터: [gimin-vector-storage-batch-benchmark-data.json](gimin-vector-storage-batch-benchmark-data.json) | ||
| - 설계: [gimin-#155-vector-storage-batch-benchmark.md](../design/gimin-%23155-vector-storage-batch-benchmark.md) | ||
|
|
||
| ## 1. 결론 | ||
|
|
||
| 실제 PostgreSQL 17.8과 pgvector 0.8.1에서 1,000·10,000·100,000개의 1024차원 Vector를 | ||
| HNSW Index가 이미 존재하는 Table에 저장했다. JDBC Batch는 데이터가 커질수록 효과가 뚜렷했다. | ||
|
|
||
| - 100,000건 기준 Batch 1의 p50은 `1,130.06 rows/s`, Batch 1,000은 `1,631.31 rows/s`였다. | ||
| - Batch 1,000은 Batch 1보다 처리량이 `44.36%` 높고, p50 저장 시간은 약 `22.63초` 짧았다. | ||
| - 같은 조건에서 `executeBatch()` 호출은 `100,000회 → 100회`로 `99.9%` 감소했다. | ||
| - Batch 100도 `1,559.21 rows/s`로 Batch 1,000 성능의 약 `95.58%`를 확보했다. | ||
| - 1,000건에서는 Batch 100이 가장 빨랐다. 작은 입력에 과도한 Batch를 적용할 이점은 확인되지 않았다. | ||
|
|
||
| 따라서 대량 Vector 적재의 로컬 기준선은 Batch 100~1,000이 타당하다. 다만 이번 결과만으로 제품의 | ||
| 기본 Batch Size를 확정하지 않고, 실제 Chunk·Embedding Pipeline의 메모리와 Transaction 시간을 함께 | ||
| 측정한 뒤 결정한다. | ||
|
|
||
| ## 2. 측정 환경 | ||
|
|
||
| | 항목 | 값 | | ||
| |---|---| | ||
| | Database | PostgreSQL 17.8 (Debian 17.8-1.pgdg12+1) | | ||
| | pgvector | 0.8.1 | | ||
| | JDBC Driver | PostgreSQL JDBC Driver 42.7.11 | | ||
| | Architecture | aarch64 | | ||
| | Java | 17.0.18 | | ||
| | Vector | 1024차원, Seed 고정 Normalized Vector Pool 1,024개 | | ||
| | HNSW | cosine, `m=16`, `ef_construction=64` | | ||
| | Warm-up | Profile별 최대 1,000건 | | ||
| | 본 측정 | Profile별 2회 | | ||
| | 측정 경계 | 첫 Bind 직전부터 HNSW Online 갱신과 Commit 완료까지 | | ||
|
|
||
| ## 3. 전체 결과 | ||
|
|
||
| TPS와 시간은 각 지표의 2회 표본을 독립적으로 정렬해 계산한 nearest-rank p50이다. 따라서 같은 | ||
| Profile의 TPS p50과 시간 p50도 서로 다른 Round에서 나올 수 있으며, 두 값을 서로 환산하면 안 된다. | ||
| 표본 수가 작으므로 절대 성능 SLO가 아니라 같은 로컬 환경의 상대 기준선으로 해석한다. | ||
|
|
||
| | 저장 건수 | Batch Size | TPS p50 | 시간 p50 | Batch 호출 | Batch 1 대비 TPS | 총 저장 크기 | | ||
| |---:|---:|---:|---:|---:|---:|---:| | ||
| | 1,000 | 1 | 414.81 | 2.389초 | 1,000 | 기준 | 13.23 MiB | | ||
| | 1,000 | 100 | **472.13** | **2.116초** | 10 | **+13.82%** | 13.23 MiB | | ||
| | 1,000 | 500 | 467.97 | 2.119초 | 2 | +12.82% | 13.23 MiB | | ||
| | 1,000 | 1,000 | 464.08 | 2.133초 | 1 | +11.88% | 13.23 MiB | | ||
| | 10,000 | 1 | 1,143.18 | 8.708초 | 10,000 | 기준 | 61.38 MiB | | ||
| | 10,000 | 100 | 1,524.13 | 6.467초 | 100 | +33.32% | 61.38 MiB | | ||
| | 10,000 | 500 | 1,397.54 | 6.922초 | 20 | +22.25% | 61.40 MiB | | ||
| | 10,000 | 1,000 | **1,533.76** | **6.495초** | 10 | **+34.17%** | 61.38 MiB | | ||
| | 100,000 | 1 | 1,130.06 | 83.220초 | 100,000 | 기준 | 732.80 MiB | | ||
| | 100,000 | 100 | 1,559.21 | 61.827초 | 1,000 | +37.98% | 733.44 MiB | | ||
| | 100,000 | 500 | 1,580.80 | 62.624초 | 200 | +39.89% | 732.71 MiB | | ||
| | 100,000 | 1,000 | **1,631.31** | **60.590초** | 100 | **+44.36%** | 734.47 MiB | | ||
|
|
||
| ## 4. 해석 | ||
|
|
||
| ### 4.1 Batch가 줄인 비용 | ||
|
|
||
| Batch Size가 커지면 JDBC 왕복과 `executeBatch()` 호출 수가 감소했다. 특히 100,000건에서 Batch 100은 | ||
| 호출 수를 99%, Batch 1,000은 99.9% 줄였다. 실제 TPS도 함께 개선됐으므로 호출 감소가 단순한 코드상 | ||
| 차이가 아니라 저장 처리량에 영향을 줬다. | ||
|
|
||
| ### 4.2 처리량이 무한히 증가하지 않는 이유 | ||
|
|
||
| 모든 Profile은 HNSW Index를 먼저 만든 뒤 Vector를 Online Insert했다. Batch 100~1,000에서도 처리량이 | ||
| 약 1.6천 rows/s 부근에 머문 것은 JDBC 호출 외에 1024차원 Vector 저장과 HNSW 갱신 비용이 남아 있기 | ||
| 때문이다. 이 Benchmark는 빠른 Bulk Import가 아니라 실제 온라인 인덱싱 경로에 가까운 기준선이다. | ||
|
|
||
| ### 4.3 단조 증가를 주장하지 않는 이유 | ||
|
|
||
| 10,000건의 Batch 500은 Batch 100과 1,000보다 낮았다. Profile별 본 측정이 2회이고 로컬 Docker의 | ||
| I/O·CPU 변동이 있으므로 모든 규모에서 Batch가 커질수록 TPS가 반드시 증가한다고 결론 내릴 수 없다. | ||
| 운영 기본값을 고르기 전 5회 이상 반복하고 실제 Chunk 크기·Worker 동시성을 포함해 재검증해야 한다. | ||
|
|
||
| ### 4.4 저장 공간 | ||
|
|
||
| 100,000건의 Table은 약 533.2 MiB, HNSW Index는 약 199.5~201.2 MiB였다. Batch Size는 전송 방식만 | ||
| 바꾸므로 관계 크기는 실질적으로 같았고, 작은 차이는 HNSW 구성과 Page 배치 변동 범위였다. | ||
|
|
||
| ## 5. 자동 검증 | ||
|
|
||
| 각 Round에서 다음 조건을 통과해야 결과에 포함되도록 구현했다. | ||
|
|
||
| - 저장 Row 수가 요청 건수와 정확히 일치 | ||
| - `min(vector_dims)=max(vector_dims)=1024` | ||
| - 실제 Batch 실행 수가 `ceil(rowCount / batchSize)`와 일치 | ||
| - JDBC 결과에 `EXECUTE_FAILED`가 없음 | ||
| - `vector_cosine_ops` HNSW Index가 존재 | ||
| - 저장 시간과 TPS가 0보다 큰 유한값 | ||
|
|
||
| ## 6. 재현 방법 | ||
|
|
||
| ```bash | ||
| docker compose up -d postgres | ||
| DB_SSLMODE=disable ./gradlew vectorStoragePerformanceTest | ||
| ``` | ||
|
|
||
| 기본 실행은 12개 Profile을 순차 측정하고 | ||
| `build/reports/vector-storage/vector-storage-latest.json`을 생성한다. | ||
|
|
||
| ## 7. 한계와 다음 검증 | ||
|
|
||
| - 로컬 Docker 단일 환경 결과이므로 공식 OpenSQL 수치와 동일하다고 볼 수 없다. | ||
| - 본 측정 2회는 회귀 기준선에는 쓸 수 있지만 안정적인 백분위 산출에는 부족하다. | ||
| - BGE-M3, Chunk 생성, HTTP, JPA와 Worker 동시 실행 비용은 제외했다. | ||
| - 제품 Batch Size 변경 전 공식 OpenSQL과 실제 Pipeline E2E에서 메모리·Connection Pool·Transaction | ||
| 시간을 함께 검증한다. |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
문서 파일명에 실제 이슈 번호를 포함하십시오.
제공된 문맥에는 연결된 이슈 번호가 없으므로 번호를 추측하지 마십시오. 실제 이슈 번호를 사용해 두 파일을
{github아이디}-#{이슈번호}-{설명}.md형식으로 변경하고 상호 참조를 갱신하십시오.docs/design/gimin-vector-storage-batch-benchmark.md#L1: 파일명을 규칙 형식으로 변경하고 Lines 115-116의 결과 문서 참조를 갱신하십시오.docs/test-results/gimin-vector-storage-batch-benchmark.md#L1: 파일명을 규칙 형식으로 변경하고 Line 6의 설계 문서 참조를 갱신하십시오.코딩 가이드라인의
docs/design/*.md및docs/test-results/*.md파일명 규칙을 적용했습니다.📍 Affects 2 files
docs/design/gimin-vector-storage-batch-benchmark.md#L1-L1(this comment)docs/test-results/gimin-vector-storage-batch-benchmark.md#L1-L1🤖 Prompt for AI Agents
Source: Coding guidelines