다음을 통해 공유


성능 소개

데이터베이스 성능은 데이터베이스, 네트워킹, 데이터베이스 드라이버, 데이터 액세스 계층(예: EF Core) 등의 전체 구성 요소 스택에 걸쳐 있는 방대하고 복잡한 주제입니다. 상위 계층 및 O/RM(예: EF Core)은 애플리케이션 개발을 상당히 간소화하고 유지 관리성을 개선하는 반면, 때로는 불투명한 것이어서 실행되고 있는 SQL 같은 성능에 중요한 내부 세부 정보를 숨기는 경우도 있습니다. 이 섹션에서는 EF Core를 사용하여 성능을 향상하는 방법 및 애플리케이션 성능을 저하할 수 있는 일반적인 문제를 방지하는 방법의 개요를 제공합니다.

병목 상태 파악 및 측정

항상 성능에 관해서는 문제를 표시하는 데이터가 없는데 성급하게 최적화하려고 하지 않는 것이 중요합니다. 위대한 Donald Knuth가 “성급한 최적화는 모든 악의 근원입니다.”라고 말한 것처럼 말입니다. 성능 진단 섹션에서는 애플리케이션이 데이터베이스 논리에서 시간을 소비하는 지점을 파악하는 다양한 방법과 특정 문제 영역을 정확히 찾아내는 방법을 설명합니다. 느린 쿼리를 파악하고 나서는 데이터베이스에 인덱스가 없는지와 다른 쿼리 패턴을 시도해야 하는지 등을 확인하여 해결 방법을 고려할 수 있습니다.

항상 코드와 가능한 대안을 직접 벤치마크하세요. 성능 진단 섹션에는 사용자 고유의 벤치마크를 위한 템플릿으로 사용할 수 있는, BenchmarkDotNet을 이용한 샘플 벤치마크가 포함되어 있습니다. 일반적인 퍼블릭 벤치마크가 특정 사용 사례에 그대로 적용된다고 가정하지 마세요. 데이터베이스 대기 시간, 쿼리 복잡성, 테이블의 실제 데이터양 등의 다양한 요소가 최상의 솔루션이 무엇인지에 큰 영향을 미칠 수 있습니다. 예를 들어 많은 퍼블릭 벤치마크는 데이터베이스에 대한 대기 시간이 거의 0인 이상적인 네트워킹 조건에서 데이터베이스 쪽의 처리(또는 디스크 I/O)가 거의 필요하지 않은 매우 간단한 쿼리를 사용하여 수행됩니다. 벤치마크가 다양한 데이터 액세스 계층의 런타임 오버헤드를 비교하는 데 중요하기는 하지만, 드러나는 차이는 데이터베이스가 실제 작업을 수행하고 데이터베이스에 대한 대기 시간이 중요한 성능 요소인 실제 애플리케이션에서는 일반적으로 미미한 것으로 입증됩니다.

데이터 액세스 성능 측면

전반적인 데이터 액세스 성능은 다음과 같은 광범위한 범주로 나뉠 수 있습니다.

  • 순수 데이터베이스 성능. 관계형 데이터베이스를 사용하는 경우 EF는 애플리케이션의 LINQ 쿼리를 데이터베이스에 의해 실행되는 SQL 문으로 변환합니다. 이 SQL 문 자체는 더 효율적이거나 덜 효율적으로 실행될 수 있습니다. 적절한 위치에 적절한 인덱스를 사용하면 SQL 성능이 크게 차이 날 수 있거나, LINQ 쿼리를 다시 작성하면 EF가 향상된 SQL 쿼리를 생성할 수 있습니다.
  • 네트워크 데이터 전송. 모든 네트워킹 시스템과 마찬가지로 네트워크상에서 오고 가는 데이터의 양을 제한하는 것이 중요합니다. 그러면 실제로 필요한 데이터만 보내고 로드할 수 있을 뿐만 아니라 관련 엔터티를 로드할 때 “데카르트 급증” 효과를 방지할 수 있습니다.
  • 네트워크 왕복. 오고 가는 데이터의 양 외에 네트워크 왕복은 애플리케이션과 데이터베이스 간에 오고 갑니다. 데이터베이스에서 쿼리를 실행하는 데 걸리는 시간은 시간 패킷으로 줄일 수 있기 때문입니다. 왕복 오버헤드는 환경에 따라 크게 달라집니다. 데이터베이스 서버가 멀리 있을수록 대기 시간이 더 길고 각 왕복의 비용이 더 비싸집니다. 클라우드의 출현으로 애플리케이션은 점점 더 데이터베이스에서 멀어지고 너무 많은 왕복을 수행하는 “대화량이 많은” 애플리케이션은 성능 저하가 발생합니다. 따라서 애플리케이션이 데이터베이스에 연결되는 경우, 수행되는 왕복 수 및 해당 숫자가 최소화될 수 있는지를 정확하게 이해하는 것이 중요합니다.
  • EF 런타임 오버헤드. 마지막으로, EF 자체도 데이터베이스 작업에 다음과 같은 런타임 오버헤드를 추가합니다. 예를 들어 EF는 쿼리를 LINQ to SQL에서 컴파일해야 하며(일반적으로 이 작업은 한 번만 수행하면 됨), 변경 내용 추적은 오버헤드를 추가합니다(그러나 사용하지 않도록 설정할 수 있음). 실제로 실제 애플리케이션의 EF 오버헤드는 대부분의 경우 미미한데, 데이터베이스의 쿼리 실행 시간 및 네트워크 대기 시간이 총 시간의 대부분을 차지하기 때문입니다. 그러나 옵션을 이해하고 몇 가지 문제를 방지하는 방법을 이해하는 것이 중요합니다.

내부적으로 발생하고 있는 상황 파악

EF를 사용하면 개발자가 SQL을 생성하고 결과를 구체화하고 기타 작업을 수행하여 비즈니스 논리에 집중할 수 있습니다. 모든 계층 또는 추상화와 마찬가지로, EF도 실행되고 있는 실제 SQL 쿼리 같은 내부적으로 발생하고 있는 상황은 숨기는 경향이 있습니다. 모든 애플리케이션에서 성능이 반드시 중요한 측면인 것은 아니지만, 성능이 있는 애플리케이션에서 개발자는 보내는 SQL 쿼리를 검사하고 N+1 문제가 발생하고 있지 않은지 확인하기 위해 왕복을 따라가는 등 EF가 수행하는 작업을 이해하는 것이 중요합니다.

데이터베이스 외부의 캐시

마지막으로, 데이터베이스와 상호 작용하는 가장 효율적인 방법은 전혀 상호 작용하지 않는 것입니다. 즉, 데이터베이스 액세스가 애플리케이션에서 성능 병목 상태로 표시되는 경우 요청을 최소화하기 위해 특정 결과를 데이터베이스 외부에 캐시하는 것이 유용할 수 있습니다. 캐싱은 복잡성을 추가하지만, 스케일링 가능한 모든 애플리케이션의 특히 중요한 부분입니다. 늘어난 부하를 처리하기 위해 서버를 더 추가하여 애플리케이션 계층을 쉽게 스케일링할 수 있지만, 데이터베이스 계층 스케일링은 일반적으로 훨씬 더 복잡합니다.