PROD주권 유럽 BaaS 플랫폼대시보드 열기 →

성능 · 10분 읽음

가비지 수집기가 p99 대기 시간을 변경하지 않는 이유

Affane Daylami · Fondateur · 2026년 7월 2일

블로그로 돌아가기

p99는 100개 요청 중 가장 느린 요청, 즉 평균은 완벽하게 유지되는 동안 SLA를 위반하는 요청을 측정합니다. 트래픽이 많은 백엔드에서 이 소수의 느린 요청은 단일 원인인 경우가 많습니다. 즉, 전체 프로그램을 중단하여 메모리를 확보하는 가비지 수집기입니다. Rust에는 가비지 수집기가 없습니다. 마케팅 수치가 아닌 날짜가 지정된 타사 소스가 포함된 메커니즘은 다음과 같습니다.

이 영어 텍스트는 프랑스어 원본에서 자동으로 생성되었으며 아직 검토되지 않았습니다.
이 페이지는 자동으로 번역되었습니다. 영어 버전은 권위가 있습니다.

이 기사는 제3자의 날짜가 기록된 소스에만 의존하며, 결코 고안된 Aurabase 암호에 의존하지 않습니다. 백엔드와 관련된 p99 대기 시간은 포함되지 않습니다. 우리는 가비지 수집기 없이 Rust로 애플리케이션 코어를 작성합니다. 이는 코드, 작업 공간 Cargo 및 axum 서비스에서 직접 확인할 수 있는 사실입니다. 그러나 우리는 이를 수치로 보여주기 위해 재현 가능한 p99 벤치마크 방법론을 아직 발표하지 않았습니다. 이 텍스트는 측정된 결과가 아닌 메커니즘을 설명합니다.

필수사항

  • p99는 가비지 수집기(GC) 일시 중지가 평균이 아닌 가장 큰 타격을 주는 지점인 100개의 쿼리 중에서 가장 느린 쿼리를 측정합니다(Dean & Barroso, “The Tail at Scale,” Google, 2013).
  • GC는 사용되지 않은 메모리를 해제하기 위해 전체 프로그램("stop-the-world")을 중단합니다. Rust에는 GC가 없습니다. 값이 범위를 벗어나는 정확한 순간에 메모리가 해제되어 컴파일러에 의해 확인됩니다.
  • Discord는 2020년에 Go가 최소 2분마다 가비지 수집 주기를 트리거하고 각 주기마다 대기 시간이 급증하는 캐시 서비스를 문서화했습니다(Discord 엔지니어링 블로그).
  • GC 일시 중지를 줄이려면 Google에서도 수년간의 엔지니어링이 필요합니다. GB 수집기는 2015년과 2018년 사이에 0에 도달하지 않고 300~400ms에서 500μs로 증가했습니다(go.dev).
  • Aurabase의 백엔드 코어는 가비지 수집기 없이 Rust로 작성되었으며 코드에서 확인되었습니다. 현재까지 p99 Aurabase 대기 시간 수치는 게시되지 않았습니다. 이는 측정이 아닌 메커니즘으로 남아 있습니다.
#
꼬리 지연 시간

p99가 평균이 아닌 이유

평균은 핵심을 숨깁니다. 100개 요청 중 99개 요청이 5ms 내에 응답하고 단 하나의 요청만 500ms가 걸리는 경우 평균은 낮게 유지됩니다. 그러나 사용자 100명 중 1명은 대기 시간이 100배 더 길어지는 것을 경험합니다. p99는 이 요청을 정확하게 측정합니다. 즉, 평균 대기 시간 대시보드가 ​​녹색으로 유지되는 동안 SLA를 위반하는 가장 느린 100분의 1 요청입니다.

Google에서 Jeffrey Dean과 Luiz André Barroso는 "The Tail at Scale"(Communications of the ACM, vol. 56, 2013)에서 이 문제를 공식화했습니다. 이들의 관찰은 다음과 같이 자주 인용됩니다. "중간 규모의 시스템에서는 중요하지 않은 일시적인 높은 대기 시간 에피소드가 대규모로 전체 서비스 성능을 지배하게 될 수 있습니다." 간단히 말해서, 소규모에서는 무시할 수 있는 간헐적인 대기 시간 에피소드가 결국 분산 시스템의 인지된 성능을 지배하게 됩니다.

초당 수천 개의 요청을 처리하는 백엔드는 어느 시점에서나 GC 일시 중지 중에 발생한 요청을 보내게 되어 있습니다. 대규모로 볼 때 이는 드문 경우가 아닙니다. 이는 통계적으로 확실합니다.

#
GC 역학

가비지 수집기의 기능과 모든 것을 일시 중지하는 이유

가비지 수집기(GC)는 프로그램의 살아있는 객체(아직 어딘가에서 참조되고 있는 객체)를 지속적으로 추적하고 액세스할 수 없게 된 객체의 메모리를 해제합니다. 이 추적을 tracing이라고 합니다. GC는 참조 그래프를 살펴보고 아직 사용되는 항목을 표시한 다음 나머지를 스윕합니다.

문제: 프로그램이 계속해서 새 참조를 생성하는 동안 이 그래프를 탐색하면 일관되지 않은 결과가 생성됩니다. 많은 최신 GC에서 최후의 수단으로 여전히 사용되는 역사적 답변은 stop-the-world입니다. 표시 및 스캔 중에 전체 프로그램이 일시 중지됩니다. 힙이 클수록 일시 중지 시간이 길어지는 경향이 있습니다. 일시 중지 기간은 현재 작업 부하가 아닌 라이브 데이터의 크기에 따라 달라집니다.

대부분의 최신 GC는 세대 전략을 사용합니다. 즉, 대부분의 개체가 어릴 때 죽는다고 가정합니다. 따라서 최근 할당은 작은 메모리 영역에서 자주, 그러나 빠르게 검색됩니다. 여러 주기를 거쳐 살아남은 개체는 더 큰 영역으로 이동하고 드물게 스캔됩니다. 하지만 해당 영역을 청소해야 하는 경우 관련 일시 중지는 크기에 따라 커집니다. 트래픽이 많고 할당량이 많은 서비스의 p99를 지배하는 것은 작은 "사소한" 중단이 아닌 이 "주요" 중단입니다.

정보

최신 동시 및 세대별 GC는 프로그램과 병렬로 작업하여 이러한 중단의 빈도와 기간을 줄입니다. 그러나 거의 모든 기업이 경계선에 있는 경우에 대비해 세계 정지 폴백 메커니즘을 유지하고 있으며 이를 줄이는 데에는 수년간의 엔지니어링이 필요합니다. 섹션 04에서는 정량화되고 출처가 제시된 예를 제공합니다.

#
실제 사례

Discord, 2020: GC 중단이 생산 사고로 변함

2020년 2월, 엔지니어 Jesse Howarth는 업계에서 참고가 된 게시물을 게시했습니다: "Discord가 Go에서 Rust로 전환하는 이유"(Discord 엔지니어링 블로그). 관련 서비스인 읽기 상태(Read States)는 초당 수십만 건의 업데이트를 통해 수백만 명의 사용자(캐시당 수천만 개의 항목)에 대한 메시지 읽기 상태를 관리합니다.

진단은 직접적입니다. "Go는 최소 2분마다 강제로 가비지 수집을 실행합니다."라는 기사에 인용되어 있습니다. 즉, Go는 이 서비스에서 최소 2분마다 가비지 수집 주기를 트리거하며 각 주기는 팀 그래프에 표시되는 지연 시간의 급증을 생성합니다.

팀에서는 먼저 캐시 크기를 줄여 스파이크를 완화했습니다. 타협은 여전히 ​​불리했습니다. GC 일시 중지 횟수는 줄어들었지만 데이터베이스에 대한 캐시 누락 요청이 많아져 다른 곳에서는 전반적인 p99 성능이 저하되었습니다. 기본적인 수정은 모니터링할 가비지 수집기 없이 Rust에서 서비스를 다시 작성하는 것이었습니다.

이 게시물은 게시 당일(2020년 2월 4일)에 Hacker News에 대한 1,580개 이상의 포인트와 642개의 댓글로 활발한 기술 토론을 촉발시켰습니다. 이는 문제가 Discord 사례를 훨씬 뛰어넘는다는 신호입니다.

#
역사로 이동

일시 중지 시간을 400ms에서 500μs로 낮추기 위해 Google에서 3년 동안 엔지니어링한 결과

Go 가비지 수집기는 Google 전담 팀의 리소스를 사용하더라도 GC 일시 중지를 해결하는 데 필요한 노력의 규모를 보여줍니다. Go GC 기술 책임자인 Rick Hudson은 이 이야기를 두 개의 공식 Go 블로그 게시물에 기록했습니다.

2015년 8월 이전300-400ms재설계 전의 역사적인 Go 수집기
2015년 8월 · Go 1.530-40ms첫 번째 경쟁 수집기, 대상 < 10ms 설정
2016 · 고 1.6< 10ms(SLO 유지)생산에서 달성된 초기 목표
2017년 3월 · Go 1.8밀리초 미만세계정지 스택 스캔 제거
2017년 8월 · Go 1.9100~200μs(마크)팀이 언급한 새로운 비공식 벤치마크
2018 · SLO 발표사이클당 500μsRick Hudson이 공식화한 서비스 목표

출처: "Getting to Go: Go의 가비지 수집기 여정", go.dev, 2018년 7월 12일; 및 "Go GC: 낮은 대기 시간 및 단순성 우선 순위", go.dev, 2015년 8월 31일.

3년간의 헌신적인 작업으로 일반적인 휴식 시간이 1000분의 1로 줄었습니다. 그러나 일시 중지는 절대 사라지지 않습니다. 이는 서비스 목표(SLO)이지 0이 절대 보장되는 것은 아닙니다. GC 추적은 구조상 때때로 살아있는 개체의 그래프를 통과해야 합니다. 조정 가능한 유일한 변수는 여행의 존재 여부가 아니라 여행의 빈도와 기간입니다.

이러한 우선순위 선택은 중립적이지 않습니다. Go는 주로 네트워크 서비스와 웹 백엔드를 대상으로 합니다. 수백 밀리초의 일시 중지로 인해 사용자 경험이 직접 중단되므로 원시 GC 처리량이 아닌 대기 시간에 막대한 노력이 투자됩니다. 다른 관리되는 런타임은 자체적인 낮은 일시 중지 수집기로 기반을 마련하기 전에 과거 사용 사례에 따라 형성된 다양한 절충안을 상속했습니다. 공통점은 동일하게 유지됩니다. 모두 GC 추적에서 시작하므로 일시 중지 메커니즘에서 최소화되어 구성으로 제거되지 않습니다.

#
녹 역학

Rust가 구조적으로 이런 문제를 겪지 않는 이유

Rust는 GC 일시 중지를 줄이지 않습니다. 이를 유발하는 메커니즘을 제거합니다. 컴파일러는 컴파일 시 각 메모리 값을 소유한 사람을 추적합니다. 이는ownership입니다. 값의 소유자가 범위를 벗어나면 Rust는 해당 메모리를 해제하는 호출을 바이너리 코드의 동일한 위치에 자동으로 삽입합니다. 이 메커니즘을 RAII(리소스 획득은 초기화)라고 합니다. 릴리스는 결정적이며 백그라운드에서 실행되는 가비지 수집기에 의해 예약되지 않습니다.

Scala/Rust 생태계에서 인정받는 기술 블로그의 저자인 Alexandru Nedelcu는 최근 기사에서 트레이드오프를 요약합니다: "Rust가 만드는 트레이드오프는 예측 가능한 대기 시간과 안전성을 갖춘 성능을 선호하는 사용 편의성 중 하나입니다"(alexn.org, 2026년 7월 21일). Rust는 예측 가능한 대기 시간을 위해 작성의 단순성을 일부 교환합니다.

같은 기사에서는 현대 GC가 항상 충분하지 않은 이유를 요약합니다: "현대 GC는 프로그램에 영향을 주지 않고 점진적으로 동시에 작업을 수행하려고 합니다. 그러나 그 능력은 제한되어 전체 프로그램을 정지시켜 대기 시간에 영향을 미치는 세계 정지 GC 주기로 되돌아갑니다."

다음은 Aurabase 코드에서 추출한 것이 아닌 일반적인 예인 약 10줄의 메커니즘입니다.

example.rsrust
struct Connection {
    id: u32,
}

impl Drop for Connection {
    fn drop(&mut self) {
        println!("connexion {} fermée", self.id);
    }
}

fn handle_request() {
    let conn = Connection { id: 42 };
    // ...요청을 처리합니다...
} // 여기서 conn은 범위를 벗어납니다. drop()이 실행됩니다.
   // 정확한 순간에 컴파일러가 확인합니다.
   // 가비지 수집 주기를 통하지 않습니다.

중요한 뉘앙스: 모든 것이 무료는 아닙니다. 참조 계산 유형(Rc, Arc)은 각 복제 및 릴리스에 약간의 비용을 추가합니다. 이 비용은 지역적이고 결정적입니다. 프로그램이 메모리 힙을 통과하는 동안 전체 프로그램이 정지되는 일시 중지는 없습니다.

비동기식 백엔드에 대한 유용한 설명: Rust 비동기 런타임(tokio, 모든 Aurabase 서비스에서 사용됨)은 가비지 수집기와 아무 관련이 없습니다. 스레드 풀에서 협력 작업을 예약하지만 메모리를 확보하기 위해 라이브 개체 그래프를 반복하지 않습니다. 비동기 런타임과 GC가 동일한 가상 머신에 의해 관리되는 생태계에서는 혼란이 흔히 발생합니다.

#
건축학적 의미

트래픽이 많은 백엔드에서 이것이 변경되는 사항

수천 개의 동시 요청을 처리하는 백엔드에서 GC가 없으면 방정식 p99에서 변수가 제거됩니다. 더 이상 메모리 힙 크기를 조정하거나, 수집기 세대를 조정하거나, 최악의 시기에 떨어질 수 있는 주기를 모니터링할 필요가 없습니다. 개별 요청의 대기 시간은 프로그램의 다른 곳에서 예측할 수 없는 전역 이벤트가 아닌 자체 작업에 따라 달라집니다.

The backend core of Aurabase applies this principle: all services (aura-gateway, aura-auth, aura-db, aura-realtime, aura-storage, aura-functions, aura-ai…) are written in Rust, organized in a single Cargo workspace. This can be verified directly in the repository:

Cargo.tomltoml
[workspace]
resolver = "2"

members = [
    "services/aura-gateway",
    "services/aura-auth",
    "services/aura-db",
    # ...9개의 기타 서비스 + 공유 라이브러리
]

[workspace.dependencies]
axum = { version = "0.8", features = ["ws", "multipart", "macros"] }

Cargo.toml, Workspace Edition 2021, Resolver v2의 실제 추출 — Aurabase 저장소에서 확인되었습니다.

What this fact does not prove, at this stage: a p99 latency figure measured for Aurabase. We have not yet published a reproducible benchmark methodology for our own backend — this is a work in progress, not a result available today. The absence of a garbage collector is a mechanism verified in the code. This alone is not evidence of measured p99 latency. Keep this distinction in mind in the face of any marketing argument on the subject, including ours — see our technical comparison Aurabase vs Supabase for details of the architecture.

p99를 올바르게 측정하려면 대표 부하 조건, 충분히 넓은 슬라이딩 창에서 계산된 백분위수, 프로덕션에 가까운 테스트 환경 등 자체적인 원칙이 필요합니다. 이 방법론 없이 그림을 출판하는 것은 마케팅 그림을 출판하는 것과 같습니다. 이것이 바로 우리가 이 글에서 거부하는 일이다.

#
뉘앙스

GC가 없으면 해결되지 않는 것

No-GC는 마술 지팡이가 아닙니다.

가비지 수집기를 제거하면 테일 대기 시간의 원인 중 전부는 아니지만 단 하나의 원인만 제거됩니다. Rust 백엔드는 네트워크 대기, Postgres 연결 풀 포화, 데이터베이스 잠금 경쟁, 인덱싱이 불량한 SQL 쿼리 또는 느린 타사 API 호출로 인해 여전히 성능 저하된 p99를 표시할 수 있습니다. 이 문서에서 설명하는 메커니즘은 구조적 원인을 제거합니다. 다른 사람에 대한 면역을 제공하지 않습니다.

예를 들어 Aurabase에서 각 서비스는 연결 풀(sqlx)을 통해 Postgres와 통신하고 NATS JetStream을 통해 다른 서비스와 통신합니다. 크기가 작은 풀, 소비가 느린 NATS 구독 또는 적절한 인덱스가 없는 SQL 쿼리는 가비지 수집기가 없어도 각각 자체적인 지연 시간 스파이크를 생성합니다.

실용적인 결론: GC가 없다는 것은 p99 인식 시스템에 Rust 백엔드를 선택하는 좋은 아키텍처 이유입니다. 이는 Aurabase나 다른 곳에서나 그 자체로 대기 시간을 보장하지 않습니다. 중요한 방법은 동일합니다. 즉, 측정하고 방법론을 발표한 다음 측정 결과를 수정하는 것입니다. GC를 사용하여 백엔드에서 마이그레이션하는 경우 Supabase에서 Aurabase로의 마이그레이션 가이드에서 무엇이 변경되고 무엇이 동일하게 유지되는지 자세히 설명합니다.

#
자주 묻는 질문

FAQ: 가비지 수집기 및 p99 대기 시간

가비지 수집기가 없으면 낮은 p99가 보장됩니까?+
아니요. 예측할 수 없는 일시 중지의 구조적 원인을 제거하지만 네트워크, 연결 풀, 데이터베이스 잠금 등 다른 요소도 꼬리 대기 시간에 영향을 미칩니다. 위의 "No GC가 해결하지 못하는 문제" 섹션을 참조하세요.
Discord는 왜 GC Go를 다르게 조정하지 않았나요?+
팀은 2020년 게시물에 기록된 캐시 크기 감소를 포함하여 몇 가지 조정을 시도했습니다. GC 일시 중지 횟수가 줄어들고 캐시 누락이 늘어나는 등 여전히 불리한 상충 관계가 유지되었습니다. Rust로 재작성하면 타협점을 이동하는 대신 제거했습니다.
Go와 같은 최신 GC가 문제를 해결했나요?+
Go의 GC는 2015년과 2018년(go.dev) 사이에 300~400ms에서 500마이크로초로 늘어났지만 제거되지는 않았습니다. GC 추적은 구성에 따라 극단적인 경우에 대한 일시 중지 메커니즘을 유지합니다.
Aurabase가 p99 레이턴시 벤치마크를 출시했습니까?+
아직은 아닙니다. 코드에서 확인된 사실은 Garbage Collector(Rust, Workspace Cargo, axum services)가 없다는 것입니다. 측정된 p99 대기 시간 수치와 재현 가능한 방법론은 아직 공개되지 않았습니다. 우리는 출처가 없는 수치보다 수치를 선호하지 않습니다.

배포할 준비가 되셨나요?

5분 만에 백엔드를 완성하세요.

신용카드 불필요 · 500MB 무료 · 50,000 MAU