Select Query에서 IP+CIDR과 IP를 일치시키는 방법이 있습니까?
문제
같은 것
SELECT COUNT(*) AS c FROM BANS WHERE typeid=6 AND (SELECT ipaddr,cidr FROM BANS) MATCH AGAINST 'this_ip';
따라서 먼저 DB에서 모든 레코드를 가져온 다음 한 번에 일치하지 않습니다.
C> 0이 일치하면.
금지 테이블 :
id int auto incr PK
typeid TINYINT (1=hostname, 4=ipv4, 6=ipv6)
ipaddr BINARY(128)
cidr INT
host VARCHAR(255)
DB : MySQL 5
IP 및 IPV 유형 (4 또는 6)은 쿼리 할 때 알려져 있습니다.
IP는 예를 들어 :: 1 바이너리 형식입니다
금지 된 IP는 예를 들어 :: 1/64입니다
해결책
IP는 텍스트 주소가 아니라 숫자 ID라는 것을 기억하십시오. 비슷한 상황이 있습니다 (우리는 Geo-IP 조회를하고 있습니다). 모든 IP 주소를 정수로 저장하는 경우 (예 : 내 IP 주소는 192.115.22.33이므로 3228767777로 저장) IPS를 조회 할 수 있습니다. 올바른 시프트 연산자를 사용하여 쉽게.
이러한 모든 유형의 조회의 단점은 인덱스로부터 혜택을받을 수 없으며 조회를 할 때마다 전체 테이블 스캔을해야한다는 것입니다. 위의 체계는 CIDR 네트워크의 네트워크 IP 주소 (범위의 시작)와 브로드 캐스트 주소 (범위의 끝)를 모두 저장하여 개선 될 수 있으므로 예를 들어 192.168.1.0/24를 저장하려면 두 개를 저장할 수 있습니다. 열 :
network broadcast
3232235776, 3232236031
그리고 당신은 단순히 당신이 그것을 일치시킬 수 있습니다.
SELECT count(*) FROM bans WHERE 3232235876 >= network AND 3232235876 <= broadcast
이를 통해 CIDR 네트워크를 데이터베이스에 저장하고 빠른 숫자 인덱스를 활용하여 IP 주소와 신속하고 효율적으로 일치시킬 수 있습니다.
아래 논의에서 참고하십시오:
MySQL 5.0에는 "라는 원거리 쿼리 최적화가 포함되어 있습니다.인덱스 병합 교차"이 쿼리 속도를 높이고 전체 테이블 스캔을 피할 수 있습니다.
- 쿼리의 열과 정확히 일치하는 다중 열 인덱스가 있습니다. 따라서 - 위의 쿼리 예제의 경우 인덱스는
(network, broadcast)
. - 모든 데이터는 인덱스에서 검색 할 수 있습니다. 이것은 사실입니다
COUNT(*)
, 그러나 사실은 아닙니다SELECT * ... LIMIT 1
.
MySQL 5.6에는 MRR이라는 최적화가 포함되어있어 전체 행 검색 속도가 빨라지지만이 답변의 범위가 없습니다.
다른 팁
IPv4 주소, 네트워크 주소 및 넷 마스크는 모두 UINT32 숫자이며 인간으로 읽을 수있는 형태로 "점선 쿼드"로 표시됩니다. 커널의 라우팅 테이블 코드는 주어진 네트워크 공간 (네트워크/넷 마스크)에 있는지 확인할 때 매우 빠른 비트와 비교를 수행합니다. 여기서 트릭은 점선 쿼드 IP 주소, 네트워크 주소 및 넷 마스크를 테이블에 UINT32와 함께 저장 한 다음 동일한 32 비트 비트 와이즈와 일치를 수행하는 것입니다. 예를 들어
SET @test_addr = inet_aton('1.2.3.4');
SET @network_one = inet_aton('1.2.3.0');
SET @network_two = inet_aton('4.5.6.0');
SET @network_netmask = inet_aton('255.255.255.0');
SELECT (@test_addr & @network_netmask) = @network_one AS IS_MATCHED;
+------------+
| IS_MATCHED |
+------------+
| 1 |
+------------+
SELECT (@test_addr & @network_netmask) = @network_two AS IS_NOT_MATCHED;
+----------------+
| IS_NOT_MATCHED |
+----------------+
| 0 |
+----------------+
을 위한 IPv4
, 당신이 사용할 수있는:
SET @length = 4;
SELECT INET_NTOA(ipaddr), INET_NTOA(searchaddr), INET_NTOA(mask)
FROM (
SELECT
(1 << (@length * 8)) - 1 & ~((1 << (@length * 8 - cidr)) - 1) AS mask,
CAST(CONV(SUBSTR(HEX(ipaddr), 1, @length * 2), 16, 10) AS DECIMAL(20)) AS ipaddr,
CAST(CONV(SUBSTR(HEX(@myaddr), 1, @length * 2), 16, 10) AS DECIMAL(20)) AS searchaddr
FROM ip
) ipo
WHERE ipaddr & mask = searchaddr & mask
흠. CIDR 마스크 테이블을 만들고 결합 한 다음 IP를 비교할 수 있습니다 (&
MySQL에서) 금지 블록 iPaddress가있는 마스크와 함께. 그것이 당신이 원하는 것을할까요?
마스크 테이블을 만들고 싶지 않다면 마스크를 다음과 같이 계산할 수 있습니다. -1 << (x-cidr)
~와 함께 x = 64
또는 32
에 따라.
IP 주소를 정수로 생성합니다
데이터베이스가 멋진 비트 시합 작업을 지원하지 않으면 단순화 된 정수 기반 접근법을 사용할 수 있습니다.
다음 예제는 PostgreSQL을 사용하는 것입니다.
select (cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 1) as bigint) * (256 * 256 * 256) +
cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 2) as bigint) * (256 * 256 ) +
cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 3) as bigint) * (256 ) +
cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 4) as bigint))
as network,
(cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 1) as bigint) * (256 * 256 * 256) +
cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 2) as bigint) * (256 * 256 ) +
cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 3) as bigint) * (256 ) +
cast(split_part(split_part('4.0.0.0/8', '/', 1), '.', 4) as bigint)) + cast(
pow(256, (32 - cast(split_part('4.0.0.0/8', '/', 2) as bigint)) / 8) - 1 as bigint
) as broadcast;