
Datadog은 Go 1.24 업그레이드 과정에서 메모리 사용량 이슈를 추적하며, 새로운 스위스 테이블(Swiss Tables) 도입이 대규모 서비스에서 막대한 메모리 절감으로 이어졌음을 확인했습니다. 대용량 맵의 동작 원리, 메모리 프로파일링, 구조체 수준의 최적화 과정을 시간 순서대로 상세히 분석하고, 실질적인 운영 비용 절감까지의 여정을 담았습니다. 핵심은 Go 1.24의 변화된 맵 구현이 대규모 환경에서의 메모리 효율과 구조 자체의 혁신적인 최적화를 가능하게 했다는 점입니다.
Datadog은 Go 1.24로 업그레이드하면서 런타임 레벨의 메모리 증가(regression) 문제를 파악하여 Go 커뮤니티와 함께 원인 분석 및 패치 적용까지 완료했습니다. 하지만 기대와 달리 가장 트래픽이 많은 환경에서 메모리 사용량이 이전보다 훨씬 더 감소하는 현상이 나타났습니다. 그 이유를 찾기 위해 런타임 라이브 힙 프로파일을 상세히 들여다보기 시작했습니다.
"우리는 ShardRouter 패키지의 shardRoutingCache 맵에서 약 500MiB의 라이브 힙 사용량을 절감했습니다."
이 수치는 Go GC 정책(GOGC)까지 고려하면 실제 메모리 사용량이 1GiB(500MiB x 2) 감소한 것을 의미합니다. 파트1에서 언급한 mallocgc 관련 메모리 증가(~400MiB)를 반영해도 전체적으로 약 600MiB 메모리 순감소가 있었습니다.
Datadog의 데이터 처리 서비스 일부는 ShardRouter라는 패키지를 사용합니다. 서비스 시작 시 데이터베이스에서 데이터를 가져와 shardRoutingCache 맵에 채웁니다. 맵의 타입은 다음과 같습니다.
shardRoutingCache map[string]Response
여기서 값 타입(Response)은 여러 필드를 가지지만, 실제로 대부분의 필드가 사용되지 않는 경우도 많았습니다.
"이 수치는 실제 운영 환경에서 본 것과도 잘 일치합니다: Go 1.23의 shardRoutingCache 맵 힙 점유량은 약 930MiB에 달했죠."
Go 1.24에서는 스위스 테이블(Swiss Tables))과 확장 해싱(Extendible Hashing) 기반으로 맵 구조가 완전히 재설계됩니다.
"스위스 테이블에서는 컨트롤 워드 덕분에 모든 슬롯을 일일이 선형 검색하지 않고, 한 번에 슬롯 후보를 좁힐 수 있습니다."
"Go 1.24의 테이블 스플리팅 접근법은 Go 1.23처럼 오래된 버킷까지 계속 메모리에 남겨두지 않으므로, 더 메모리 효율적입니다."
트래픽이 적은 환경에서는 맵의 요소 수가 적어 절감 효과가 상대적으로 작았습니다.
코드를 재검토하다 보니, shardRoutingCache 맵에 저장할 때 Response 구조체의 RoutingKey 및 LastModified 필드가 전혀 사용되지 않고 있음을 발견했습니다. 또한 ShardType 필드도 실제로는 3가지 값만 사용되어, 굳이 int64가 아닌 uint8로 줄일 수 있었습니다.
다음 두 가지 최적화를 적용했습니다.
cachedResponse)를 도입해, 불필요한 string, nil 포인터 필드 제거"이 변경 덕분에 각 key-value 쌍이 56바이트에서 24바이트로 감소했습니다."
최적화 후, Datadog은 freed memory를다음 두 가지 방식으로 활용할 수 있었습니다.
쿠버네티스 컨테이너 메모리 할당량 낮추기
→ freed memory를 클러스터 내 다른 앱이 활용 가능

GOMEMLIMIT 설정으로 CPU와 메모리 트레이드 오프
→ 일부 CPU 바운드 workload는 메모리 자원을 CPU 효율 향상에 활용해, pod 개수 downscaling 가능

마지막으로, 이번 경험에서 얻은 주요 교훈은 다음과 같습니다.
런타임 변화 추적 및 커뮤니티 협업의 중요성
"우리는 Go 1.24에서 도입된 미묘하지만 임팩트가 큰 메모리 회귀 이슈를 찾아내고 패치에 기여했습니다."
언어 버전 업그레이드시 단순 성능 향상 기대보다는, 불가피하게 이슈가 발견될 수 있음을 항상 염두에 둘 것
→ 최신 버전에서만 누릴 수 있는 최적화(ex. Swiss Tables)는 적극 활용, 문제점은 빠르게 발견·대응해야 함
정확한 런타임 메트릭과 라이브 힙 프로파일링이 문제 파악 및 커뮤니케이션에 결정적 역할
Go 1.24의 Swiss Table 도입이 대형 맵에서 메모리 사용량을 획기적으로 줄였음
"대용량 환경에서 Map 메모리 사용량이 약 70% 감소했습니다."
구조체 설계, 불필요한 필드 제거 및 타입 최적화 등 '사소해보이는' 코드 개선도 대규모 운영 환경에서는 막대한 효과를 발휘할 수 있음을 재확인
Go 1.24의 스위스 테이블 도입과 구조체 최적화는 Datadog의 대규모 서비스에 실질적인 메모리 절감과 운영비용 감소 효과를 가져왔습니다. 작은 디테일이 모여 대규모 시스템에서 놀라운 성능 향상으로 이어질 수 있음을 이번 사례가 잘 보여줍니다. Datadog은 앞으로도 커뮤니티와 함께 최신 기술 변화를 선도하며, 밑바닥부터 꼼꼼하게 최적화하는 엔지니어링 문화를 이어갈 계획입니다. 🚀