측정 가능한 목표는 사람들이 그것을 최적화하기 시작하면 유용성을 잃게 된다는 Goodhart의 법칙은 데이터베이스 벤치마킹에서도 매우 잘 성립합니다. 물론 이것이 새로운 현상은 아닙니다. 유명하게도, TPC-D는 사람들이 과도한 캐싱 때문에 쿼리 실행 시간이 제로에 수렴하도록 너무 많이 최적화하면서 폐기되었습니다. TPC-H는 스스로를 ad-hoc 벤치마크라고 선언하고, 작업 부하(workload)에 대한 지식 활용이나 결과 캐싱과 같은 것들(그리고 여러 가지 다른 최적화 방법)을 명시적으로 금지함으로써 이 문제를 해결했습니다.
우리는 오늘날 AI 코드 생성의 사용으로 인해 ClickBench 벤치마크에서도 동일한 문제를 목격하고 있습니다. 만약 AI에게 쿼리를 더 빠르게 만들라고 지시하면, 이것이 일반적으로 타당한 접근 방식인지 여부와 관계없이 쿼리를 더 빠르게 만들 것입니다. 예를 들어, pgrust 구현은 긴 문자열 내용을 무시하고 순전히 해시 값(hash value)에 기반하여 문자열 컬럼에 대한 group-by 집계(group-by aggregations)를 수행합니다. 물론 이것은 엄청난 성능 향상이며 이 벤치마크에서는 작동하지만, 잘못된 결과를 초래할 수 있습니다. 더 빠른가요? 예. 타당한 최적화인가요? 아마 아닐 것입니다. 그리고 그것은 다른 많은 일들을 합니다. 예를 들어, ClickBench는 여러 쿼리에 걸쳐 특정 술어(predicate)를 반복하며, 따라서 pgrust는 해당 술어를 캐싱하여 두 번째 실행 시 쿼리 비용을 완전히 제거합니다. 또는 userid별로 그룹화된 튜플을 반환하는 Q17의 경우, a) 튜플이 대략적으로 userid별로 클러스터링되어 있고, b) 쿼리가 order by 없이 limit 절을 지정한다는 사실을 활용합니다. 따라서 어떤 userid든 상관없으며 대부분의 데이터를 읽지 않습니다. 인간은 이러한 미세한 마이크로 최적화(micro-optimizations)를 수행하지 않을 것입니다. 왜냐하면 그것들이 일반적인 사용 사례에서 관련성이 낮을 가능성이 높기 때문입니다. 하지만 AI는 물론 각 쿼리를 세밀하게 살펴보고 그에 맞게 조정할 수 있습니다.
물론 문제의 일부는 ClickBench 자체에 있습니다. 이 벤치마크는 무엇이 허용되고 무엇이 허용되지 않는지에 대한 명세가 불분명하며, 업데이트 스트림(update stream)도 없고, 쿼리가 매우 단순하고, 술어(predicates)를 공유하며, 정의가 미흡한 등 문제가 많습니다. 하지만 근본적인 문제는 특정 구체적인 벤치마크와는 무관합니다. 우리가 특정 벤치마크에 대해 공격적으로 최적화를 시작하는 순간, 그 벤치마크는 더 이상 유용하지 않게 됩니다. 역사적으로 이러한 압력은 시스템이 일반적으로 유용해야 한다는 요구사항 덕분에 다소 완화되었습니다. 즉, 단순히 벤치마크에서 좋은 성능을 보이는 것뿐만 아니라 고객에게도 유용해야 했습니다. 물론 이는 vibe-코딩된 실행 엔진(vibe-coded execution engines)의 관심사는 아닙니다.
이에 대해 무엇을 해야 할지는 명확하지 않습니다. 저희 SQLStorm 제안은 수만 개의 쿼리를 포함함으로써 과적합(overfitting)을 어렵게 하려고 시도했습니다. 아무도 그렇게 방대한 워크로드에 맞춰 시스템을 미세 조정하지 않을 것입니다. 하지만 AI 도구를 사용한다면 필요하다면 만 개의 쿼리까지 상세하게 조사할 수 있으므로, 이것은 진정한 방어책이 아닙니다.
저는 의미를 가지려면 벤치마크가 a) TPC-H처럼 임시적(ad-hoc)이어야 하며, 사전 지식 활용과 어떠한 형태의 중간 결과 캐싱도 금지해야 한다고 생각합니다. 그렇지 않으면 실행 시간이 0으로 수렴하게 됩니다. 그리고 b) 벤치마크는 어떤 형태의 업데이트 스트림을 포함해야 합니다. 그렇지 않으면 지나치게 공격적인 읽기 전용(read-only) 또는 읽기 위주(read-mostly) 시스템이 항상 완전한 트랜잭션 지원을 제공하는 시스템보다 우수한 성능을 보일 것이기 때문입니다. 그리고 c) 벤치마크는 테스트되는 시스템보다 더 오래된, 진정으로 임시적이어야 합니다. Goodhart의 법칙에 맞서 싸우는 한 가지 방법은 (자동으로) 시간이 지남에 따라 새로운 벤치마크를 만들어내고, 그 전에 존재했던 시스템들을 측정하는 것입니다. 물론 캐싱/사전 계산 문제는 여전히 남아있지만, 적어도 이는 단 하나의 특정 벤치마크에 대한 과도한 조정을 막아줄 것입니다.
흥미롭게도, 머신러닝(machine learning) 커뮤니티는 수년 동안 정확히 같은 문제로 고심해 왔으며, 우리는 그들의 아이디어를 빌려와야 할 것입니다. 예를 들어, Kaggle 대회는 참가자들이 결코 볼 수 없는 숨겨진 테스트 세트(hidden test set)를 기준으로 제출물을 순위 매기는데, 이는 공개 데이터에 대한 과적합(overfitting)을 무의미하게 만듭니다. 유사하게, LiveBench와 같은 벤치마크는 모델이 학습 과정에서 볼 수 없도록 정기적으로 새로운 질문을 공개합니다. 데이터베이스 벤치마킹의 경우 실제 벤치마크 쿼리(benchmark queries)를 비밀로 유지하고 개발 목적으로 대표 샘플만 공개하거나, 더 나아가 모든 평가 라운드마다 새로운 워크로드(workload)를 생성하는 것을 의미할 수 있습니다. SQLStorm은 이미 생성된 쿼리(generated queries)를 사용하고 있으므로, 때때로 새로운 쿼리 세트를 만드는 것은 상당히 쉬울 것입니다. 우리는 아마도 더 일반적인 접근 방식(스키마(schemas)의 높은 다양성, 업데이트 스트림(update streams), 트랜잭션(transactions) 등)을 원할 것이지만, 이는 벤치마크를 의미 있게 유지하는 데 도움이 될 방향으로 보입니다.