Jeg havde at gøre med et lignende problem, hvor jeg skulle søge i en database med omkring 4 millioner IP-intervaller og fandt en fin løsning, der bragte antallet af scannede rækker ned fra 4 millioner til omkring ~5 (afhængigt af IP):
Denne SQL-sætning:
SELECT id FROM geoip WHERE $iplong BETWEEN range_begin AND range_end
er omdannet til:
SELECT id FROM geoip WHERE range_begin <= $iplong AND range_end >= $iplong
Problemet er, at MySQL henter alle rækker med 'range_begin <=$iplong' og derefter skal scannes, hvis 'range_end>=$iplong'. Denne første AND betingelse (range_begin <=$iplong) hentede omkring 2 millioner rækker, og alle skal kontrolleres, hvis range_end matcher.
Dette kan dog forenkles dramatisk ved at tilføje en OG-betingelse:
SELECT id FROM geoip WHERE range_begin <= $iplong AND range_begin >= $iplong-65535 AND range_end >= $iplong
Udtalelsen
range_begin <= $iplong AND range_begin >= $iplong-65535
henter kun poster, hvor range_begin er mellem $iplong-65535 og $iplong. I mit tilfælde reducerede dette antallet af hentede rækker fra 4 mio. til omkring 5, og scriptets køretid gik ned fra flere minutter til et par sekunder.
Bemærk om 65535 :Dette er for min tabel den maksimale afstand mellem range_begin og range_end, dvs. (range_end-range_begin) <=65535 for alle mine rækker. Hvis du har større IP-områder, skal du øge 65535, hvis du har mindre IP-områder, kan du reducere denne konstant. Hvis denne konstant er for stor (for eksempel 4 milliarder), sparer du ingen forespørgselstid.
Til denne forespørgsel behøver du kun et indeks på range_begin.