Ich hatte es mit einem ähnlichen Problem zu tun, bei dem ich eine Datenbank mit etwa 4 Millionen IP-Bereichen durchsuchen musste und eine nette Lösung fand, die die Anzahl der gescannten Zeilen von 4 Millionen auf etwa 5 (je nach IP) reduzierte:
Diese SQL-Anweisung:
SELECT id FROM geoip WHERE $iplong BETWEEN range_begin AND range_end
wird umgewandelt in:
SELECT id FROM geoip WHERE range_begin <= $iplong AND range_end >= $iplong
Das Problem ist, dass MySQL alle Zeilen mit „range_begin <=$iplong“ abruft und dann scannen muss, wenn „range_end>=$iplong“ ist. Diese erste UND-Bedingung (range_begin <=$iplong) hat ungefähr 2 Millionen Zeilen abgerufen, und alle müssen überprüft werden, ob range_end übereinstimmt.
Dies kann jedoch drastisch vereinfacht werden, indem eine UND-Bedingung hinzugefügt wird:
SELECT id FROM geoip WHERE range_begin <= $iplong AND range_begin >= $iplong-65535 AND range_end >= $iplong
Die Aussage
range_begin <= $iplong AND range_begin >= $iplong-65535
ruft nur Einträge ab, bei denen range_begin zwischen $iplong-65535 und $iplong liegt. In meinem Fall reduzierte dies die Anzahl der abgerufenen Zeilen von 4 Mio. auf etwa 5 und die Laufzeit des Skripts ging von mehreren Minuten auf wenige Sekunden zurück.
Hinweis zu 65535 :Dies ist für meine Tabelle der maximale Abstand zwischen range_begin und range_end, dh (range_end-range_begin) <=65535 für alle meine Zeilen. Wenn Sie größere IP-Bereiche haben, müssen Sie die 65535 erhöhen, wenn Sie kleinere IP-Bereiche haben, können Sie diese Konstante verringern. Wenn diese Konstante zu groß ist (z. B. 4 Milliarden), sparen Sie keine Abfragezeit.
Für diese Abfrage brauchen Sie nur einen Index auf range_begin.