Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

245
Vistas
MySQL BETWEEN query not using index

I have geoip data in a table, network_start_ip and network_end_ip are varbinary(16) columns with the result of INET6_ATON(ip_start/end) as values. 2 other columns are latitude and longitude.

CREATE TABLE `ipblocks` (
 `network_start_ip` varbinary(16) NOT NULL,
 `network_last_ip` varbinary(16) NOT NULL,
 `latitude` double NOT NULL,
 `longitude` double NOT NULL,
 KEY `network_start_ip` (`network_start_ip`),
 KEY `network_last_ip` (`network_last_ip`),
 KEY `idx_range` (`network_start_ip`,`network_last_ip`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8

As you can see I have created 3 indexes for testing. Why does my (quite simple) query

SELECT 
    latitude, longitude
FROM
    ipblocks b
WHERE
    INET6_ATON('82.207.219.33') BETWEEN b.network_start_ip AND b.network_last_ip

not use any these indexes?

result of EXPLAIN

The query takes ~3 seconds which is way too long to use it in production.

over 4 years ago · Santiago Trujillo
3 Respuestas
Responde la pregunta

0

It doesn't work because there are two columns referenced -- and that is really hard to optimize. Assuming that there are no overlapping IP ranges, you can restructure the query as:

SELECT b.*
FROM (SELECT b.*
      FROM ipblocks b
      WHERE b.network_start_ip <= INET6_ATON('82.207.219.33')
      ORDER BY b.network_start_ip DESC
      LIMIT 1
     ) b
WHERE INET6_ATON('82.207.219.33') <= network_last_ip;

The inner query should use an index on ipblocks(network_start_ip). The outer query is only comparing one row, so it does not need any index.

Or as:

SELECT b.*
FROM (SELECT b.*
      FROM ipblocks b
      WHERE b.network_last_ip >= INET6_ATON('82.207.219.33')
      ORDER BY b.network_end_ip ASC
      LIMIT 1
     ) b
WHERE network_last_ip <= INET6_ATON('82.207.219.33');

This would use an index on (network_last_ip). MySQL (and I think MariaDB) does a better job with ascending sorts than descending sorts.

over 4 years ago · Santiago Trujillo Denunciar

0

Thanks to Gordon Linoff I found the optimal query for my question.

SELECT b.* FROM 
  (SELECT b.* FROM ipblocks b WHERE b.network_start_ip <= INET6_ATON('82.207.219.33') 
                              ORDER BY b.network_start_ip DESC LIMIT 1 ) 
b WHERE INET6_ATON('82.207.219.33') <= network_last_ip

Now we select the blocks smaller than INET6_ATON(82.207.219.33) in the inner query but we order them descending which enables us to use the LIMIT 1 again.

Query response time is now .002 to .004 seconds. Great!

over 4 years ago · Santiago Trujillo Denunciar

0

Does this query give you correct results? Your start/end IPs seem to be stored as a binary string while you're searching for an integer representation. I would first make sure that network_start_ip and network_last_ip are unsigned INT fields with the integer representation of the IP addresses. This is assuming that you work with IPv4 only:

CREATE TABLE ipblocks_int AS
SELECT
    INET_ATON(network_start_ip) as network_start_ip,
    INET_ATON(network_last_ip) as network_last_ip,
    latitude,
    longitude
FROM ipblocks

Then use (network_start_ip,network_last_ip) as primary key.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda