
안녕하세요! 에이플랫폼 Support Bulletin의 열 번째 이야기입니다. 😊
이번 글에서는 ODBC의 Prefetch 설정이 의도하지 않은 쿼리 변형과 데이터 왜곡을 어떻게 일으킬 수 있는지를 다룹니다.
분명 쿼리에는 LIMIT이 없었는데…
서버 로그엔 LIMIT이 붙은 쿼리가 보였습니다.
게다가 100만 건의 중복 없는 데이터를 분석했는데도, COUNT(DISTINCT( )) 결과가 엉뚱하게 나왔습니다.
어떻게 이런 일이 벌어졌을까요?
이번 경험을 통해, Prefetch 옵션이 데이터 흐름에 어떤 영향을 줄 수 있는지 직접 살펴보겠습니다.
MySQL ODBC 사용
이번 환경에서는 SingleStore는 MySQL 호환성 덕분에 MySQL ODBC 드라이버를 사용할 수 있었고,
무엇보다 BI 툴에서 MySQL ODBC 드라이버가 별도 설정 없이 기본 지원되기 때문에 커스텀 드라이버 설정 없이 곧바로 연결해 사용할 수 있는 점이 매우 유리했습니다.
따라서 내장된 MySQL ODBC 드라이버를 그대로 사용했습니다.
이때 사용한 쿼리의 예시를 단순화 한다면 아래와 같습니다.
SELECT id FROM t1
데이터의 id 컬럼은 중복이 없는 PK 성격의 컬럼이며, 전체 로드 대상은 정확히 100만건이었습니다.
하지만, 데이터를 불러오고 나서 COUNT(DISTINCT( ))를 해보니 예상과 다른 결과가 나왔습니다.
Prefetch 옵션의 기능 설명과 설정 방법
ODBC 드라이버는 SQL을 실행한 뒤, 결과를 한 번에 전부 가져오는 게 아니라
여러 번에 나눠서(fetch) 데이터를 받아오는 구조를 기본적으로 갖고 있습니다.
이때 한 번에 받아올 행 수나 데이터 크기를 조절하는 설정이 바로 Prefetch이며,
드라이버에 따라 Fetch Buffer Size, Row Array Size, Prefetch Count 등으로 다르게 표현됩니다.
→ 이 설정은 네트워크 왕복을 줄이고 fetch 성능을 최적화하기 위한 목적을 갖고 있죠.
예를 들어 데이터가 수천·수만 건인 경우, 한꺼번에 다 받아오면
메모리 병목이나 지연 현상이 생길 수 있기 때문에, 일정 단위로 나눠 받아오는 방식이 효과적입니다.
하지만 이 방식은 결과 순서가 고정되지 않는 DB나 분산 환경에서는 문제가 될 수 있습니다.
→ 내부적으로 중간 fetch 요청 시점마다 순서가 섞이게 되면, 결과에 중복이나 누락이 발생할 가능성이 생깁니다.
실제 설정 위치 예시 (Windows 기준)
ODBC 관리자의 DSN 설정 창에서
- 예시 드라이버 이름: MySQL ODBC 5.3 ANSI Driver
- 설정 경로: Details >> → Cursors/Results
- 주요 항목:
- Use Fetch Buffer Size: (예: 1000)
- Enable prefetch of data for queries: 체크박스로 설정 가능

일부 BI 툴이나 애플리케이션은 내부적으로 해당 옵션을 강제 적용하거나 사용자에게 노출하지 않는 경우도 있으므로,
연결되는 툴에서 DSN 설정을 수동으로 조정할 수 있는지 확인하는 것이 중요합니다.
이처럼 Prefetch는 성능을 높이는 유용한 기능이지만,
상황에 따라 예상치 못한 정합성 오류를 유발할 수 있습니다.
이제 다음 장에서 Prefetch 설정에 따라 실제 어떤 결과 차이가 나타났는지 살펴보겠습니다.
Prefetch 옵션 ON/OFF 비교 실험
테스트 환경
데이터 셋: 10,000 건 중복없는 INT 값
사용 쿼리: SELECT id FROM t1
ODBC Prefetch 설정:
- ✅ 켬 (Prefetch from server by 100 rows)
- ❌ 끔 (Prefetch 체크 해제)
테스트 방법
예시로 5.3 버전의 Mysql ODBC를 옵션만 다르게 하여 두개의 DSN을 생성 했습니다.


아래와 같이 DSN 이름에 따라 파이썬 코드로 쿼리를 한번 실행 시킨 후
import pyodbc
# DSN을 사용하는 경우
conn = pyodbc.connect('DSN={5.3-100/5.3};UID=root;PWD=wodn0714')
cursor = conn.cursor()
# 쿼리 실행 (테이블 이름과 쿼리는 환경에 맞게 조정하세요)
cursor.execute("SELECT /*{5.3-100/5.3}*/ FROM t1")
# 결과 출력
for row in cursor.fetchall():
print(row)
# 연결 종료
cursor.close()
conn.close()
SingleStore의 MV_QUERIES 뷰를 통해 실제 어떤 쿼리가 실행 되었는지 확인 해 보겠습니다.
select * from information_schema.MV_QUERIES where QUERY_TEXT = '%5.3%';
테스트 결과
Prefetch 옵션을 주지 않은 DSN을 사용하면 사용한 쿼리 그대로 전달이 되는 반면 Prefetch 옵션을 사용하면 Limit이 사용됩니다.
select * from information_schema.MV_QUERIES where QUERY_TEXT = '%5.3%';
>>>
singlestore> select * from information_schema.MV_QUERIES where QUERY_TEXT like '%5.3%';
+----------------------------+-----------------------------------------------------------------+---------------+-----------+
| ACTIVITY_NAME | QUERY_TEXT | PLAN_WARNINGS | PLAN_INFO |
+----------------------------+-----------------------------------------------------------------+---------------+-----------+
| Select_t1_187adf9007d19928 | SELECT /*5.3*/ * FROM t1 | | NULL |
| Select_t1_dcc5deb2fedb5a22 | SELECT /*5.3-100*/ * FROM t1 LIMIT @, @ | | NULL |
| Select_t1_662c906deb94ed1f | SELECT /*5.3-100*/ * FROM t1 LIMIT @, @ | | NULL |
| Select_t1_31fc0112c5502c53 | SELECT /*5.3-100*/ * FROM t1 LIMIT @, @ | | NULL |
| Select_t1_6f6fa395b610c2a7 | SELECT /*5.3-100*/ * FROM t1 LIMIT @, @ | | NULL |
| Select_t1_11b6e729bc73d295 | SELECT /*5.3-100*/ * FROM t1 LIMIT @, @ | | NULL |
+----------------------------+-----------------------------------------------------------------+---------------+-----------+
6 rows in set (0.01 sec)
분산형 DB에서 LIMIT의 위험성
그럼 왜 LIMIT이 사용되면 문제가 될까요?
SingleStore와 같은 분산형 DB에서는 데이터를 여러 노드에서 병렬로 처리하기 때문에,
ORDER BY 절이 없는 쿼리에서는 매 실행마다 다른 순서로 결과가 반환될 수 있습니다.
이때 ODBC 드라이버가 일정 단위로 데이터를 나눠서 fetch하면,
각 요청 사이의 순서가 뒤섞이며 중복이나 누락된 데이터가 포함될 가능성이 생깁니다.
실제로 SELECT /*5.3-100*/ * FROM t1의 결과를 보면 중복된 id 값이 다수 확인됩니다.
이러한 문제는 특정 드라이버의 결함이 아니라,
순서를 보장하지 않는 분산형 DB에서 ORDER BY 없이 Prefetch 기능을 사용했을 때 발생하는 구조적 충돌이라 할 수 있습니다.

ODBC의 Prefetch 옵션은 성능을 높이는 강력한 도구지만,
결과 순서가 보장되지 않는 DB에서는 데이터 정합성을 해칠 위험이 있습니다.
특히 분산형 DB처럼 결과 순서가 매번 바뀔 수 있는 환경에서는,
Prefetch 기능을 사용할 경우 반드시 ORDER BY 절을 함께 지정하거나
필요에 따라 Prefetch 기능 자체를 끄고 사용하는 것이 안정적입니다.
성능 최적화와 결과의 신뢰성 사이에서 균형을 맞추는 것이,
실무 환경에서 가장 중요한 전략입니다.
'SingleStoreDB > Support Bulletin' 카테고리의 다른 글
[Support Bulletin 09] - Trailing Space 에 따른 SQL 결과 차이 (0) | 2025.05.30 |
---|---|
[Support Bulletin 08] - SingleStore 모니터링 시스템에서 Pipeline 오류? 포트 번호가 문제다! (0) | 2025.04.17 |
[Support Bulletin 07] - DBeaver Multiple statements 활성화 방법 (0) | 2025.03.17 |
[Support Bulletin 06] - Division by Zero (0) | 2025.03.07 |
[Support Bulletin 05] - Red Hat 9.2 Memory 할당 에러 (0) | 2025.02.19 |