행궁동 데이터 엔지니어

반응형

Python cx_Oracle 패키지의 Arraysize에 관한 포스팅입니다.

 

Arraysize라는 파라미터는 Oracle DBMS 자체에 존재하는 파라미터로 DBMS가 클라이언트에게 데이터를 보내는 단위라는 정의는 동일하고 다른 DBMS도 이와 같은 파라미터를 가지고 있으니 참고 바랍니다.

 

Arraysize를 잘 튜닝하면 대용량 데이터를 빠르게 가져올 수 있습니다.

(대용량 데이터를 빠르게 Select 할 수 있습니다)

 

Arraysize?

DBMS는 클라이언트에게 SELECT 요청을 받았을 때 DBMS가 데이터를 모두 읽어 한 번에 전송하지 않고

먼저 읽는 데이터부터 일정량(Array Size)을 전송하고 멈추는 구조를 가지고 있습니다.

  • Arraysize :  DBMS에서 클라이언트로 데이터를 전송할 때 한 번에 몇 개씩 묶음으로 보낼지 결정하는 파라미터

예를 들어 DBMS가 아래와 같은 쿼리로 1000개의 Rows를 요청했을 경우 단순 계산하면 다음과 같은 결과가 나옵니다.

SELECT * FROM sample_data WHERE rownow <= 1000;

 

  • Arraysize = 1, 1000번 전송
  • Arraysize = 100, 10번 전송

여기서 1000번 전송이든 10번 전송이든 클라이언트가 받는 데이터는 1,000개로 동일합니다.

 

하지만 위의 2개 케이스에서 클라이언트가 데이터를 받는 속도는 다릅니다.(대부분의 경우 Arraysize = 1000이 빠름)

 

이유는 DBMS → 클라이언트 데이터 전송마다 발생하는 작업 때문입니다.

Arrarysize 단위로 데이터를 전송할 때마다 DBMS는 데이터를 메모리에 할당하고 전송 후 메모리를 비우는 것 과 같은 일련의 과정을 거칩니다(더 복잡한 과정일 것이나 단순히 적어봤습니다).

 

DBMS가 데이터를 메모리에 할당하고 비우는 과정 외에도 데이터를 전송하는 과정에서 네트워크에서도 오버헤드*가 발생할 것입니다.

  • *오버헤드 : 어떤 처리를 하기 위해 들어가는 간접적인 처리 시간
  • *Round-trip 이라고도 부르는 것 같습니다.
  • *A round-trip is defined as the trip from the Oracle Client libraries (used by cx_Oracle) to the database and back. Increasing arraysize can improve performance by reducing the number of round-trips to the database.

위와 같은 오버헤드 시간이 Arraysize = 1에서는 1000번, Arraysize = 1000에서는 1번 발생하니 대부분의 경우 Arraysize 1000인 경우에 클라이언트가 데이터를 빨리 받을 수밖에 없습니다.

 

하지만 Arraysize가 크면 DBMS가 리소스(메모리)를 많이 사용하는 단점이 있으니, 무조건 Arraysize를 크게 설정하는 것이 좋은 것은 아닙니다.

 

 

적정 Arraysize?

결론적으로 데이터 Rows, Row 당 크기, DBMS 메모리 사이즈 등을 고려하여 Arraysize을 적절하게 조정하는 것이 좋습니다.

 

100만 행 이상의 데이터의 경우에는 대게 arraysize를 5,000 이상으로 설정하는 것 같습니다.

 

아래에는 시뮬레이션 진행한 코드 남깁니다.

  • 10만, 20만 행의 데이터를 arraysize 100, 500, 1000, 10000, 50000, 100000으로 테스트 해봤습니다.
  • 결과 값은 DBMS 설정/사양, 네트워크 속도 등에 따라 다를 수 있습니다.

 

시뮬레이션

시뮬레이션 코드

# 출처 : https://github.com/oracle/python-cx_Oracle/issues/139
import cx_Oracle

conn = cx_Oracle.connect("접속정보")
cur = conn.cursor()
print("default arraysize: ", cur.arraysize)
query = "select * from sample_data"

fetch_sizes = [100000, 200000]
array_sizes = [100, 500, 1000, 5000, 10000, 50000, 100000]
for fetchsize in fetch_sizes:
    print("# fetchsize {fetchsize}".format(fetchsize=fetchsize))
    for size in array_sizes:
        cur = conn.cursor()
        cur.arraysize = size
        start_time = datetime.datetime.today()
        results = cur.execute(query).fetchmany(fetchsize)
        end_time = datetime.datetime.today()
        cur.close()
        print("fetchsize {fetchsize} arrarysize {size} took...".format(fetchsize=fetchsize, size=size), end_time - start_time)

 

시뮬레이션 결과

Rows 10만, 20만개에서는 arraysize = 1,000정도면 괜찮은 속도가 나왔습니다. (물론 row당 크기, DBMS의 메모리 등에 따라 다릅니다)

 

기타 궁금하신 사항은 댓글 남겨주세요.

 

감사합니다.

 

 

참고자료

1. https://github.com/oracle/python-cx_Oracle/issues/139
2. https://reoim.tistory.com/entry/Oracle-array-size
3. https://cx-oracle.readthedocs.io/en/latest/user_guide/tuning.html?highlight=arraysize#database-round-trips

 

 

반응형

이 글을 공유합시다

facebook twitter kakaoTalk kakaostory naver band