J'ai deux tables dans lesquelles je stocke:
- une plage d'adresses IP - table de recherche par pays
- une liste de requêtes provenant de différentes IP
Les adresses IP ont été stockées sous forme de bigint
s pour améliorer les performances de recherche.
Voici la structure du tableau:
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
)
Je souhaite obtenir la répartition des demandes par pays, j'exécute donc la requête suivante:
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
J'ai beaucoup d'enregistrements dans les tableaux: environ 200 000 IP2Country
et quelques millions Request
, la requête prend donc un certain temps.
En regardant le plan d'exécution, la partie la plus coûteuse est une recherche d'index cluster sur l'index PK_IP2Country, qui est exécutée plusieurs fois (le nombre de lignes dans la demande).
En outre, quelque chose qui me semble un peu étrange est la left join ip2country ic on r.IP between ic.begin_num and ic.end_num
partie (je ne sais pas s'il existe une meilleure façon d'effectuer la recherche).
La structure de la table, des exemples de données et des requêtes sont disponibles dans SQLFiddle: http://www.sqlfiddle.com/#!3/a463e/3 (malheureusement, je ne pense pas pouvoir insérer de nombreux enregistrements pour reproduire le problème, mais cela donne, espérons-le, une idée).
Je ne suis (évidemment) pas un expert en performances / optimisations SQL, donc ma question est la suivante: y a-t-il des façons évidentes d'améliorer cette structure / requête en termes de performances qui me manquent?
la source
begin_num
. Je dois également participerA BETWEEN B AND C
assez souvent, et je suis curieux de savoir s'il existe un moyen d'y parvenir sans les fastidieuses jointures RBAR.begin_ip
etend_ip
persistantes, pour éviter que le texte et les chiffres ne se désynchronisent d'une manière ou d'une autre.ip2country (begin_num, end_num)
?give me the first record that has a begin_num < ip in asc order of begin_num
(corrigez-moi si je me trompe) pourrait être valide et améliorer les performances.begin_num
, puis analyseend_num
dans cet ensemble et ne trouve qu'un seul enregistrement.Réponses:
Vous avez besoin d'un index supplémentaire. Dans votre exemple Fiddle, j'ai ajouté:
CREATE UNIQUE INDEX ix_IP ON Request(CategoryID, IP)
Ce qui vous couvre pour la table de demande et obtient une recherche d'index au lieu d'une analyse d'index en cluster.
Voyez comment cela l'améliore et faites le moi savoir. J'imagine que ça va aider un peu puisque l'analyse sur cet index est que je ne suis certainement pas bon marché.
la source
Il y a toujours l'approche par force brute: vous pouvez exploser votre carte IP. Joignez un tableau de nombres à votre carte existante pour créer un enregistrement par adresse IP. Cela ne représente que 267 000 enregistrements basés sur vos données Fiddle, aucun problème du tout.
Cela rendrait les recherches plus simples et, espérons-le, plus rapides. Cela n'a de sens que si vous effectuez relativement peu de mises à jour
ip2country
, bien sûr.J'espère que quelqu'un d'autre a une meilleure solution!
la source
Essaye ça:
la source