23

何かのようなもの

SELECT COUNT(*) AS c FROM BANS WHERE typeid=6 AND (SELECT ipaddr,cidr FROM BANS) MATCH AGAINST 'this_ip';

したがって、最初に DB からすべてのレコードを取得してから、それらを 1 つずつ照合するわけではありません。

c > 0 の場合、一致しました。

BANS テーブル:

id int auto incr PK
typeid TINYINT (1=hostname, 4=ipv4, 6=ipv6)
ipaddr BINARY(128)
cidr INT
host VARCHAR(255)

データベース: MySQL 5

IP および IPv タイプ (4 または 6) は、クエリ時に認識されます。

IP は、バイナリ形式の ::1 などです。

禁止 IP の例:::1/64

4

5 に答える 5

30

IPはテキストアドレスではなく、数値IDであることに注意してください。私も同様の状況にあり(geo-ipルックアップを実行しています)、すべてのIPアドレスを整数として保存すると(たとえば、私のIPアドレスは192.115.22.33なので、3228767777として保存されます)、IPをルックアップできます。右シフト演算子を使用して簡単に。

これらすべてのタイプのルックアップの欠点は、インデックスの恩恵を受けることができず、ルックアップを実行するたびに全表スキャンを実行する必要があることです。上記のスキームは、CIDRネットワークのネットワークIPアドレス(範囲の開始)とブロードキャストアドレス(範囲の終了)の両方を格納することで改善できます。たとえば、192.168.1.0 / 24を格納するには、2つを格納できます。列:

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と呼ばれる最適化が含まれており、これにより全行の取得も高速化されますが、これはこの回答の範囲外です。

于 2009-02-27T20:45:34.827 に答える
1

整数としての 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;
于 2019-05-13T23:59:41.860 に答える
0

うーん。cidrマスクのテーブルを作成し、それを結合してから、ip anded(&MySQLの場合)をbanブロックのIPアドレスを持つマスクと比較することができます。それはあなたが望むことをしますか?

マスクテーブルを作成したくない場合は、と同じよう-1 << (x-cidr)に、x = 64または32依存してマスクを計算できます。

于 2009-02-27T17:43:22.830 に答える