Kết hợp chỉ số toàn văn và vô hướng


8

Giả sử chúng ta có một cơ sở dữ liệu gồm 12 triệu tên và địa chỉ cần tìm kiếm bằng toàn văn bản, nhưng mỗi hàng cũng chứa một giá trị nguyên COMPANYID. Bảng này chứa khoảng 250 CÔNG TY riêng biệt trên 12 triệu hàng đó.

Có thể, khi xác định các chỉ mục toàn văn bản, để cung cấp cho mỗi COMPANY"nhánh" riêng của nó trong cây?


Bạn đang thấy vấn đề hiệu suất bây giờ? Ví dụ: nếu có một chỉ mục trên CompanyID và bạn lọc theo đó, bạn có thấy hiệu suất toàn văn bản giống như không có bộ lọc trên cột đó không? Tôi không có nhiều kinh nghiệm với tôi, tôi hy vọng SQL Server đủ thông minh để thu hẹp không gian tìm kiếm toàn văn bản thành các hàng khớp với bộ lọc ít tốn kém trước tiên.
Aaron Bertrand

Trên thực tế, tôi mới viết ứng dụng cho một người companycho đến nay và mọi người thích nó rất nhiều, họ muốn tôi đưa nó vào sản xuất cho tất cả các công ty và tôi chưa có cơ hội tạo ra một bản mô phỏng với 12 triệu hàng dữ liệu giả có ý nghĩa chưa. Các giá trị như "Lastname1", "Lastname2", "City1", v.v. sẽ không có đủ biến thể và có thể làm sai lệch kết quả kiểm tra. Dữ liệu thay đổi thường xuyên đến nỗi tôi không chắc chắn SQL Server sẽ biết chỉ số nào là hẹp hơn trong bất kỳ truy vấn cụ thể nào và số lượng hàng trên mỗi công ty thay đổi rất nhiều. Một công ty có thể chỉ có 1000 hàng, 60.000 khác.
Tim

Không ai ở đây sẽ có thể suy đoán, với mức độ chi tiết ở đây, SQL Server sẽ xử lý tình huống này tốt như thế nào. Bạn sẽ phải xây dựng thực tế, dữ liệu có ý nghĩa thử nghiệm và kiểm tra chạy của bạn khối lượng công việc trên của bạn phần cứng ...
Aaron Bertrand

Nhưng tôi vẫn muốn có một câu trả lời cho câu hỏi của tôi. Tôi không yêu cầu ai suy đoán.
Tim

Câu trả lời:


3

Không là câu trả lời ngắn và bạn không thực sự cần điều này. Các chỉ mục toàn văn bản là các chỉ mục đảo ngược để chúng lưu trữ các từ được phân tách bằng doc_id duy nhất mà bạn phải chỉ định khi bạn tạo chỉ mục toàn văn bản. Đây phải là một "cột duy nhất, một khóa, không thể rỗng" lý tưởng là một số nguyên. Những gì về cơ bản là một khóa ngoại không hình và không có cách dễ dàng để phân vùng chúng trên cơ sở đó.

Bạn có thể giả mạo một cái gì đó như thế này với một bảng cho mỗi công ty và chỉ mục toàn văn cho mỗi bảng. Bạn sẽ cần một số loại logic mã nằm ở phía trước để xác định bảng nào sẽ chèn vào / tìm nạp. Đây sẽ là một vấn đề đau đầu đáng kể để quản lý gần như chắc chắn không đáng.

Nếu bạn có khối lượng nghiêm trọng (ví dụ như hơn 23 tỷ bản ghi) thì bạn có thể xem xét một giải pháp ngăn chặn, ví dụ như một máy ảo Azure cho mỗi công ty có ứng dụng nằm trước chúng để xác định máy nào sẽ kết nối. Nhưng rõ ràng bạn cũng không cần điều đó.

Cũng có một số cải tiến trong SQL 2008 thành toàn văn bản hiện được tích hợp nhiều hơn vào công cụ cơ sở dữ liệu. Một kịch bản, trong đó bạn chỉ định mệnh đề WHERE đối với cột bình thường và sử dụng các hàm toàn văn bản, được gọi là 'Truy vấn hỗn hợp' và được thảo luận tại đây . Đây vẫn là một bài viết tuyệt vời mặc dù thông tin dành cho SQL 2008.

Nếu bạn thường quan tâm đến hiệu suất và kế hoạch, tại sao không quay một số dữ liệu thử nghiệm, giới thiệu một số sai lệch và thử. Tôi đã gõ kịch bản này với ~ 2 triệu hàng trong vài phút:

!!TODO introduce some skew
USE master
GO

SET NOCOUNT ON
GO

DBCC TRACEON(610)   -- Minimal logging
GO

GO

IF EXISTS ( SELECT * FROM sys.databases WHERE name = 'fullTextDemo' )
BEGIN
    ALTER DATABASE fullTextDemo SET SINGLE_USER WITH ROLLBACK IMMEDIATE
    DROP DATABASE fullTextDemo
END
GO

IF NOT EXISTS ( SELECT * FROM sys.databases WHERE name = 'fullTextDemo' )
CREATE DATABASE fullTextDemo
GO

ALTER DATABASE fullTextDemo SET RECOVERY SIMPLE
GO

USE fullTextDemo
GO

IF OBJECT_ID('dbo.yourAddresses') IS NOT NULL DROP TABLE dbo.yourAddresses
IF OBJECT_ID('dbo.companies') IS NOT NULL DROP TABLE dbo.companies
GO

CREATE TABLE dbo.companies (
    companyId       INT IDENTITY NOT NULL,
    companyName     NVARCHAR(50) NOT NULL,

    CONSTRAINT PK_companies PRIMARY KEY ( companyId )
)
GO

CREATE TABLE dbo.yourAddresses (
    rowId           INT IDENTITY,
    companyId       INT NOT NULL FOREIGN KEY REFERENCES dbo.companies ( companyId ),
    searchTerms     NVARCHAR(2048) NOT NULL

    CONSTRAINT PK_yourAddresses PRIMARY KEY ( rowId )
)
GO

-- Populate the companies
;WITH cte AS (
SELECT TOP 250 ROW_NUMBER() OVER ( ORDER BY ( SELECT 1 ) ) rn
FROM master.sys.columns c1
    CROSS JOIN master.sys.columns c2
    CROSS JOIN master.sys.columns c3
)
INSERT INTO dbo.companies ( companyName )
SELECT NEWID()
FROM cte
GO

-- Generate 2,636,000 records
INSERT dbo.yourAddresses ( companyId, searchTerms )
SELECT c.companyId, m.[text]
FROM dbo.companies c
    CROSS JOIN ( SELECT * FROM sys.messages ) m
WHERE m.language_id = 1033
AND m.[text] Like '[a-z]%'
GO

CREATE INDEX _idx ON dbo.yourAddresses ( companyId ) INCLUDE ( searchTerms )
GO

-- !!TODO look at compression
--ALTER INDEX PK_yourAddresses ON dbo.yourAddresses REBUILD WITH ( DATA_COMPRESSION = PAGE )
--GO

-- Create the catalog
IF NOT EXISTS ( SELECT * FROM sys.fulltext_catalogs WHERE name = N'ftc_yourAddresses' )
CREATE FULLTEXT CATALOG ftc_yourAddresses
GO

-- Create the full-text index
CREATE FULLTEXT INDEX ON dbo.yourAddresses ( searchTerms ) KEY INDEX PK_yourAddresses ON ftc_yourAddresses WITH CHANGE_TRACKING MANUAL  -- CHANGE_TRACKING OFF, NO POPULATION
GO

SELECT 'before' ft, * FROM sys.fulltext_indexes
GO

ALTER FULLTEXT INDEX ON dbo.yourAddresses START FULL POPULATION;
GO


DECLARE @i INT 
SET @i = 0

WHILE EXISTS ( SELECT * FROM sys.fulltext_indexes WHERE has_crawl_completed = 0 )
BEGIN

        SELECT outstanding_batch_count, *
        FROM sys.dm_fts_index_population
        WHERE database_id = DB_ID()

        --SELECT *
        --FROM sys.dm_fts_outstanding_batches
        --WHERE database_id = DB_ID()

    WAITFOR DELAY '00:00:05'

    SET @i = @i + 1
    IF @i > 60 BEGIN RAISERROR( 'Too many loops!', 16, 1 ) BREAK END

END

SELECT 'after' ft, * FROM sys.fulltext_indexes
GO



SELECT TOP 1000 *
FROM dbo.yourAddresses ft
WHERE companyId = 42
 AND CONTAINS ( searchTerms, 'data' )
GO

SELECT TOP 1000 *
FROM dbo.yourAddresses a
    INNER JOIN CONTAINSTABLE ( dbo.yourAddresses, searchTerms, 'data' ) ct ON a.rowId = ct.[key]
WHERE a.companyId = 42
GO

SELECT TOP 1000 *
FROM dbo.yourAddresses a
    INNER JOIN CONTAINSTABLE ( dbo.yourAddresses, searchTerms, 'data' ) ct ON a.rowId = ct.[key]
WHERE a.companyId = 42
OPTION ( MERGE JOIN )
GO

SELECT TOP 100 *
FROM sys.dm_fts_index_keywords (DB_ID(), OBJECT_ID('dbo.yourAddresses') )

SELECT TOP 100 *
FROM sys.dm_fts_index_keywords_by_document(DB_ID(), OBJECT_ID('dbo.yourAddresses') )
ORDER BY document_id
GO

Cảm ơn rất nhiều vì đã dành thời gian để viết kịch bản đó, cho liên kết đến bài viết truy vấn "hỗn hợp" và cho hàng tỷ so với hàng triệu phối cảnh :-)
Tim

1
Theo bài báo, một giải pháp cho vấn đề tiềm ẩn đã được giới thiệu trong SQL Server 2008
Tim

Vui vì nó hữu ích. Tôi có lẽ nên nói rằng chỉ số bao phủ và gợi ý truy vấn trong tập lệnh của tôi chỉ là các thử nghiệm không phải là đề xuất, đây là những tùy chọn bạn có thể có nếu bạn gặp vấn đề về hiệu suất. Chỉ số có khả năng hơi rộng và các cảnh báo thông thường với các gợi ý được áp dụng.
wBob
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.