Saya sarankan menggunakan rasa metode 2 dan memecah rentang pencarian menjadi banyak tabel target. 10.000 meja adalah upaya pertama yang bagus. Misalnya, jika Anda mencari "012345678901" maka kueri Anda akan melihat tabel yang terkait dengan data yang dimulai dengan "0123". Anda masih memiliki sekitar satu triliun baris secara total, tetapi memecah data menjadi banyak tabel memiliki kualitas positif berikut:
- Semua string 12 digit yang mungkin sekarang dapat masuk ke dalam INT.
- Membangun representasi pencarian yang lebih efisien dari string 1 TB Anda sepertinya akan mahal biar bagaimanapun. Dengan banyak tabel, Anda dapat dengan mudah memparalelkan pekerjaan dan bahkan sementara meminta lebih banyak CPU untuk dialokasikan ke VM Anda.
- Anda bisa membangun satu tabel sebagai bukti konsep untuk menentukan waktu kueri dan total kebutuhan ruang untuk string penuh.
- Jika Anda perlu melakukan pemeliharaan database apa pun, Anda akan senang bahwa Anda tidak membuat satu tabel besar.
Pada titik ini, pertanyaan utama bagi saya adalah apakah Anda menggunakan rowstore terkompresi atau kolomstore. Kode di bawah ini membuat tabel rowstore untuk ruang pencarian "0123" dan memasukkan 100 juta baris ke dalamnya. Jika string Anda cukup acak maka Anda juga bisa berharap untuk melihat sekitar 100 juta baris per tabel.
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;
Berita buruknya adalah untuk kumpulan data lengkap yang mungkin Anda perlukan sekitar 15,4 TB. Kabar baiknya adalah bahwa kueri hanya membutuhkan 1 ms untuk saya walaupun tidak ada data yang relevan dalam cache buffer, yang hampir selalu merupakan kasus untuk set data sebesar milik Anda.
-- 1 ms
CHECKPOINT;
DBCC DROPCLEANBUFFERS;
SELECT MIN(STR_POS)
FROM dbo.T0123_Q229892_PAGE_COMPRESSION
WHERE STRING_PIECE = 45678901; -- searching for '012345678901'
Anda mungkin dapat membuang data ini pada penyimpanan termurah yang Anda miliki dan masih melihat waktu respons yang baik karena kueri melakukan beberapa pembacaan logis.
Untuk toko kolom, Anda tidak dapat mencari data yang Anda butuhkan dan Anda masih sangat tidak mungkin untuk memasukkan semua data Anda ke dalam memori, jadi penting untuk membaca sesedikit mungkin data terkompresi dengan pertanyaan Anda. Saya sangat menyarankan untuk mempartisi tabel Anda. Salah satu pendekatan sederhana yang berfungsi dengan baik adalah menggunakan empat digit pertama dari string pencarian Anda untuk menemukan nama tabel dan dua digit berikutnya sebagai partisi. Menggunakan "012345678901" lagi, Anda akan pergi ke partisi 45 dari tabel yang menyimpan data untuk "0123". 100 partisi adalah angka yang baik untuk menghindari masalah yang disebabkan oleh terlalu banyak partisi dan Anda akan berakhir dengan rata-rata sekitar 1 juta baris untuk setiap partisi. Jumlah maksimum baris yang dapat masuk ke dalam satu grup baris adalah 1048576, jadi dengan pendekatan ini Anda akan melakukan IO sesedikit mungkin.
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
Dengan pendekatan ini, kumpulan data lengkap akan membutuhkan sekitar 10,9 TB. Tidak jelas bagi saya bagaimana membuatnya lebih kecil. Kueri pencarian agak lambat dalam hal ini. Di komputer saya dibutuhkan sekitar 25 ms, tetapi ini sebagian besar akan tergantung pada IO:
CHECKPOINT;
DBCC DROPCLEANBUFFERS;
SELECT MIN(STR_POS)
FROM dbo.T0123_Q229892_COLUMNSTORE
WHERE PART_ID = 45
AND STRING_PIECE = 678901; -- searching for '012345678901'
Satu catatan penting pada pendekatan columnstore adalah bahwa angka 10,9 TB adalah untuk data terkompresi 100%. Akan sulit untuk mengisi meja seperti itu secara efisien sambil menghindari toko-toko delta. Kemungkinan Anda akan berakhir dengan data yang tidak terkompresi di toko delta di beberapa titik dalam proses yang bisa dengan mudah membutuhkan lebih dari 15,4 TB yang digunakan untuk pendekatan rowstore.