Database
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> Database

Hekaton with a twist:In-memory TVPs - Part 2

Trong bài đăng cuối cùng của mình, tôi đã chứng minh rằng ở dung lượng nhỏ, TVP được tối ưu hóa bộ nhớ có thể mang lại lợi ích hiệu suất đáng kể cho các mẫu truy vấn điển hình.

Để kiểm tra ở quy mô cao hơn một chút, tôi đã tạo một bản sao của SalesOrderDetailEnlarged bảng mà tôi đã mở rộng lên khoảng 5.000.000 hàng nhờ tập lệnh này của Jonathan Kehayias (blog | @SQLPoolBoy)).

DROP TABLE dbo.SalesOrderDetailEnlarged;
GO
 
SELECT * INTO dbo.SalesOrderDetailEnlarged 
  FROM AdventureWorks2012.Sales.SalesOrderDetailEnlarged; -- 4,973,997 rows
 
CREATE CLUSTERED INDEX PK_SODE 
  ON dbo.SalesOrderDetailEnlarged(SalesOrderID, SalesOrderDetailID);

Tôi cũng đã tạo ba phiên bản trong bộ nhớ của bảng này, mỗi phiên bản có số lượng nhóm khác nhau (đánh cá cho một "điểm ngọt") - 16,384, 131,072 và 1,048,576. (Bạn có thể sử dụng các số làm tròn số, nhưng chúng vẫn được làm tròn thành lũy thừa tiếp theo của 2.) Ví dụ:

CREATE TABLE [dbo].[SalesOrderDetailEnlarged_InMem_16K] -- and _131K and _1MM
(
	[SalesOrderID] [int] NOT NULL,
	[SalesOrderDetailID] [int] NOT NULL,
	[CarrierTrackingNumber] [nvarchar](25) COLLATE SQL_Latin1_General_CP1_CI_AS NULL,
	[OrderQty] [smallint] NOT NULL,
	[ProductID] [int] NOT NULL,
	[SpecialOfferID] [int] NOT NULL,
	[UnitPrice] [money] NOT NULL,
	[UnitPriceDiscount] [money] NOT NULL,
	[LineTotal] [numeric](38, 6) NOT NULL,
	[rowguid] [uniqueidentifier] NOT NULL,
	[ModifiedDate] [datetime] NOT NULL
 PRIMARY KEY NONCLUSTERED HASH 
 (
	[SalesOrderID],
	[SalesOrderDetailID]
 ) WITH ( BUCKET_COUNT = 16384) -- and 131072 and 1048576
) WITH ( MEMORY_OPTIMIZED = ON , DURABILITY = SCHEMA_AND_DATA );
GO
 
INSERT dbo.SalesOrderDetailEnlarged_InMem_16K
  SELECT * FROM dbo.SalesOrderDetailEnlarged;
 
INSERT dbo.SalesOrderDetailEnlarged_InMem_131K
  SELECT * FROM dbo.SalesOrderDetailEnlarged;
 
INSERT dbo.SalesOrderDetailEnlarged_InMem_1MM
  SELECT * FROM dbo.SalesOrderDetailEnlarged;
GO

Lưu ý rằng tôi đã thay đổi kích thước nhóm từ ví dụ trước (256). Khi xây dựng bảng, bạn muốn chọn "điểm hấp dẫn" cho kích thước nhóm - bạn muốn tối ưu hóa chỉ số băm cho việc tra cứu điểm, nghĩa là bạn muốn có nhiều nhóm nhất có thể với càng ít hàng trong mỗi nhóm càng tốt. Tất nhiên nếu bạn tạo ~ 5 triệu nhóm (vì trong trường hợp này, có lẽ không phải là một ví dụ tốt, có ~ 5 triệu tổ hợp giá trị duy nhất), bạn sẽ phải xử lý một số việc sử dụng bộ nhớ và đánh đổi việc thu gom rác. Tuy nhiên, nếu bạn cố gắng nhồi ~ 5 triệu giá trị duy nhất vào 256 nhóm, bạn cũng sẽ gặp một số vấn đề. Trong mọi trường hợp, cuộc thảo luận này vượt ra ngoài phạm vi thử nghiệm của tôi cho bài đăng này.

Để kiểm tra so với bảng tiêu chuẩn, tôi đã thực hiện các quy trình được lưu trữ tương tự như trong các thử nghiệm trước:

CREATE PROCEDURE dbo.SODE_InMemory
  @InMemory dbo.InMemoryTVP READONLY
AS
BEGIN
  SET NOCOUNT ON;
 
  DECLARE @tn NVARCHAR(25);
 
  SELECT @tn = CarrierTrackingNumber
    FROM dbo.SalesOrderDetailEnlarged AS sode
    WHERE EXISTS (SELECT 1 FROM @InMemory AS t
    WHERE sode.SalesOrderID = t.Item);
END
GO
 
CREATE PROCEDURE dbo.SODE_Classic
  @Classic dbo.ClassicTVP READONLY
AS
BEGIN
  SET NOCOUNT ON;
 
  DECLARE @tn NVARCHAR(25);
 
  SELECT @tn = CarrierTrackingNumber
    FROM dbo.SalesOrderDetailEnlarged AS sode
    WHERE EXISTS (SELECT 1 FROM @Classic AS t
    WHERE sode.SalesOrderID = t.Item);
END
GO

Vì vậy, trước tiên, hãy xem xét các kế hoạch, chẳng hạn như 1.000 hàng được chèn vào các biến bảng, và sau đó chạy các thủ tục:

DECLARE @InMemory dbo.InMemoryTVP;
INSERT @InMemory SELECT TOP (1000) SalesOrderID
  FROM dbo.SalesOrderDetailEnlarged
  GROUP BY SalesOrderID ORDER BY NEWID();
 
DECLARE @Classic dbo.ClassicTVP;
INSERT @Classic SELECT Item FROM @InMemory;
 
EXEC dbo.SODE_Classic  @Classic  = @Classic;
EXEC dbo.SODE_InMemory @InMemory = @InMemory;

Lần này, chúng tôi thấy rằng trong cả hai trường hợp, trình tối ưu hóa đã chọn một tìm kiếm chỉ mục được phân nhóm dựa trên bảng cơ sở và một vòng lặp lồng nhau tham gia chống lại TVP. Một số số liệu chi phí khác nhau, nhưng nếu không thì các kế hoạch khá giống nhau:

Các kế hoạch tương tự cho TVP trong bộ nhớ so với TVP cổ điển ở quy mô cao hơn

So sánh chi phí toán tử tìm kiếm - Cổ điển ở bên trái, Trong bộ nhớ ở bên phải

Giá trị tuyệt đối của chi phí làm cho có vẻ như TVP cổ điển sẽ kém hiệu quả hơn nhiều so với TVP trong bộ nhớ. Nhưng tôi tự hỏi liệu điều này có đúng trong thực tế hay không (đặc biệt là vì con số Số lần thực thi ước tính ở bên phải có vẻ đáng ngờ), vì vậy tất nhiên, tôi đã chạy một số thử nghiệm. Tôi quyết định kiểm tra các giá trị 100, 1.000 và 2.000 sẽ được gửi đến quy trình.

DECLARE @values INT = 100; -- 1000, 2000
 
DECLARE @Classic dbo.ClassicTVP;
DECLARE @InMemory dbo.InMemoryTVP;
 
INSERT @Classic(Item) 
SELECT TOP (@values) SalesOrderID
  FROM dbo.SalesOrderDetailEnlarged
  GROUP BY SalesOrderID ORDER BY NEWID();
 
INSERT @InMemory(Item) SELECT Item FROM @Classic;
 
DECLARE @i INT = 1;
 
SELECT SYSDATETIME();
 
WHILE @i <= 10000
BEGIN
  EXEC dbo.SODE_Classic  @Classic  = @Classic;
  SET @i += 1;
END
 
SELECT SYSDATETIME();
 
SET @i = 1;
 
WHILE @i <= 10000
BEGIN
  EXEC dbo.SODE_InMemory @InMemory = @InMemory;
  SET @i += 1;
END
 
SELECT SYSDATETIME();

Kết quả hiệu suất cho thấy rằng, ở số lượng tra cứu điểm lớn hơn, sử dụng TVP trong bộ nhớ dẫn đến lợi nhuận giảm đi một chút, mỗi lần chậm hơn một chút:


Kết quả của 10.000 lần thực thi sử dụng TVP cổ điển và trong bộ nhớ

Vì vậy, trái với ấn tượng mà bạn có thể đã nhận được từ bài viết trước của tôi, việc sử dụng TVP trong bộ nhớ không nhất thiết có lợi trong mọi trường hợp.

Trước đó, tôi cũng đã xem xét các thủ tục được lưu trữ được biên dịch nguyên bản và các bảng trong bộ nhớ, kết hợp với các TVP trong bộ nhớ. Điều này có thể tạo ra sự khác biệt ở đây? Spoiler:hoàn toàn không. Tôi đã tạo ba quy trình như sau:

CREATE PROCEDURE [dbo].[SODE_Native_InMem_16K] -- and _131K and _1MM
  @InMemory dbo.InMemoryTVP READONLY
WITH NATIVE_COMPILATION, SCHEMABINDING, EXECUTE AS OWNER 
AS 
  BEGIN ATOMIC WITH (TRANSACTION ISOLATION LEVEL = SNAPSHOT, LANGUAGE = N'us_english');
 
  DECLARE @tn NVARCHAR(25);
 
  SELECT @tn = CarrierTrackingNumber
    FROM dbo.SalesOrderDetailEnlarged_InMem_16K AS sode -- and _131K and _1MM
    INNER JOIN @InMemory AS t -- no EXISTS allowed here
    ON sode.SalesOrderID = t.Item;
END
GO

Một đoạn giới thiệu khác:Tôi không thể chạy 9 bài kiểm tra này với số lần lặp là 10.000 - nó mất quá nhiều thời gian. Thay vào đó, tôi lặp lại và chạy mỗi quy trình 10 lần, chạy bộ thử nghiệm đó 10 lần và lấy điểm trung bình. Đây là kết quả:


Kết quả của 10 lần thực thi sử dụng TVP trong bộ nhớ và được lưu trữ nguyên bản thủ tục

Nhìn chung, thử nghiệm này khá đáng thất vọng. Chỉ cần nhìn vào mức độ khác biệt tuyệt đối, với một bảng trên đĩa, cuộc gọi thủ tục được lưu trữ trung bình được hoàn thành trong trung bình 0,0036 giây. Tuy nhiên, khi mọi thứ đang sử dụng công nghệ trong bộ nhớ, cuộc gọi thủ tục được lưu trữ trung bình là 1,1662 giây. Rất tiếc . Rất có thể tôi đã chọn một trường hợp sử dụng kém để demo tổng thể, nhưng vào thời điểm đó, đây có vẻ là một "lần thử đầu tiên" trực quan.

Kết luận

Còn rất nhiều điều khác để kiểm tra xung quanh tình huống này và tôi có nhiều bài đăng trên blog hơn để theo dõi. Tôi vẫn chưa xác định trường hợp sử dụng tối ưu cho TVP trong bộ nhớ ở quy mô lớn hơn, nhưng hy vọng rằng bài đăng này như một lời nhắc nhở rằng mặc dù một giải pháp có vẻ tối ưu trong một trường hợp, nhưng sẽ không bao giờ là an toàn nếu cho rằng nó có thể áp dụng như nhau đến các tình huống khác nhau. Đây chính là cách nên tiếp cận OLTP trong bộ nhớ:như một giải pháp với một loạt các trường hợp sử dụng hẹp mà hoàn toàn phải được xác thực trước khi triển khai trong sản xuất.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Đặt chế độ chờ vật lý bảo vệ dữ liệu chủ động trong kiến ​​trúc một nút RAC - Phần 2

  2. Cách các kế hoạch song song bắt đầu - Phần 5

  3. Sự khác biệt giữa phép nối bên trong và phép nối bên ngoài trong SQL

  4. Cách viết câu lệnh CASE trong SQL

  5. Mười mối đe dọa phổ biến đối với chất lượng kế hoạch thực thi