문제

같은 것

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;
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top