PostgreSQL(포스트그레스큐엘)의 LISTEN/NOTIFY(리스닝/노티파이) 기능은 데이터베이스 변경 사항을 실시간으로 애플리케이션에 알리는 데 사용됩니다. 이는 온라인 채팅이나 실시간 대시보드처럼 낮은 지연 시간이 요구되는 애플리케이션에 유용하지만, 기존에는 초당 약 2,900건의 스트림 쓰기에서 성능 병목이 발생해 대규모 시스템에는 적합하지 않다는 인식이 있었습니다. 그러나 최근 연구에 따르면, 알림 처리 방식을 최적화함으로써 단일 서버에서 초당 최대 6만 건의 쓰기를 처리할 수 있음이 밝혀져 이 기능의 확장성에 대한 인식을 바꾸고 있습니다.
이러한 성능 향상은 NOTIFY 호출 방식의 변화에서 비롯됩니다. 기존 방식은 데이터베이스에 새 데이터가 기록될 때마다 트리거를 통해 NOTIFY를 한 번씩 전송했습니다. 이 과정에서 알림의 커밋 순서를 보장하기 위해 트랜잭션 커밋 시 전역 배타적 잠금(global exclusive lock)이 발생했고, 이는 커밋을 직렬화하여 처리량을 제한하는 주원인이었습니다. 즉, 여러 트랜잭션이 동시에 커밋될 수 없고 차례를 기다려야 했으며, Postgres의 그룹 커밋(group commit) 최적화도 활용할 수 없었습니다. 하지만 최적화된 방식은 알림 자체를 진실의 원천(source of truth)으로 삼는 대신, 실제 데이터가 저장된 테이블을 확인하라는 '신호'로 간주합니다. 따라서 알림을 메모리에 버퍼링한 뒤 주기적으로 하나의 배치 트랜잭션(batch transaction)으로 전송함으로써 전역 잠금 획득 횟수를 획기적으로 줄였습니다. 이로 인해 개별 쓰기 트랜잭션은 빠르게 진행되고, 그룹 커밋 같은 Postgres 최적화를 활용하여 처리량을 크게 높일 수 있었습니다.
물론 알림을 버퍼링하는 과정에서 프로세스 장애 시 알림이 유실될 가능성이 있습니다. 이를 보완하기 위해 읽기 프로세스가 알림을 기다리는 동시에 데이터베이스를 주기적으로 조회하여 유실된 알림 없이 기록된 스트림 데이터가 있는지 확인하는 저빈도 폴링(low-frequency polling)을 함께 사용합니다. 이 보조적인 폴링은 낮은 빈도로 실행되므로 전체 성능에 미치는 영향은 미미합니다. 이러한 최적화된 구현은 동시 읽기 프로세스가 있는 환경에서 초기 구현 대비 20배 높은 초당 6만 건의 스트림 쓰기를 처리하면서도, 지연 시간은 15~100ms 범위 내로 유지하는 놀라운 결과를 보여주었습니다. 이는 Postgres CPU가 완전히 사용되어 데이터베이스 자체의 실제 포화 상태에 도달했음을 의미하며, 잠금 경합이 아닌 하드웨어 한계에 도달했음을 시사합니다.
이번 연구 결과는 Postgres LISTEN/NOTIFY가 특정 사용 사례에서 충분히 확장 가능하며, 별도의 메시지 큐 시스템 없이도 상당한 처리량을 달성할 수 있음을 입증했습니다. 이는 특히 스타트업이나 소규모 팀이 인프라 복잡성을 줄이면서도 실시간 기능을 구현하고자 할 때 매력적인 대안이 될 수 있습니다. 다만, 벤치마크에 96코어, 384GB RAM의 고사양 서버가 사용되었음을 고려할 때, 실제 프로덕션 환경에서는 예상 트래픽과 비용 효율성을 면밀히 분석하여 적절한 아키텍처를 선택하는 것이 중요합니다. 무조건적인 고확장성보다 실제 예상 규모에 적당한 여유를 더해 설계하고, 추가 확장성이 사실상 '공짜'일 때만 그 이상을 선택하는 현명한 접근이 필요합니다.