MySQL BETWEEN 쿼리가 인덱스를 사용하지 않음
테이블에 지리정보가 있는데network_start_ip
그리고.network_end_ip
이다varbinary(16)
결과가 있는 컬럼INET6_ATON(ip_start/end)
가치관으로서.다른 두 열은 위도와 경도입니다.
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
보시는 바와 같이 테스트용 인덱스를 3개 만들었습니다.(quit simple) 쿼리는 왜
SELECT
latitude, longitude
FROM
ipblocks b
WHERE
INET6_ATON('82.207.219.33') BETWEEN b.network_start_ip AND b.network_last_ip
이 인덱스를 사용하지 않는가?
쿼리는 프로덕션에서 사용하기에는 너무 긴 최대 3초 정도 걸립니다.
2개의 컬럼이 참조되어 있기 때문에 동작하지 않습니다.이것은 최적화가 매우 어렵습니다.중복되는 IP 범위가 없다고 가정하면 다음과 같이 쿼리를 재구성할 수 있습니다.
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;
내부 쿼리는 다음에 인덱스를 사용해야 합니다.ipblocks(network_start_ip)
외부 쿼리는 한 행만 비교하므로 인덱스가 필요하지 않습니다.
또는 다음과 같이 합니다.
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');
이것은 다음 항목에 대한 인덱스를 사용합니다.(network_last_ip)
MySQL(및 MariaDB)은 내림차순 정렬보다 오름차순 정렬을 더 잘합니다.
Gordon Linoff 덕분에 나는 내 질문에 대한 최적의 질문을 찾았다.
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
이제 다음보다 작은 블록을 선택합니다.INET6_ATON(82.207.219.33)
내림차순을 정해서 우리가 사용할 수 있게 합니다.LIMIT 1
다시.
현재 쿼리 응답 시간은 .002 ~ .004초입니다좋았어!
이 쿼리는 올바른 결과를 제공합니까?정수 표현을 검색하는 동안 시작/끝 IP가 이진 문자열로 저장되는 것 같습니다.먼저 network_start_ip 및 network_last_ip이 IP 주소를 정수화한 부호 없는 INT 필드인지 확인합니다.이것은, IPv4 만을 사용하고 있는 것을 전제로 하고 있습니다.
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
다음으로 (network_start_ip, network_last_ip)를 프라이머리 키로 사용합니다.
어려운 문제입니다.간단한 해결책은 없습니다.
그것이 힘든 이유는 그것이 효과적이기 때문이다.
start <= 123 AND
last >= 123
사용 가능한 인덱스에 관계없이 Optimizer는 이러한 인덱스와 함께 작동합니다.와 함께INDEX(start, ...)
, 그것은 선택될 것이다.start <= 123
인덱스의 첫 번째 부분을 스캔합니다.다른 절도 마찬가지입니다.이 중 하나는 인덱스의 절반 이상을 스캔하고 다른 하나는 더 적게 스캔하지만 인덱스를 사용할 가치가 있을 만큼 충분히 스캔하지는 않습니다.로의 이동PRIMARY KEY
어떤 경우에는 도움이 되지만, 노력할 가치가 거의 없습니다.
요컨대, 무엇을 하든INDEX
또는PRIMARY KEY
대부분의 IP 상수는 쿼리에 1.5초 이상 걸립니다.
"/" IP "/" IP "/" IP "/"?렇면의 '고든', '고든', '고든', '고든', '고든', '고든', '고든', '고든', '고든', '고든', '고든', '고든', '고든', '고든'이 무효가 될 수 있습니다.LIMIT 1
.
이 솔루션에는 중복되지 않는 지역이 포함되어 있습니다.IP 의 갭은, 「소유되지 않은」IP 의 범위를 필요로 합니다.이는 start_ip만 존재하기 때문입니다.last_ip은 테이블 내의 다음 항목의 시작보다 작음을 암시합니다.http://mysql.rjweb.org/doc.php/ipranges 를 참조해 주세요(IPv4 및 IPv6 의 코드가 포함되어 있습니다).
한 meanwhile.DOUBLE
lat/lng은 과잉입니다.http://mysql.rjweb.org/doc.php/latlng#representation_choices
언급URL : https://stackoverflow.com/questions/62738076/mysql-between-query-not-using-index
'programing' 카테고리의 다른 글
MySQL 스왑 사용 및 최종 oom_killer의 원인 (0) | 2022.10.02 |
---|---|
JAXB란 무엇이며 왜 사용해야 합니까? (0) | 2022.10.02 |
Java에서 자동 박스와 언박스를 사용하는 이유는 무엇입니까? (0) | 2022.10.02 |
Big Decimal, 정밀도 및 확장성 (0) | 2022.10.02 |
Scala에서 통합 및 루프를 최적화하려면 어떻게 해야 합니까? (0) | 2022.10.02 |