Hiệu suất TSQL - THAM GIA giá trị GIỮA tối thiểu và tối đa


10

Tôi có hai bảng trong đó tôi lưu trữ:

  • một dải IP - bảng tra cứu quốc gia
  • một danh sách các yêu cầu đến từ các IP khác nhau

Các IP được lưu trữ dưới dạng bigints để cải thiện hiệu suất tra cứu.

Đây là cấu trúc bảng:

create table [dbo].[ip2country](
    [begin_ip] [varchar](15) NOT NULL,
    [end_ip] [varchar](15) NOT NULL,
    [begin_num] [bigint] NOT NULL,
    [end_num] [bigint] NOT NULL,
    [IDCountry] [int] NULL,
    constraint [PK_ip2country] PRIMARY KEY CLUSTERED 
    (
        [begin_num] ASC,
        [end_num] ASC
    )
)

create table Request(
    Id int identity primary key, 
    [Date] datetime, 
    IP bigint, 
    CategoryId int
)

Tôi muốn nhận phân tích yêu cầu cho mỗi quốc gia, vì vậy tôi thực hiện truy vấn sau:

select 
    ic.IDCountry,
    count(r.Id) as CountryCount
from Request r
left join ip2country ic 
  on r.IP between ic.begin_num and ic.end_num
where r.CategoryId = 1
group by ic.IDCountry

Tôi có rất nhiều bản ghi trong các bảng: khoảng 200.000 trong IP2Countryvà một vài triệu trong Request, vì vậy truy vấn mất một lúc.

Nhìn vào kế hoạch thực hiện, phần đắt nhất là Tìm kiếm chỉ mục cụm trên chỉ mục PK_IP2Country, được thực hiện nhiều lần (số lượng hàng trong Yêu cầu).

Ngoài ra, một phần mà tôi cảm thấy hơi lạ là left join ip2country ic on r.IP between ic.begin_num and ic.end_numphần (không biết có cách nào tốt hơn để thực hiện tra cứu không).

Cấu trúc bảng, một số dữ liệu mẫu và truy vấn có sẵn trong SQLFiddle: http://www.sqlfiddle.com/#!3/a463e/3 (tiếc là tôi không nghĩ rằng tôi có thể chèn nhiều bản ghi để tái tạo vấn đề, nhưng điều này hy vọng đưa ra một ý tưởng).

Tôi (rõ ràng) không phải là một chuyên gia về hiệu suất / tối ưu hóa SQL, vì vậy câu hỏi của tôi là: Có cách nào rõ ràng để cấu trúc / truy vấn này có thể được cải thiện hiệu năng khôn ngoan mà tôi đang thiếu không?


2
Bản đồ địa chỉ IP có thể đến nhiều quốc gia không? Nếu không, bạn có thể thu hẹp PK của bạn xuống begin_num. Tôi cũng phải tham gia A BETWEEN B AND Ckhá thường xuyên và tôi tò mò muốn biết liệu có cách nào để đạt được điều này mà không cần tham gia RBAR tẻ nhạt không.
Jon của tất cả các giao dịch

1
Đó là một chủ đề nhỏ đối với câu hỏi của bạn, nhưng tôi sẽ xem xét việc tạo begin_ipend_ipduy trì các cột được tính toán, để ngăn khả năng văn bản và các số không đồng bộ bằng cách nào đó.
Jon của tất cả các giao dịch

@ w0lf: có phạm vi chồng chéo trong ip2country (begin_num, end_num)?
ypercubeᵀᴹ

@Jonof ALLTrades thông thường một IP nên thuộc về một quốc gia, vì vậy tôi nghĩ rằng ý tưởng của bạn về một truy vấn như give me the first record that has a begin_num < ip in asc order of begin_num(sửa tôi nếu tôi sai) có thể hợp lệ và cải thiện hiệu suất.
Cristian Lupascu

1
@ w0lf: Ấn tượng của tôi là về cơ bản đó là những gì máy chủ đang làm trong trường hợp như thế này, vì trước tiên nó quét begin_num, sau đó quét end_numtrong bộ đó và chỉ tìm thấy một bản ghi.
Jon của tất cả các giao dịch

Câu trả lời:


3

Bạn cần một chỉ số bổ sung. Trong ví dụ Fiddle của bạn, tôi đã thêm:

CREATE UNIQUE INDEX ix_IP ON Request(CategoryID, IP)

Nó bao gồm bạn cho bảng yêu cầu và được tìm kiếm chỉ mục thay vì quét chỉ mục cụm.

Xem làm thế nào cải thiện nó và cho tôi biết. Tôi đoán nó sẽ giúp ích khá nhiều vì việc quét chỉ số đó chắc chắn tôi không rẻ.


Tôi không biết tại sao, nhưng kết quả dường như khác (trong SQLFiddle)
Cristian Lupascu

@ w0lf: chúng khác nhau (probbaly) vì cả hai bạn đều chèn dữ liệu ngẫu nhiên vào các bảng.
ypercubeᵀᴹ

@ypercube chắc chắn đó là nguyên nhân. Gần đây tôi đã làm rất nhiều việc mà tôi quên rằng dữ liệu là ngẫu nhiên. Lấy làm tiếc.
Cristian Lupascu

2

Luôn có cách tiếp cận mạnh mẽ: bạn có thể làm nổ bản đồ IP của mình. Tham gia bảng số so với bản đồ hiện tại của bạn để tạo một bản ghi cho mỗi địa chỉ IP. Đó chỉ là bản ghi 267K dựa trên dữ liệu Fiddle của bạn, không có vấn đề gì cả.

CREATE TABLE IPLookup
  (
  IP  BIGINT PRIMARY KEY,
  CountryID  INT
  )
INSERT INTO IPLookup (IP, CountryID)
  SELECT
    N.Number, Existing.IDCountry
  FROM
    ip2country AS Existing
    INNER JOIN Numbers AS N ON N.Number BETWEEN Existing.begin_num AND Existing.end_num

Điều này sẽ làm cho việc tìm kiếm đơn giản hơn, và hy vọng nhanh hơn. Điều này chỉ có ý nghĩa nếu bạn thực hiện tương đối ít cập nhật trên ip2country, tất nhiên.

Tôi hy vọng người khác có một giải pháp tốt hơn!


Toàn bộ tập dữ liệu sẽ tạo ra hơn 5 tỷ bản ghi, vì vậy tôi không nghĩ mình sẽ làm được. Nhưng đây vẫn là một ý tưởng hay; Tôi chắc chắn rằng nó khả thi trong nhiều trường hợp tương tự. +1
Cristian Lupascu

0

Thử đi:

SELECT ic.IDCountry,
        COUNT(r.Id) AS CountryCount
FROM Request r
INNER JOIN (SELECT begin_num+NUMS.N [IP], IDCountry 
            FROM ip2country
            CROSS JOIN (SELECT TOP(SELECT ABS(MAX(end_num-begin_num)) FROM ip2country) ROW_NUMBER() OVER(ORDER BY sc.name)-1 [N]
                        FROM sys.columns sc) NUMS
            WHERE begin_num+NUMS.N <= end_num) ic
ON r.IP = ic.IP
WHERE r.CategoryId = 1
GROUP BY ic.IDCountry

cảm ơn, tôi đã thử cách tiếp cận của bạn, nhưng có vẻ đắt hơn truy vấn ban đầu
Cristian Lupascu

Bạn có bao nhiêu hàng trong mỗi bảng? Tôi muốn tái tạo thang đo vấn đề của bạn trên DB của tôi và cố gắng giải quyết mà không cần thêm chỉ mục :)
Vince Pergolizzi

khoảng 200.000 trong IP2Country và một vài triệu (có thể hàng chục triệu trong tương lai gần) trong Yêu cầu. Tôi nghĩ rằng nếu bạn giải quyết nó mà không có chỉ số, bạn xứng đáng với danh hiệu "DBA của năm" :)
Cristian Lupascu
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.