ABAP SQL 튜닝 시리즈 여섯 번째 글입니다. ⑤편이 속도였다면, 이번은 메모리입니다.
쿼리 자체는 문제없어 보입니다. 조건도 있습니다. 그런데 500만 건이 전부 애플리케이션 서버 메모리로 올라옵니다.
한 행이 500바이트라면 2.5GB입니다. 그러면 이런 일이 벌어집니다.
TSV_TNEW_PAGE_ALLOC_FAILED — 내부 테이블에 더 이상 공간을 못 잡는 덤프이 문제는 개발계에서 잘 안 보입니다. 테스트 데이터가 1만 건이면 아무 일도 안 일어납니다. 운영 데이터가 수백만 건일 때 처음 터집니다.
메모리 상황을 실제로 확인하는 방법은 메모리 스냅샷과 Memory Inspector 글에서 다룹니다.
해법은 단순합니다. 한 번에 다 받지 말고 묶음 단위로 받아 처리하고 버립니다.
500만 건이면 500만 건분의 메모리를 씁니다. 건수가 늘면 언젠가 반드시 터집니다.
전체가 몇 건이든 한 묶음 크기만큼만 메모리에 있습니다. 건수가 늘어도 안전합니다.
정답은 없지만 기준은 있습니다.
보통 수천~수만 건 범위에서 시작해, 한 행의 크기와 실제 메모리 사용량을 보고 조정합니다. 한 행이 크면 묶음을 작게 잡아야 합니다.
PACKAGE SIZE 없이 SELECT ... ENDSELECT로 한 건씩 도는 방식도 있지만, 왕복이 지나치게 잦아 권하지 않습니다. 나눠 읽되 묶음 단위로 받으세요.
PACKAGE SIZE는 내부적으로 커서를 쓰지만, 그 커서를 우리가 직접 다룰 수는 없습니다. 더 세밀한 제어가 필요하면 커서를 직접 엽니다.
SELECT ... ENDSELECT 블록 구조가 로직에 맞지 않을 때그 외 단순한 대량 순차 처리라면 PACKAGE SIZE로 충분합니다.
이 편에서 가장 중요한 함정입니다.
COMMIT WORK는 데이터베이스 커밋을 일으키고, 데이터베이스 커밋은 열려 있는 커서를 닫습니다. 다음 FETCH가 정상 동작하지 않습니다.
OPEN CURSOR WITH HOLD를 쓰면 데이터베이스 커밋 후에도 커서가 유지됩니다. 또는 읽기와 쓰기를 분리합니다.
대량 처리에서는 중간중간 커밋을 해야 하는 경우가 많아서, 이 조합이 자연스럽게 만들어집니다.
COMMIT WORK 말고도 암묵적인 데이터베이스 커밋이 발생하는 지점이 있습니다. 다이얼로그 화면 전환, 일부 RFC 호출 등이 그렇습니다. 커서를 연 상태로 로직이 길어진다면 그 사이에 커밋이 끼어들 여지가 없는지 확인하세요.
또 하나, 한 작업 프로세스가 동시에 열 수 있는 커서 수에는 한계가 있습니다. 커서를 열었으면 반드시 CLOSE CURSOR로 닫으세요.
나눠 읽더라도, 결과를 다시 다른 내부 테이블에 쌓으면 아무 소용이 없습니다.
큰 내부 테이블을 다 썼다면 FREE로 즉시 반납하는 것도 습관으로 둘 만합니다.
CLEAR는 내용을 비우지만 확보한 메모리는 그대로 잡고 있습니다. 큰 테이블을 확실히 반납하려면 FREE를 쓰세요.
집계만 필요하다면 애초에 담을 이유가 없습니다. ③편에서 본 것처럼 DB에서 계산해 결과만 받으세요.
⑤편과 ⑥편이 비슷해 보이지만 목적이 다릅니다. 이걸 구분해야 상황에 맞는 선택을 할 수 있습니다.
고쳤으면 확인해야 합니다.
FETCH 횟수를 확인합니다.메모리와 시간은 상충 관계입니다. 묶음을 줄이면 메모리는 안전해지지만 왕복이 늘어 느려집니다. 둘을 함께 보면서 균형점을 찾으세요.
INTO TABLE로 받지 마세요. 언젠가 반드시 터집니다.PACKAGE SIZE 로 나눠 읽으면 메모리 사용량이 일정해집니다.OPEN CURSOR, 다 쓰면 반드시 CLOSE CURSOR.COMMIT WORK는 커서를 닫습니다. 중간 커밋이 필요하면 WITH HOLD를 쓰거나 구조를 바꾸세요.FREE 로 반납하세요.다음 ⑦편은 시리즈 마지막으로, S/4HANA의 Code-to-Data 방향을 다룹니다.
Disclaimer — 이 포스트는 AI(Claude)를 활용하여 작성된 초안을 바탕으로 검수 및 보완하여 작성되었습니다. 내용 중 오류나 오타가 있다면 댓글로 알려주시면 감사하겠습니다.