Tôi khuyên bạn nên sử dụng hương vị của phương pháp 2 và chia phạm vi tìm kiếm thành nhiều bảng mục tiêu. 10000 bảng là một nỗ lực đầu tiên tốt. Ví dụ: nếu bạn tìm kiếm "012345678901" thì truy vấn của bạn sẽ xem bảng được liên kết với dữ liệu bắt đầu bằng "0123". Bạn vẫn sẽ có tổng cộng khoảng một nghìn tỷ hàng, nhưng việc chia dữ liệu thành nhiều bảng có những phẩm chất tích cực sau:
- Tất cả các chuỗi 12 chữ số có thể bây giờ có thể phù hợp với một INT.
- Xây dựng một đại diện hiệu quả tìm kiếm hơn cho chuỗi 1 TB của bạn có thể sẽ tốn kém cho dù thế nào đi chăng nữa. Với nhiều bảng, bạn có thể dễ dàng song song hóa công việc và thậm chí tạm thời yêu cầu nhiều CPU được phân bổ cho VM của bạn.
- Bạn có thể xây dựng một bảng duy nhất làm bằng chứng về khái niệm để xác định thời gian truy vấn và tổng yêu cầu không gian cho chuỗi đầy đủ.
- Nếu bạn cần thực hiện bất kỳ loại bảo trì cơ sở dữ liệu nào, bạn sẽ rất vui vì bạn đã không tạo một bảng lớn.
Tại thời điểm này, câu hỏi chính đối với tôi là liệu bạn sử dụng kho hàng nén hay cột lưu trữ. Mã bên dưới tạo bảng hàng cho không gian tìm kiếm "0123" và chèn 100 triệu hàng vào đó. Nếu chuỗi của bạn đủ ngẫu nhiên thì bạn cũng có thể thấy khoảng 100 triệu hàng trên mỗi bảng.
DROP TABLE IF EXISTS #t;
SELECT TOP (10000) 0 ID INTO #t
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
OPTION (MAXDOP 1);
DROP TABLE IF EXISTS dbo.Q229892_RAW_100M_RANGE;
CREATE TABLE dbo.Q229892_RAW_100M_RANGE (
STRING_PIECE INT NOT NULL,
STR_POS BIGINT NOT NULL
);
INSERT INTO dbo.Q229892_RAW_100M_RANGE WITH (TABLOCK)
SELECT ABS(CHECKSUM(NEWID()) % 100000000),
TRY_CAST(ABS(CHECKSUM(NEWID())) AS BIGINT) * TRY_CAST(ABS(CHECKSUM(NEWID())) AS BIGINT)
FROM #t t1
CROSS JOIN #t t2
OPTION (MAXDOP 4);
DROP TABLE IF EXISTS dbo.T0123_Q229892_PAGE_COMPRESSION;
CREATE TABLE dbo.T0123_Q229892_PAGE_COMPRESSION (
STRING_PIECE INT NOT NULL,
STR_POS BIGINT NOT NULL,
PRIMARY KEY (STRING_PIECE, STR_POS)
) WITH (DATA_COMPRESSION = PAGE);
INSERT INTO dbo.T0123_Q229892_PAGE_COMPRESSION WITH (TABLOCK)
SELECT STRING_PIECE, STR_POS
FROM dbo.Q229892_RAW_100M_RANGE;
Tin xấu là cho tập dữ liệu đầy đủ mà bạn có thể cần khoảng 15,4 TB. Tin vui là các truy vấn chỉ mất 1 ms cho tôi ngay cả khi không có dữ liệu liên quan trong bộ đệm bộ đệm, điều này hầu như sẽ luôn là trường hợp đối với một tập dữ liệu lớn như của bạn.
-- 1 ms
CHECKPOINT;
DBCC DROPCLEANBUFFERS;
SELECT MIN(STR_POS)
FROM dbo.T0123_Q229892_PAGE_COMPRESSION
WHERE STRING_PIECE = 45678901; -- searching for '012345678901'
Bạn có thể có thể ném dữ liệu này vào bộ lưu trữ rẻ nhất mà bạn có và vẫn thấy thời gian phản hồi tốt vì truy vấn không có quá nhiều lần đọc logic.
Đối với nhà lưu trữ cột, bạn không thể tìm kiếm dữ liệu bạn cần và bạn vẫn không thể phù hợp với tất cả dữ liệu của mình vào bộ nhớ, vì vậy điều quan trọng là đọc càng ít dữ liệu nén càng tốt với các truy vấn của bạn. Tôi thực sự khuyên bạn nên phân vùng bảng của bạn. Một cách tiếp cận đơn giản hoạt động tốt là sử dụng bốn chữ số đầu tiên của chuỗi tìm kiếm của bạn để tìm tên bảng và hai chữ số tiếp theo làm phân vùng. Sử dụng "012345678901" một lần nữa, bạn sẽ đi đến phân vùng 45 của bảng chứa dữ liệu cho "0123". 100 phân vùng là một con số tốt để tránh các vấn đề gây ra bởi quá nhiều phân vùng và trung bình bạn sẽ có khoảng 1 triệu hàng cho mỗi phân vùng. Số lượng hàng tối đa có thể phù hợp với một nhóm hàng duy nhất là 1048576, vì vậy với phương pháp này, bạn sẽ thực hiện càng ít IO càng tốt.
DROP TABLE IF EXISTS dbo.Q229892_RAW_1M_RANGE;
CREATE TABLE dbo.Q229892_RAW_1M_RANGE (
STRING_PIECE INT NOT NULL,
STR_POS BIGINT NOT NULL
);
INSERT INTO dbo.Q229892_RAW_1M_RANGE WITH (TABLOCK)
SELECT TOP (1000000) ABS(CHECKSUM(NEWID()) % 1000000),
TRY_CAST(ABS(CHECKSUM(NEWID())) AS BIGINT) * TRY_CAST(ABS(CHECKSUM(NEWID())) AS BIGINT)
FROM master..spt_values t1
CROSS JOIN master..spt_values t2
OPTION (MAXDOP 1);
DECLARE @IntegerPartitionFunction nvarchar(max) =
N'CREATE PARTITION FUNCTION partition100 (tinyint)
AS RANGE LEFT FOR VALUES (';
DECLARE @i int = 0;
WHILE @i < 100
BEGIN
SET @IntegerPartitionFunction += CAST(@i as nvarchar(10)) + N', ';
SET @i += 1;
END
SET @IntegerPartitionFunction += CAST(@i as nvarchar(10)) + N');';
EXEC sp_executesql @IntegerPartitionFunction;
GO
CREATE PARTITION SCHEME partition100_scheme
AS PARTITION partition100
ALL TO ([DEFAULT]);
DROP TABLE IF EXISTS dbo.T0123_Q229892_COLUMNSTORE;
-- this table must be partitioned by PART_ID!
CREATE TABLE dbo.T0123_Q229892_COLUMNSTORE (
PART_ID TINYINT NOT NULL,
STRING_PIECE INT NOT NULL,
STR_POS BIGINT NOT NULL,
INDEX CCS CLUSTERED COLUMNSTORE
) ON partition100_scheme (PART_ID);
GO
DECLARE @part_id TINYINT = 0;
SET NOCOUNT ON;
WHILE @part_id < 100
BEGIN
INSERT INTO dbo.T0123_Q229892_COLUMNSTORE WITH (TABLOCK)
SELECT @part_id, STRING_PIECE, STR_POS
FROM dbo.Q229892_RAW_1M_RANGE
OPTION (MAXDOP 1);
SET @part_id = @part_id + 1;
END;
GO
Với phương pháp này, bộ dữ liệu đầy đủ sẽ cần khoảng 10,9 TB. Tôi không rõ làm thế nào để làm cho nó nhỏ hơn. Truy vấn tìm kiếm chậm hơn một chút trong trường hợp này. Trên máy của tôi mất khoảng 25 ms, nhưng điều này chủ yếu sẽ phụ thuộc vào IO:
CHECKPOINT;
DBCC DROPCLEANBUFFERS;
SELECT MIN(STR_POS)
FROM dbo.T0123_Q229892_COLUMNSTORE
WHERE PART_ID = 45
AND STRING_PIECE = 678901; -- searching for '012345678901'
Một lưu ý quan trọng trong cách tiếp cận cột là con số 10,9 TB dành cho dữ liệu nén 100%. Sẽ rất khó khăn để đưa vào một bảng như vậy một cách hiệu quả trong khi tránh các cửa hàng delta. Có khả năng bạn sẽ kết thúc với dữ liệu không nén trong các cửa hàng delta tại một số điểm trong quy trình có thể dễ dàng yêu cầu nhiều hơn 15,4 TB được sử dụng cho phương pháp tiếp cận hàng.