Tengo dos mesas en las que almaceno:
- un rango de IP - tabla de búsqueda de países
- una lista de solicitudes provenientes de diferentes IP
Las IP se almacenaron como bigint
s para mejorar el rendimiento de búsqueda.
Esta es la estructura de la tabla:
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
)
Quiero obtener el desglose de la solicitud por país, por lo que realizo la siguiente 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
Tengo muchos registros en las tablas: alrededor de 200,000 IP2Country
y algunos millones Request
, por lo que la consulta lleva un tiempo.
Mirando el plan de ejecución, la parte más costosa es una Búsqueda de índice agrupado en el índice PK_IP2Country, que se ejecuta muchas veces (el número de filas en Solicitud).
Además, algo de lo que me siento un poco extraño es la left join ip2country ic on r.IP between ic.begin_num and ic.end_num
parte (no sé si hay una mejor manera de realizar la búsqueda).
La estructura de la tabla, algunos datos de muestra y consultas están disponibles en SQLFiddle: http://www.sqlfiddle.com/#!3/a463e/3 (desafortunadamente no creo que pueda insertar muchos registros para reproducir el problema, pero esto con suerte da una idea).
No soy (obviamente) un experto en rendimiento / optimizaciones de SQL, así que mi pregunta es: ¿hay alguna forma obvia de mejorar esta estructura / consulta en cuanto al rendimiento que me falta?
fuente
begin_num
. También tengo que unirme conA BETWEEN B AND C
bastante frecuencia, y tengo curiosidad por saber si hay una manera de lograr esto sin tediosas uniones RBAR.begin_ip
yend_ip
persistentes, para evitar la posibilidad de que el texto y los números se desincronicen de alguna manera.ip2country (begin_num, end_num)
?give me the first record that has a begin_num < ip in asc order of begin_num
(corríjame si me equivoco) podría ser válida y mejorar el rendimiento.begin_num
, luego escaneaend_num
dentro de ese conjunto y solo encuentra un registro.Respuestas:
Necesitas un índice adicional. En su ejemplo de Fiddle agregué:
CREATE UNIQUE INDEX ix_IP ON Request(CategoryID, IP)
Que lo cubre para la tabla de solicitud y obtiene una búsqueda de índice en lugar de una exploración de índice agrupado.
Vea cómo eso lo mejora y hágamelo saber. Supongo que ayudará bastante ya que el escaneo en ese índice es seguro que no es barato.
fuente
Siempre existe el enfoque de la fuerza bruta: podría explotar su mapa IP. Únase a una tabla de números contra su mapa existente para crear un registro por dirección IP. Eso es solo 267K registros basados en sus datos de Fiddle, sin ningún problema.
Esto haría las búsquedas más simples y, con suerte, más rápidas. Esto solo tiene sentido si realiza relativamente pocas actualizaciones
ip2country
, por supuesto.¡Espero que alguien más tenga una mejor solución!
fuente
Prueba esto:
fuente