Sangwon
09/07/2026, 6:19 AMRocky
09/07/2026, 6:19 AMDISTRIBUTED BY HASH 컬럼(버킷 키)을 선정할 때는 다음의 원칙을 따르는 것이 가장 이상적입니다.
1. [필수] PK의 부분 집합이어야 함
PK 테이블에서는 *버킷 키로 설정하려는 컬럼이 반드시 Primary Key에 포함*되어야 합니다.
* 예: PK가 `(order_id, dt)`라면, 버킷 키는 order_id 또는 dt, 혹은 (order_id, dt) 전체가 될 수 있습니다. (PK에 없는 컬럼은 버킷 키로 쓸 수 없음)
2. 높은 카디널리티 (High Cardinality)
데이터가 여러 버킷에 골고루 분산되도록 *중복도가 낮고 유니크한 값이 많은 컬럼*을 선택해야 합니다.
* 이상적: ID, Serial Number, 주문번호 등
* 피해야 할 것: 성별, 상태값(Y/N), 날짜(파티션 키와 겹칠 경우 데이터 쏠림 위험) 등 중복이 심한 컬럼
3. 자주 사용되는 쿼리 조건 (Bucket Pruning)
WHERE 절에서 필터 조건으로 자주 쓰이는 컬럼을 버킷 키로 잡으면, 쿼리 시 해당 버킷만 골라 읽는 *Bucket Pruning*이 작동하여 성능이 비약적으로 향상됩니다.
* 예: WHERE user_id = 123 형태의 쿼리가 많다면 `user_id`를 버킷 키로 설정
4. 조인(Join) 성능 고려 (Colocate Join)
두 테이블을 조인할 때, *조인 키를 두 테이블 모두의 버킷 키*로 설정하고 버킷 수를 맞추면 *Colocate Join*이 발생합니다. 이는 네트워크를 통한 데이터 이동(Shuffle) 없이 로컬에서 바로 조인을 수행하게 해줍니다.
──────────
요약 가이드
* 최우선: PK 중 하나이면서 값이 매우 다양한 컬럼 (예: user_id, order_id)
* 개수: 보통 1~3개 이내의 컬럼을 권장합니다.
* 데이터 쏠림 주의: 특정 값에 데이터가 몰려있다면(Skew), 해당 컬럼만 단독으로 버킷 키로 쓰지 말고 다른 PK 컬럼과 조합하세요.
예시 코드:
sql
CREATE TABLE orders (
order_id BIGINT NOT NULL,
dt DATE NOT NULL,
user_id INT NOT NULL,
amount DECIMAL(10, 2)
) PRIMARY KEY (order_id) -- order_id가 PK
DISTRIBUTED BY HASH (order_id) -- PK인 order_id를 버킷 키로 사용 (이상적)
PROPERTIES ("enable_persistent_index" = "true");
```
## References
- [table_design/table_types/primary_key_table.md](https://docs.starrocks.io/docs/table_design/table_types/primary_key_table)
- [best_practices/primarykey_table.md](https://docs.starrocks.io/docs/best_practices/primarykey_table)
- [best_practices/bucketing.md](https://docs.starrocks.io/docs/best_practices/bucketing)
- [table_design/data_distribution/Data_distribution.md](https://docs.starrocks.io/docs/table_design/data_distribution/Data_distribution)Sangwon
09/07/2026, 8:39 AMRocky
09/07/2026, 8:39 AMstarrocks 라이브러리와 df.to_sql 사용 (가장 간단한 방법)
SQLAlchemy 기반의 공식 Python 클라이언트를 사용하여 to_sql 메서드로 데이터를 밀어 넣을 수 있습니다. 대상 테이블이 *Primary Key 테이블*로 생성되어 있다면, if_exists='append' 설정만으로 중복 키에 대해 Upsert가 발생합니다.
python
import pandas as pd
from sqlalchemy import create_engine
# 1. SQLAlchemy 엔진 생성 (pip install starrocks)
# 형식: starrocks://<user>:<password>@<host>:<port>/<database>
engine = create_engine("<starrocks://root:@127.0.0.1:9030/test_db>")
# 2. 샘플 데이터프레임 생성
df = pd.DataFrame({
'id': [1, 2, 3],
'name': ['Alice', 'Bob', 'Charlie'],
'score': [100, 90, 85]
})
# 3. 데이터 삽입 (PK 테이블인 경우 자동으로 Upsert 수행)
df.to_sql('my_pk_table', engine, if_exists='append', index=False)
* 장점: 코드가 매우 간단함.
* 단점: 대량의 데이터를 처리할 때 속도가 느릴 수 있음 (JDBC/Insert 방식의 한계).
──────────
2. Stream Load 사용 (고성능 권장 방식)
데이터가 수만 건 이상인 경우, StarRocks의 핵심 기능인 *Stream Load*를 사용하는 것이 훨씬 빠릅니다. Pandas의 `to_csv`로 데이터를 변환한 뒤 HTTP 통신으로 바로 쏴주는 방식입니다.
python
import requests
import io
def stream_load_upsert(df, host, port, db, table, user, password):
# 1. 데이터프레임을 CSV 버퍼로 변환
csv_buffer = io.StringIO()
df.to_csv(csv_buffer, index=False, header=False)
# 2. Stream Load API 호출
url = f"http://{host}:{port}/api/{db}/{table}/_stream_load"
headers = {
"Expect": "100-continue",
"column_separator": ",",
"format": "csv"
}
auth = (user, password)
response = requests.put(url, data=csv_buffer.getvalue().encode('utf-8'), headers=headers, auth=auth)
return response.json()
# 사용 예시
df = pd.DataFrame({'id': [1], 'name': ['Rocky'], 'score': [99]})
res = stream_load_upsert(df, 'FE_HOST', 8030, 'test_db', 'my_pk_table', 'root', '')
print(res)
* 장점: 매우 빠름 (초당 수십만 건 이상 가능).
* 중요: Expect: 100-continue 헤더를 포함해야 하며, 대상 테이블은 반드시 *Primary Key 테이블*이어야 합니다.
──────────
필수 확인 사항
성공적인 Upsert를 위해서는 대상 테이블이 반드시 다음과 같이 Primary Key 엔진으로 생성되어 있어야 합니다.
sql
CREATE TABLE my_pk_table (
id INT NOT NULL,
name VARCHAR(65533),
score INT
) PRIMARY KEY (id) -- 이 설정이 있어야 Upsert가 작동함
DISTRIBUTED BY HASH(id);
참고:
* 부분 업데이트 (Partial Update): 만약 데이터프레임에 테이블의 일부 컬럼만 있고 해당 컬럼만 업데이트하고 싶다면, Stream Load 시 헤더에 `partial_update: true`를 추가하세요. (StarRocks v2.5 이상)
* 조건부 업데이트: __op 컬럼을 명시하여 특정 조건에서만 Upsert 하거나 Delete를 동시에 수행할 수도 있습니다.
References
• loading/Load_to_Primary_Key_tables.md
• loading/InsertInto.md
• unloading/arrow_flight.md
• table_design/table_types/primary_key_table.md
References
• faq/Others.md
• loading/Load_to_Primary_Key_tables.md
• loading/InsertInto.md
• unloading/arrow_flight.md