테넌트 ID 우선 :이것은 전혀 선택적이지 않습니다. TenantID가 100 개만있는 경우 백만 개의 행에서 변형이 거의 없을 수 있습니다. 그러나 SQL Server는 테넌트 A에 대한 쿼리가 500,000 개의 행을 철회하지만 테넌트 B에 대한 동일한 쿼리는 50 개의 행에 불과하다는 것을 알기 때문에 이러한 쿼리에 대한 통계가 더 정확합니다. 이것이 주요 고통 지점입니다. 이 방법을 사용하면 저장 프로 시저의 첫 번째 실행이 테넌트 A에 대한 매개 변수 스니핑 문제가 발생할 가능성이 크게 증가하고 쿼리 최적화 프로그램이 이러한 통계를보고 500k 행을 효율적으로 가져와야한다는 사실을 알 수 있습니다. 그러나 50 행만있는 테넌트 B가 실행될 때 해당 실행 계획은 더 이상 적합하지 않으며 실제로는 부적절합니다. 선행 필드의 순서대로 데이터가 삽입되지 않기 때문에
그러나 첫 번째 TenantID가 저장 프로 시저를 실행하는 경우 데이터를 (적어도 인덱스 유지 관리를 수행 한 후) 물리적 및 논리적으로 구성하여 데이터 페이지를 충족시키는 데 필요한 데이터 페이지가 훨씬 적으므로 다른 접근 방식보다 성능이 향상되어야합니다. 쿼리. 즉, 동일한 데이터 페이지에 대한 물리적 I / O, 논리적 읽기, 테넌트 간 경합, 버퍼 풀에서 차지하는 공간 낭비 (페이지 수명 기대 향상) 등이 줄어 듭니다.
이 향상된 성능을 얻는 데는 두 가지 주요 비용이 있습니다. 첫 번째는 그리 어렵지 않습니다 . 조각화 증가에 대응하기 위해 정기적 인 인덱스 유지 관리를 수행 해야합니다 . 두 번째는 약간 덜 재미있다.
증가 된 매개 변수 스니핑 문제를 해결하려면 테넌트간에 실행 계획을 분리해야합니다. 간단한 접근 방식은 WITH RECOMPILE
procs 또는 OPTION (RECOMPILE)
쿼리 힌트에 사용하는 것이지만, 우선 순위 를 두어 얻을 수있는 모든 이점을 없앨 수있는 성능에 영향을 미칩니다 TenantID
. 내가 찾은 가장 좋은 방법은를 통해 매개 변수화 된 동적 SQL을 사용하는 것 sp_executesql
입니다. 동적 SQL이 필요한 이유는 TenantID를 쿼리 텍스트에 연결하는 것이 일반적으로 가능하지만 일반적으로 매개 변수가되는 다른 모든 술어는 여전히 매개 변수입니다. 예를 들어 특정 주문을 찾고 있다면 다음과 같은 작업을 수행합니다.
DECLARE @GetOrderSQL NVARCHAR(MAX);
SET @GetOrderSQL = N'
SELECT ord.field1, ord.field2, etc.
FROM dbo.Orders ord
WHERE ord.TenantID = ' + CONVERT(NVARCHAR(10), @TenantID) + N'
AND ord.OrderID = @OrderID_dyn;
';
EXEC sp_executesql
@GetOrderSQL,
N'@OrderID_dyn INT',
@OrderID_dyn = @OrderID;
이로 인해 해당 테넌트의 데이터 볼륨과 일치하는 해당 테넌트 ID에 대해서만 재사용 가능한 쿼리 계획을 작성하게됩니다. 동일한 테넌트 A가 다른 테넌트에 대해 저장 프로 시저를 다시 실행하면 @OrderID
캐시 된 쿼리 계획을 재사용합니다. 단지 TenantID의 가치 달랐다 쿼리 텍스트를 생성하는 것 같은 저장 프로 시저를 실행하는 다른 세입자하지만, 어떤 쿼리 텍스트의 차이는 다른 계획을 생성하기에 충분하다. 그리고 테넌트 B에 대해 생성 된 계획은 테넌트 B의 데이터 볼륨과 일치 할뿐만 아니라, @OrderID
술어가 여전히 매개 변수화되어 있으므로 다른 값의 테넌트 B에 대해서도 재사용 할 수 있습니다 .
이 방법의 단점은 다음과 같습니다.
- 단순한 쿼리를 입력하는 것보다 약간 더 많은 작업입니다 (그러나 모든 쿼리는 동적 SQL 일 필요는 없으며 매개 변수 스니핑 문제가 발생하는 쿼리).
- 시스템에있는 테넌트 수에 따라 각 쿼리는이를 호출하는 테넌트 ID 당 하나의 계획이 필요하므로 계획 캐시의 크기가 증가합니다. 이것은 문제가되지 않지만 적어도 알아야 할 사항입니다.
동적 SQL은 소유권 체인을 중단합니다. 즉 EXECUTE
, 스토어드 프로 시저에 대한 권한을 가지고 테이블에 대한 읽기 / 쓰기 액세스를 가정 할 수 없습니다 . 쉽고 덜 안전한 수정은 사용자에게 테이블에 직접 액세스하는 것입니다. 이것은 이상적이지는 않지만 일반적으로 빠르고 쉬운 트레이드 오프입니다. 보다 안전한 방법은 인증서 기반 보안을 사용하는 것입니다. 의미, 인증서를 만든 다음 해당 인증서에서 사용자를 만들고 해당 권한 을 사용자 에게 부여 합니다 (인증서 기반 사용자 또는 로그인은 자체적으로 SQL Server에 연결할 수 없음). 그런 다음 Dynamic SQL을 사용하는 저장 프로 시저에 서명 ADD SIGNATURE 를 통한 동일한 인증서 .
모듈 서명 및 인증서에 대한 자세한 내용은 ModuleSigning.Info 를 참조하십시오.