Eu tenho duas tabelas nas quais guardo:
- um intervalo de IP - tabela de pesquisa de país
- uma lista de solicitações provenientes de IPs diferentes
Os IPs foram armazenados como bigint
s para melhorar o desempenho da pesquisa.
Esta é a estrutura da tabela:
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
)
Quero obter o detalhamento da solicitação por país e, portanto, faço a seguinte consulta:
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
Eu tenho muitos registros nas tabelas: cerca de 200.000 IP2Country
e alguns milhões Request
, portanto a consulta demora um pouco.
Observando o plano de execução, a parte mais cara é uma Busca de Índice em Cluster no índice PK_IP2Country, que é executado várias vezes (o número de linhas na Solicitação).
Além disso, algo que me parece um pouco estranho é a left join ip2country ic on r.IP between ic.begin_num and ic.end_num
parte (não sei se há uma maneira melhor de executar a pesquisa).
A estrutura da tabela, alguns dados de amostra e consulta estão disponíveis no SQLFiddle: http://www.sqlfiddle.com/#!3/a463e/3 (infelizmente não acho que posso inserir muitos registros para reproduzir o problema, mas isso espero que dê uma idéia).
Como (obviamente) eu não sou especialista em otimizações / desempenho de SQL, minha pergunta é: existem formas óbvias pelas quais essa estrutura / consulta possa ser aprimorada em termos de desempenho que estou perdendo?
fonte
begin_num
. Eu também tenho que participar comA BETWEEN B AND C
bastante frequência, e estou curioso para saber se há uma maneira de conseguir isso sem a junção tediosa do RBAR.begin_ip
eend_ip
manter colunas calculadas, para evitar a possibilidade de o texto e os números ficarem fora de sincronia de alguma forma.ip2country (begin_num, end_num)
?give me the first record that has a begin_num < ip in asc order of begin_num
(corrija-me se estiver errado) pode ser válida e melhorar o desempenho.begin_num
e depois verificaend_num
dentro desse conjunto e encontra apenas um registro.Respostas:
Você precisa de um índice adicional. No seu exemplo do Fiddle, eu adicionei:
CREATE UNIQUE INDEX ix_IP ON Request(CategoryID, IP)
Que abrange você para a tabela de solicitação e obtém uma busca de índice em vez de uma varredura de índice em cluster.
Veja como isso melhora e me avise. Acho que vai ajudar um pouco, já que a verificação desse índice é, com certeza, não é barata.
fonte
Sempre existe a abordagem da força bruta: você pode explodir seu mapa IP. Associe uma tabela de números ao seu mapa existente para criar um registro por endereço IP. São apenas 267 mil registros com base nos dados do Fiddle, sem nenhum problema.
Isso tornaria as buscas mais simples e, esperançosamente, mais rápidas. Isso só faz sentido se você fizer relativamente poucas atualizações
ip2country
, é claro.Espero que alguém tenha uma solução melhor!
fonte
Tente o seguinte:
fonte