Je recommande d'utiliser une version de la méthode 2 et de diviser la plage de recherche en plusieurs tables cibles. 10000 tables est une bonne première tentative. Par exemple, si vous recherchez «012345678901», votre requête examinera la table associée aux données commençant par «0123». Vous auriez toujours environ un billion de lignes au total, mais la division des données en plusieurs tableaux présente les qualités positives suivantes:
- Toutes les chaînes de 12 chiffres possibles peuvent désormais tenir dans un INT.
- Construire une représentation plus efficace de la recherche de votre chaîne de 1 To va probablement coûter cher quoi qu'il arrive. Avec de nombreuses tables, vous pouvez facilement paralléliser le travail et même demander temporairement plus de CPU à allouer à votre VM.
- Vous pouvez créer une seule table comme preuve de concept pour déterminer le temps de requête et les exigences d'espace total pour la chaîne complète.
- Si jamais vous avez besoin de faire n'importe quel type de maintenance de base de données, vous serez heureux de ne pas avoir créé une énorme table.
À ce stade, la question principale pour moi est de savoir si vous utilisez un magasin de lignes compressé ou un magasin de colonnes. Le code ci-dessous crée une table de magasin de lignes pour l'espace de recherche "0123" et y insère 100 millions de lignes. Si votre chaîne est suffisamment aléatoire, vous pouvez également vous attendre à voir environ 100 millions de lignes par table.
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;
La mauvaise nouvelle concerne l'ensemble de données dont vous auriez probablement besoin d'environ 15,4 To. La bonne nouvelle est que les requêtes ne prennent que 1 ms pour moi même lorsqu'aucune donnée pertinente ne se trouve dans le cache de tampon, ce qui sera presque toujours le cas pour un ensemble de données aussi volumineux que le vôtre.
-- 1 ms
CHECKPOINT;
DBCC DROPCLEANBUFFERS;
SELECT MIN(STR_POS)
FROM dbo.T0123_Q229892_PAGE_COMPRESSION
WHERE STRING_PIECE = 45678901; -- searching for '012345678901'
Vous pouvez probablement jeter ces données sur le stockage le moins cher que vous avez et toujours voir de bons temps de réponse car la requête effectue si peu de lectures logiques.
Pour columnstore, vous ne pouvez pas rechercher les données dont vous avez besoin et vous avez encore très peu de chances de mettre toutes vos données en mémoire, il est donc important de lire le moins de données compressées possible avec vos requêtes. Je recommande fortement de partitionner vos tables. Une approche simple qui fonctionne bien consiste à utiliser les quatre premiers chiffres de votre chaîne de recherche pour trouver le nom de la table et les deux chiffres suivants comme partition. En utilisant à nouveau "012345678901", vous iriez à la partition 45 de la table qui contient les données pour "0123". 100 partitions est un bon nombre pour éviter les problèmes causés par trop de partitions et vous vous retrouverez avec environ 1 million de lignes pour chaque partition en moyenne. Le nombre maximum de lignes pouvant tenir dans un seul groupe de lignes est de 1048576, donc avec cette approche, vous ferez le moins d'E / S possible.
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
Avec cette approche, l'ensemble de données complet nécessiterait environ 10,9 To. Je ne sais pas comment réduire cela. La requête de recherche est un peu plus lente dans ce cas. Sur ma machine, cela prend environ 25 ms, mais cela dépendra principalement des IO:
CHECKPOINT;
DBCC DROPCLEANBUFFERS;
SELECT MIN(STR_POS)
FROM dbo.T0123_Q229892_COLUMNSTORE
WHERE PART_ID = 45
AND STRING_PIECE = 678901; -- searching for '012345678901'
Une remarque importante sur l'approche columnstore est que le chiffre de 10,9 To correspond à des données 100% compressées. Il sera difficile de remplir efficacement une telle table tout en évitant les magasins delta. Il est probable que vous vous retrouviez avec des données non compressées dans les magasins delta à un moment donné du processus, ce qui pourrait facilement nécessiter plus que les 15,4 To utilisés pour l'approche Rowstore.