PostgreSQL

PostgreSQL: io_combine_limit

dewstream 2025. 7. 1. 08:00
728x90

※ PostgreSQL: io_combine_limit.
 
안녕하세요. 듀스트림입니다.
 
오늘 포스팅은 PostgreSQL 17버전에서 도입된 GUC 설정인 io_combine_limit에 관한 내용입니다.


1. io_combine_limit ?

  • PostgreSQL 17에서 추가된 GUC 설정으로 preadv()를 활용한 병합 I/O 시 최대 바이트 수를 조절합니다.
  • io_combine_limit는 러닝 타임에 변경할 수 있습니다.
  • 이를 통해 여러 데이터 페이지를 한 번에 읽어 시스템 호출 횟수와 디스크 오버헤드를 줄여 성능을 향상시킬 수 있습니다.
  • io_combin_limit의 기본값은 '128kB'입니다.
  • io_combin_limit 값은 'io_max_combine_limit' 값을 초과할 수 없습니다.
io_max_combine_limit ?
→ 18 버전에서 도입된 GUC입니다.
→ 변경 시 서버 재시작이 필요하며 사용자가 수정할 수 없습니다.
→ io_combine_limit의 설정 값은 이 파라미터 값을 초과할 수 없습니다.

2. 벡터 I/O와 preadv() 개념

  • preadv()는 POSIX API로 여러 버퍼를 한 번에 읽는 벡터화 I/O 방식입니다.
  • iovec[] 배열을 통해 scatter-gather 방식으로 데이터를 동시에 처리하며, PostgreSQL 17에서는 이를 적극 활용해 연속 블록들을 묶어 읽도록 설계되었습니다.

3. 소스 코드 구현

3.1 preadv() 호출 위치

  • read_stream.c의 핵심 함수인 StartReadBuffers() 내부에서 stream->pending_read_nblocks 값을 기준으로 실제 preadv() 호출이 이루어집니다. (이때 stream → io_combine_limit 값 이하로 묶인 블록만 읽도록 제어합니다.)
    • 관련 소스 코드: src/backend/storage/aio/read_stream.c
    • Doxygen 문서에서 관련 호출 흐름 확인 가능합니다.
  • 함수 흐름
read_stream_begin_impl()
  → read_stream_look_ahead()
  → read_stream_start_pending_read()
     → StartReadBuffers() { preadv(...) 호출 } → 내부적으로 preadv() 사용 (벡터화된 병합 I/O)
  → read_stream_next_buffer()
  → read_stream_end()
이 로직은 preadv() 기반 병합 I/O를 제어하며 io_combine_limit은 해당 스트림의 초기 생성 시점에 stream → io_combine_limit으로 저장되어 이후에는 고정적으로 사용됩니다.
(GUC 변경에도 영향을 받지 않도록 설계되어 있습니다.)

 

 

PostgreSQL Source Code: src/backend/storage/aio/read_stream.c File Reference

Go to the source code of this file. static void * get_per_buffer_data (ReadStream *stream, int16 buffer_index)   BlockNumber block_range_read_stream_cb (ReadStream *stream, void *callback_private_data, void *per_buffer_data)   static BlockNumber read_

doxygen.postgresql.org


4. 테스트

4.1 테스트 환경

  • 테이블: 약 10GB io_test (1,730만 행)
  • 실행: SELECT count(*) FROM io_test; (병렬 스캔 2 workers)
  • 환경: Rocky 8.10, PostgreSQL 17.4, SSD, Linux 파라미터는 기본값
  • 측정 항목:
    • EXPLAIN (ANALYZE, BUFFERS, TIMING)
    • strace -e preadv

4.2 테스트

  • 캐시 초기화 후 32kB~256kB 수행 반복

▸ 캐시 초기화

sudo sh -c "echo 3 > /proc/sys/vm/drop_caches"

 
▸ 파라미터 변경, 확인

-- 설정 변경
SET io_combine_limit = 'n';

-- 설정 확인
SHOW io_combine_limit;

 
▸ PID 확인

SELECT pg_backend_pid();

 
▸ 시스템 콜 추적 (preadv()만 필터링)

strace -e preadv -p <PID> -s 0 -f

 
▸ 실행 계획 SQL

EXPLAIN (ANALYZE, BUFFERS, TIMING) SELECT count(*) FROM io_test.io_test;

 
▸ 32kB

 
64kB

 
▸ 128kB(기본값)

 
▸ 256kB

 
▸ 요약 표

실행 시간 Seq Scan 시간 shared_read 블록 preadv 병합 패턴
32kB 5,150 ms 3,992 ms 632,989 ~4page
64kB 4,048 ms 2,891 ms 632,954 ~8page
128kB (기본값) 3,914 ms 2,850 ms 632,945 ~16page
256kB 3,460 ms 2,396 ms 674,764 ~32page
병합 패턴 페이지 계산식: 추적 값 Byte ÷ 8192(1page = 8kB)

 
+ io_combine_limit 값이 높다고해서 반드시 성능이 잘 나오는 것은 아닙니다. 스토리지 상태 및 I/O 스케줄링 등에 따라 오히려 병목이 발생할 수 있습니다.
 
아래는 io_combine_limit = 256kB이고, 스토리지 환경이 달라졌을 때의 결과입니다.


최적화가 잘 된다면 Disk I/O 성능을 증가시켜 SeqScan 시 성능 향상을 기대할 수 있습니다.
하지만 높게 설정하다고 반드시 좋은 건 아닙니다. 반드시 해당 워크로드에서 테스트 후 적용이 필요합니다.

• shared_buffers가 높고 SSD 캐시 효율이 좋다면 낮추는 게 성능상 유리할 수도 있습니다.
• 랜덤 I/O 비율이 높은 워크로드(OLTP) 환경에서도 낮추는 게 성능상 유리할 수도 있습니다.

 
+ 관련된 OS 파라미터

파라미터명 설명 확인 방법 설정 방법 기본값
fs.aio-max-nr 시스템 전체에서 허용되는 AIO(비동기 IO) 요청 수 한도 cat /proc/sys/fs/aio-max-nr sysctl -w fs.aio-max-nr=1048576 /etc/sysctl.conf에 추가 65536
max_iov_len (UIO_MAXIOV) preadv() 호출당 가능한 최대 iovec 배열 수 (= 병합 가능한 최대 페이지 수) 소스 코드 (/usr/include/linux/uio.h에서 UIO_MAXIOV) 커널 컴파일 시 설정됨
(수정 불가)
보통 1024
/sys/block/*/queue/read_ahead_kb 디스크 장치 read-ahead 크기
(미리 읽는 범위로 페이지 캐시 효율과 관련)
cat /sys/block/sda/queue/read_ahead_kb echo 8192 > /sys/block/sda/queue/read_ahead_kb 4096
/sys/block/*/queue/scheduler 디스크 I/O 스케줄러 설정 (I/O 병합 정책에 영향) cat /sys/block/sdX/queue/scheduler echo mq-deadline > /sys/block/sdX/queue/scheduler 버전에 따라 다름
vm.dirty_ratio 전체 메모리 중 dirty page가 차지할 수 있는 최대 비율 (백그라운드 write 타이밍에 영향) sysctl vm.dirty_ratio sysctl -w vm.dirty_ratio=10 /etc/sysctl.conf 30
vm.dirty_background_ratio 백그라운드로 디스크에 쓰기를 시작하는 dirty page 비율 sysctl vm.dirty_background_ratio sysctl -w vm.dirty_background_ratio=5 /etc/sysctl.conf 10

 
++ ulimit의 open files가 io_combine_limit 값과 간접적으로 왜 관련이 있는지에 대한 내용입니다.

항목 설명
preadv()는 파일 디스크립터(fd)를 통해 호출됨 PostgreSQL이 데이터 파일을 읽을 때 내부적으로 fd를 사용
병렬 워커가 많아지면 동시에 열린 fd도 증가 병렬 SeqScan, basebackup, wal sender 등에서 fd 다수 사용
IO 병합이 커질수록 한 번에 열리는 relation segment 수 증가 가능성이 있음 1 relation = 다수의 1GB segment → 병합 시 여러 segment 접근
shared_buffers 캐시 미스가 많을수록 open/close 횟수 많아짐 병합 효과 작을 때 IOPS 증가 = 더 많은 fd 활동

오늘은 여기까지~

 

728x90

'PostgreSQL' 카테고리의 다른 글

PostgreSQL: pg_store_plans  (4) 2025.07.10
PostgreSQL: Logical Replication  (0) 2025.07.02
PostgreSQL: Partition  (4) 2025.06.26
PostgreSQL: Lock  (0) 2025.06.24
PostgreSQL: credcheck  (0) 2025.06.17