Cách hiệu quả nhất về chi phí để trang thông qua một bảng được sắp xếp kém?


7

Tôi có một bảng có ba cột: HashUID1, HashUID2, Địa chỉ_Name (là một địa chỉ email văn bản và hai cột băm trước đó là một số sáng tạo điên rồ để liên kết các bảng người tham gia sự kiện với địa chỉ email. kiểm soát của tôi. Tập trung vào chỉ mục address_name)

Nó có 78 triệu hàng. Không được sắp xếp đúng. Bất kể, chỉ số này được chia thành rất nhiều LUN nhanh và thực hiện tìm kiếm chỉ số nhanh THỰC SỰ.

Tôi cần tạo một loạt các truy vấn để chỉ trích xuất 20.000 "hàng trên mỗi trang", nhưng tránh xung đột hoặc lừa đảo. Vì không có cột nhận dạng, hoặc cột dễ dàng đặt hàng, có cách nào dễ dàng để chọn tất cả và trang thông qua nó không?

Tôi có đúng không khi nói rằng nếu tôi thực hiện một lựa chọn * từ hugetablewithemails vào một bảng tạm thời, thì hãy chọn qua nó bởi row_number rằng bảng vẫn còn trong bộ nhớ trong suốt thời gian giao dịch, mà theo tôi, là một lượng tài nguyên bộ nhớ quá mức ? Đây có vẻ là phương pháp phân trang ưa thích. Tôi muốn trang theo tỷ lệ phần trăm thống kê. :

Có một chỉ mục duy trì địa chỉ email address_name theo thứ tự và được duy trì tốt. Trong tuần qua tôi đã có ý định giúp đỡ nhà phát triển khác này bằng cách dành một chút thời gian để xem xét việc xây dựng một Proc phát ra các phạm vi dựa trên các chức năng cửa sổ dựa trên số liệu thống kê (mà tôi không giỏi lắm, nhưng truy vấn này thực sự khiến tôi quan tâm) cung cấp một phạm vi các ký tự từ 1 đến (biến) TRÁI các ký tự của chỉ mục, đáp ứng 20.000 hàng - Nhưng tôi chưa có thời gian để bắt đầu truy vấn ...

Câu hỏi cặp đôi:

  1. Bất kỳ đề xuất? Không tìm kiếm mã thực tế, chỉ cần một số gợi ý hoặc gợi ý dựa trên kinh nghiệm, có thể hãy cẩn thận. Tôi muốn tránh quét chỉ mục bổ sung sau lần quét đầu tiên.

  2. Đây có phải là cách tiếp cận đúng?

  3. Tôi đang nghĩ đến việc phá vỡ tổng chỉ số của tất cả các địa chỉ email, thu thập số lượng hàng (*), / 20.000 và sử dụng chức năng cửa sổ này để nhóm các giá trị chuỗi con tối thiểu / tối đa (1,5) dựa trên tỷ lệ phần trăm của tổng số hàng để xây dựng phạm vi nhóm. Suy nghĩ?

Đây là một quy trình ETL không thể sửa đổi cơ sở dữ liệu nguồn.

Tôi hy vọng với một lần quét chỉ mục đầy đủ tôi có thể thực hiện:

  • Truy vấn để có được một biểu đồ dựa trên việc sử dụng chỉ mục (được sắp xếp theo thứ tự abc) và tách nó ra (cửa sổ) bằng cách sử dụng min / max để tạo một số phạm vi như thế này, để dễ dàng tìm kiếm chỉ mục cần thiết:

  • A-> AAAX, (ví dụ 20k hàng) AAA-Z, B-> (20k khác), B-> BAAR -> BAAR-> CDEFG -> CDEFH> FAAH, v.v.

Chúng tôi chạy đọc cam kết trong các cơ sở dữ liệu cho quá trình ETL này. Chúng tôi chỉ cố gắng đưa ra hàng loạt số điểm 20 nghìn vì DBA nói rằng chúng tôi đang sử dụng quá nhiều tài nguyên mạng bằng cách lấy các bảng đầy đủ. Nếu dữ liệu đã thay đổi (đó là một mối quan tâm), chúng tôi sẽ cập nhật bảng DW và bảng phân tầng của mình một cách nhanh chóng.

Tôi rất thích sử dụng các bảng tạm thời, nhưng nếu tôi đã làm như vậy, tôi đã tràn vào tempdb và nhận được thông báo qua e-mail từ các DBA liên quan đến nó và cơ sở dữ liệu quá lớn.

Câu trả lời:


9

Về cơ bản, bạn đang hỏi liệu bạn có thể thực hiện một lần quét theo thứ tự thông qua dữ liệu tổng thể hay không, trong khi không tạo ra các bản sao của dữ liệu và trả về các tập hợp phân tách 'x' từ toàn bộ trên mỗi cuộc gọi. Đây chính xác là hành vi của một con trỏ API được cấu hình phù hợp.

Ví dụ: sử dụng bảng AdventureWorks Person.EmailAddressđể trả về bộ 1.000 hàng:

DECLARE 
    @cur integer,
    -- FAST_FORWARD | AUTO_FETCH | AUTO_CLOSE
    @scrollopt integer = 16 | 8192 | 16384,
    -- READ_ONLY, CHECK_ACCEPTED_OPTS, READ_ONLY_ACCEPTABLE
    @ccopt integer = 1 | 32768 | 65536, 
    @rowcount integer = 1000,
    @rc integer;

-- Open the cursor and return the first 1,000 rows
EXECUTE @rc = sys.sp_cursoropen
    @cur OUTPUT,
    N'
    SELECT *
    FROM AdventureWorks2012.Person.EmailAddress
        WITH (INDEX([IX_EmailAddress_EmailAddress]))
    ORDER BY EmailAddress;
    ',
    @scrollopt OUTPUT,
    @ccopt OUTPUT,
    @rowcount OUTPUT;

IF @rc <> 16 -- FastForward cursor automatically closed
BEGIN
    -- Name the cursor so we can use CURSOR_STATUS
    EXECUTE sys.sp_cursoroption
        @cur, 
        2, 
        'MyCursorName';

    -- Until the cursor auto-closes
    WHILE CURSOR_STATUS('global', 'MyCursorName') = 1
    BEGIN
        EXECUTE sys.sp_cursorfetch
            @cur,
            2,
            0,
            1000;
    END;
END;

Mỗi thao tác tìm nạp trả về tối đa 1.000 hàng, ghi nhớ vị trí quét từ cuộc gọi trước.


2

Nếu không biết mục đích đằng sau cửa sổ, sẽ rất khó để được cụ thể. Xem xét bạn đang xem hai mươi nghìn hàng cùng một lúc, tôi đoán đây là một quy trình hàng loạt và không dành cho người xem.

Nếu có một chỉ mục trên địa chỉ email thì nó được sắp xếp. Các chỉ số là BTrees và họ duy trì một trật tự nội bộ. Đây sẽ là thứ tự sắp xếp đối chiếu của cột đó (có khả năng, nhưng không nhất thiết là thuộc địa mặc định của cơ sở dữ liệu).

Các bảng tạm thời - cả #table và @table - sẽ có sự hiện diện trong tempdb. Ngoài ra các kết quả lớn sẽ tràn ra khỏi bộ nhớ đến tempdb.

Nếu theo "thống kê", bạn có nghĩa là số liệu thống kê nội bộ của SQL Server, nó duy trì trên các chỉ mục hoặc thông qua create statistics..tuyên bố thì tôi không nghĩ rằng nó sẽ bay. Những thống kê đó chỉ có vài trăm thùng (đã quên giới hạn chính xác ngay bây giờ) khi bạn sẽ cần 39.000 "cửa sổ" để đọc đọc toàn bộ bảng của mình. Nếu bạn có ý định duy trì ánh xạ hàng-cửa sổ của riêng mình thông qua các kích hoạt, điều này có thể đạt được nhưng chi phí có thể là đáng kể.

Cách truyền thống để trang thông qua một tập dữ liệu lớn là ghi nhớ giá trị khóa lớn nhất từ ​​mỗi nhóm và đọc từ đó trở đi. Nếu cột địa chỉ email không phải là duy nhất, tức là một địa chỉ có thể xảy ra nhiều lần khi bạn có một vài tùy chọn. A) xử lý từng hàng từng đợt trong ứng dụng và bỏ qua các bản sao hoặc b) lọc chúng ra trong SQL. "B" sẽ yêu cầu sắp xếp nhưng nếu dữ liệu được đọc theo trình tự khóa thì loại này có thể được tối ưu hóa đi:

declare @MaxKey varchar(255) = '';  -- email size

while exists (select 1 from mytable where address_name > @MyKey)
begin
    ;with NewBatch as
    (
    select top 20000  -- whatever size a "window" must be
        address_name
    from mytable
    where address_name > @MaxKey
    order by address_name
    )
    select distinct
        address_name
    from NewBatch;

    --process and then
    select @MaxKey = max(address_name) -- from this batch of rows
end

Việc lặp lại có thể xảy ra trong SQL hoặc applicaiton của bạn, tùy thuộc vào kiến ​​trúc của bạn.

Nếu nhiều cột được yêu cầu, ngoài địa chỉ email, bạn có thể xem xét một con trỏ với từ khóa KEYSET hoặc STATIC được xác định. Điều này vẫn sẽ sử dụng tài nguyên trong tempdb, tuy nhiên.

Lùi một bước lùi, SSIS được thiết kế đặc biệt để xử lý các hàng lớn một cách hiệu quả. Xác định một gói đáp ứng yêu cầu của bạn có thể là câu trả lời dài hạn tốt nhất.


1

Nếu bạn chỉ quan tâm đến sự ổn định thứ tự theo thời gian với sự hiện diện của DML, hãy cân nhắc sử dụng Snapshot Isolation để truy vấn bảng. Bạn có thể để một SNAPSHOTgiao dịch mở cho đến khi bạn hoàn thành việc trích xuất các trang. Điều này có những nhược điểm thông thường liên quan đến Snapshot Isolation.

Điều đó nói rằng kỹ thuật này sẽ yêu cầu sắp xếp toàn bộ bảng cho mỗi trang bạn trích xuất. Điều đó thực sự tốn kém (hiệu suất tiệm cận bậc hai).

Cân nhắc sử dụng bảng tạm thời với IDENTITYkhóa chính. Bằng cách đó bạn có thể dễ dàng trích xuất các trang thông qua tìm kiếm phạm vi.

Bảng tạm thời không được ghim vào bộ nhớ. Đây là một quan niệm sai lầm phổ biến.

Với 78m hàng (mỗi 100 byte => 7,8 GB dung lượng đĩa), kỹ thuật này sẽ hoạt động tốt.

Lưu ý rằng, trích xuất dữ liệu từ bảng gốc bằng cách sử dụng, ví dụ, READ COMMITTEDcó thể cung cấp cho bạn một bộ dữ liệu chưa từng tồn tại tại bất kỳ thời điểm nào (do DML đồng thời). Sử dụng SNAPSHOTcách ly nếu bạn có thể.

Bạn có thể tạo bảng tạm thời trong cơ sở dữ liệu của riêng bạn hoặc ở chế độ SIMPLE riêng, không được sao lưu cơ sở dữ liệu. Cũng lưu ý rằng việc sắp xếp toàn bộ bảng sẽ tạm thời sử dụng nhiều dung lượng tempdb khi lưu trữ tất cả các cột bạn cần. Vì vậy, có thể bạn cần lấy các số hàng từ chỉ mục (duy nhất) đã có (và áp dụng thủ thuật giảm kích thước).


Một ý tưởng khác: Thay vì đệm tất cả các hàng vào bảng tạm thời, chỉ viết một khóa của mỗi hàng. Bạn chỉ ra rằng tìm kiếm trong bảng chính của họ sẽ được nhanh chóng.

Hoặc, bạn chỉ viết mỗi hàng thứ 20.000 để bạn biết nơi bắt đầu mỗi truy vấn phân trang. Trích xuất một trang sau đó sẽ không hoạt động theo số hàng mà với SELECT TOP 20000 ... WHERE SomeKey >= PageStartKey ORDER BY SomeKey.

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.