Miałem do czynienia z podobnym problemem, gdzie musiałem przeszukać bazę danych z około 4 milionami zakresów adresów IP i znalazłem fajne rozwiązanie, które zmniejszyło liczbę skanowanych wierszy z 4 milionów do około ~5 (w zależności od IP):
Ta instrukcja SQL:
SELECT id FROM geoip WHERE $iplong BETWEEN range_begin AND range_end
przekształca się w:
SELECT id FROM geoip WHERE range_begin <= $iplong AND range_end >= $iplong
Problem polega na tym, że MySQL pobiera wszystkie wiersze z 'range_begin <=$iplong', a następnie musi przeskanować, czy 'range_end>=$iplong'. Ten pierwszy warunek ORAZ (range_begin <=$iplong) pobrał około 2 miliony wierszy i wszystkie należy sprawdzić, jeśli pasuje do range_end.
Można to jednak radykalnie uprościć, dodając jeden warunek AND:
SELECT id FROM geoip WHERE range_begin <= $iplong AND range_begin >= $iplong-65535 AND range_end >= $iplong
Oświadczenie
range_begin <= $iplong AND range_begin >= $iplong-65535
pobiera tylko wpisy, w których range_begin mieści się w zakresie od $iplong-65535 do $iplong. W moim przypadku zmniejszyło to liczbę pobranych wierszy z 4 mln. do około 5, a czas działania skryptu skrócił się z kilku minut do kilku sekund.
Uwaga na temat 65535 :To jest dla mojej tabeli maksymalna odległość między range_begin i range_end, tj. (range_end-range_begin) <=65535 dla wszystkich moich wierszy. Jeśli masz większe zakresy adresów IP, musisz zwiększyć 65535, jeśli masz mniejsze zakresy adresów IP, możesz zmniejszyć tę stałą. Jeśli ta stała jest zbyt duża (na przykład 4 miliardy), nie zaoszczędzisz czasu na zapytania.
Dla tego zapytania potrzebujesz tylko indeksu na range_begin.