Cách nhanh nhất để tách / lưu trữ một chuỗi dài cho hàm charindex


8

Tôi có một chuỗi các chữ số 1 TB. Đưa ra một chuỗi gồm 12 ký tự, tôi muốn lấy vị trí bắt đầu của chuỗi này trong chuỗi gốc ( charindexhàm).

Tôi đã thử nghiệm điều này với chuỗi 1GB và chuỗi con 9 chữ số bằng SQL Server, lưu trữ chuỗi dưới dạng a varchar(max). Charindexmất 10 giây. Phá vỡ chuỗi 1GB trong các khối chồng chéo 900 byte và tạo một bảng (StartPocationOfChunk, Chunkof chuỗi) với chuỗi khối trong đối chiếu nhị phân, được lập chỉ mục trong vòng 1 giây. Phương pháp Latter cho 10GB, chuỗi con 10 chữ số tăng charindex lên 1,5 phút. Tôi muốn tìm một phương pháp lưu trữ nhanh hơn.

Thí dụ

chuỗi chữ số: 0123456789 - chuỗi con để tìm kiếm 345
charindex ('345', '0123456789') cho 4

Phương pháp 1 : Bây giờ tôi có thể lưu trữ tệp này trong bảng SQL Server có thể phân tầng gồm một cột colstrvà thực hiện:

select charindex('345',colstr) from strtable

Phương pháp 2 : hoặc tôi có thể tạo một bảng strtable2 (pos, colstr1) bằng cách tách chuỗi gốc: 1; 012 | 2; 123 | 3; 234 aso và sau đó chúng ta có thể có truy vấn

select pos from strtable2 where colstr1='345'

Phương pháp 3 : Tôi có thể tạo một bảng strtable2 (pos2, colstr2) bằng cách tách chuỗi gốc thành các phần lớn hơn 1; 01234 | 4; 34567 | 7; 6789 và sau đó

select pos2+charindex('345',colstr2) from strtable2 where colstr2 like '%345%'

Phương pháp đầu tiên là chậm nhất.

Phương pháp thứ hai làm tăng kích thước lưu trữ cơ sở dữ liệu!

Phương pháp 3 : Đặt độ dài colstr2 thành 900 byte trong đối chiếu nhị phân, tạo chỉ mục trên cột này mất 1 giây cho chuỗi 1GB và tìm kiếm chuỗi con 9 chữ số. Đối với chuỗi 10GB và chuỗi con 10 chữ số ist mất 90 giây.

Bất kỳ ý tưởng nào khác làm thế nào để thực hiện việc này nhanh hơn (có thể bằng cách sử dụng chuỗi bao gồm Chữ số, với số nguyên dài, ....)?

Tìm kiếm luôn có một chuỗi con 12 chữ số trong chuỗi 1TB chữ số SQL Server 2017 Developer Edition, 16 lõi, RAM 16GB. Mục tiêu chính là tốc độ tìm kiếm! 10 chữ số trong chuỗi 10 GB (để kiểm tra hiệu suất).

Câu trả lời:


6

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:

  1. 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.
  2. 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.
  3. 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 đủ.
  4. 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.


6

Lưu trữ và xử lý 1TB dữ liệu chỉ với 16GB RAM khả dụng có thể là một thách thức. 1GB mỗi lõi khá mất cân đối, đặc biệt đối với loại khối lượng công việc này. 8GB mỗi lõi sẽ là điểm khởi đầu tốt hơn nhiều, với mong muốn hơn.

Điều đó nói rằng, tôi vẫn sẽ bị cám dỗ để thử một biến thể của phương pháp 2:

Lưu trữ tất cả các chuỗi con 12 ký tự có thể như biginttrong bảng cột lưu trữ cụm (với nén lưu trữ nếu điều đó trở nên hữu ích):

CREATE TABLE dbo.Search
(
    pos bigint NOT NULL,
    fragment bigint NOT NULL,

    INDEX CCS CLUSTERED COLUMNSTORE 
        WITH (DATA_COMPRESSION = COLUMNSTORE_ARCHIVE) -- optional
);

Bạn có thể sẽ phải thực hiện một số cách tải dữ liệu nguồn vào bảng này. Hãy chắc chắn rằng bạn kết thúc với các nhóm hàng có kích thước tối đa (1.048.576 hàng) trong cấu trúc cột lưu trữ đã hoàn thành . Xem hướng dẫn tải dữ liệu .

Bạn có thể tạo các hàng trong bội số của 1048576 trong một bảng lưu trữ hàng không được lập trình trước khi tạo một chỉ mục cột được phân cụm trên đó, sau đó chuyển trực tiếp kết quả vào một bảng chính được phân đoạn. Cách tiếp cận chính xác phụ thuộc vào cách bạn định tải dữ liệu, liệu nó sẽ được thêm vào và mức độ quen thuộc của bạn với SQL Server nói chung.

Hiệu suất rất tốt là có thể với phương pháp này, nhưng như thường thấy với cột lưu trữ, bạn sẽ cần phải đạt được phân vùng và phân đoạn hiệu quả. Phân vùng trên fragmentcột và xây dựng chỉ mục kho lưu trữ trong khi thay thế một chỉ mục cụm hàng được khóa fragmentlà cách để đạt được điều này, như đã lưu ý trong tài liệu được liên kết ở trên. Điều này cũng sẽ giảm thiểu nhu cầu lưu trữ, vì fragmentcác giá trị trong cùng phạm vi sẽ được lưu trữ trong cùng phân khúc. Điều này cho phép rebasing giá trị hiệu quả và đóng gói bit.

Trong khi tải, hãy cố gắng giới hạn các chuỗi bạn đang làm việc trong SQL Server ở các loại không LOB (tối đa). Nếu bạn thấy làm việc với các LOB tốt nhất cho thông lượng, thường có một điểm ngọt ngào về độ dài dữ liệu được tìm thấy, trên đó hiệu suất giảm đáng kể.

Tùy thuộc vào kích thước cuối cùng của cấu trúc và tốc độ của hệ thống con I / O của bạn, bạn có thể thấy rằng phương pháp này cung cấp hiệu suất đủ tốt. Tăng bộ nhớ có sẵn sẽ cải thiện mọi thứ rõ rệt.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.