Mam dwa stoliki, w których przechowuję:
- zakres adresów IP - tabela przeglądowa kraju
- lista żądań pochodzących z różnych adresów IP
Adresy IP były przechowywane jako bigint
s, aby poprawić wydajność wyszukiwania.
Oto struktura tabeli:
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
)
Chcę uzyskać podział żądań według kraju, dlatego wykonuję następujące zapytanie:
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
Mam wiele rekordów w tabelach: około 200 000 w IP2Country
i kilka milionów w Request
, więc zapytanie zajmuje trochę czasu.
Patrząc na plan wykonania, najdroższą częścią jest Wyszukiwanie klastrowane na indeksie PK_IP2Country, które jest wykonywane wielokrotnie (liczba wierszy w żądaniu).
Poza tym czuję się trochę dziwnie w tej left join ip2country ic on r.IP between ic.begin_num and ic.end_num
części (nie wiem, czy jest lepszy sposób na przeprowadzenie wyszukiwania).
Struktura tabeli, niektóre przykładowe dane i zapytanie są dostępne w SQLFiddle: http://www.sqlfiddle.com/#!3/a463e/3 (niestety nie sądzę, żebym mógł wstawić wiele rekordów w celu odtworzenia problemu, ale to mam nadzieję, że daje pomysł).
Nie jestem (oczywiście) ekspertem od wydajności / optymalizacji SQL, więc moje pytanie brzmi: czy są jakieś oczywiste sposoby na ulepszenie tej struktury / zapytania pod względem wydajności, których mi brakuje?
źródło
begin_num
. Muszę też dołączaćA BETWEEN B AND C
dość często i jestem ciekawy, czy istnieje sposób na osiągnięcie tego bez żmudnych połączeń RBAR.begin_ip
iend_ip
utrwalenie kolumn obliczeniowych, aby zapobiec możliwości synchronizacji tekstu i liczb.ip2country (begin_num, end_num)
?give me the first record that has a begin_num < ip in asc order of begin_num
(popraw mnie, jeśli się mylę) może być poprawny i poprawić wydajność.begin_num
, a następnie skanuje wend_num
obrębie tego zestawu i znajduje tylko jeden rekord.Odpowiedzi:
Potrzebujesz dodatkowego indeksu. W twoim przykładzie Fiddle dodałem:
CREATE UNIQUE INDEX ix_IP ON Request(CategoryID, IP)
Który obejmuje tabelę żądań i pobiera wyszukiwanie indeksu zamiast skanowania indeksu klastrowego.
Zobacz, jak to poprawia, i daj mi znać. Zgaduję, że to trochę pomoże, ponieważ skanowanie tego indeksu nie jest na pewno tanie.
źródło
Zawsze istnieje podejście oparte na brutalnej sile: możesz eksplodować swoją mapę IP. Dołącz tabelę liczb do istniejącej mapy, aby utworzyć jeden rekord dla każdego adresu IP. To tylko 267 tys. Rekordów opartych na twoich danych Fiddle, bez problemu.
Dzięki temu poszukiwania będą prostsze i, miejmy nadzieję, szybsze. Ma to sens tylko wtedy, gdy dokonasz stosunkowo niewielu aktualizacji
ip2country
.Mam nadzieję, że ktoś inny ma lepsze rozwiązanie!
źródło
Spróbuj tego:
źródło