IPアドレスの保存-varchar(45)とvarbinary(16)


11

2つのフィールド(IDas BIGINTおよびIPAddressas varchar(45)またはor)を持つテーブルを作成しますvarbinary(16)。アイデアは、すべての一意のIPアドレスを保存し、他のテーブルのID実際のIPアドレスの代わりに参照を使用することIP addressです。

一般的に、ID与えられたforを返す、IP addressまたは(アドレスが見つからなかった場合は)アドレスを挿入して生成されたを返すストアドプロシージャを作成しますID

多くのレコードがあることを期待しています(正確な数はわかりません)が、上記のストアドプロシージャをできるだけ速く実行する必要があります。それで、実際のIPアドレスをテキストまたはバイト形式で保存する方法を知りたいです。どっちがいいの?

SQL CLRIPアドレスのバイトを文字列に変換したり、その逆を行ったりするための関数をすでに作成しているため、変換は問題ではありません(IPv4およびの両方で機能しますIPv6)。

検索を最適化するためにインデックスを作成する必要があると思いIP addressますが、クラスター化インデックスにフィールドを含める必要があるのか、または別のインデックスを作成し、どのタイプで検索を高速化するのかわかりませんか?


2
少なくともIPv4の場合、なぜ4 tinyintsでないのですか?そうすれば、実際には人間が読める形式になり、変換を実行する必要はありません。すべての種類の永続的な計算列を作成して、特定のタイプの検索(完全一致、サブネットなど)を表すこともできます。
アーロンバートランド

それだけの場合はIPv4、アドレスをに変換しINT、フィールドをインデックスキーとして使用したと思います。しかし、IPv6私は2つのBIGINTフィールドを使用する必要があり、1つのフィールドに値を格納することを好むため、より自然に思えます。
gotqn 2015年

1
それでもなぜ4つのTINYINTではなくINTなのか理解できませんか?同じストレージ、より簡単なデバッグ、あまり意味のない、私見。検証と意味が異なる2つの完全に異なる型がある場合、なぜ同じ列を使用する必要があるのですか?単一の列の方が単純であると賭けているのであれば、SQL_VARIANTを使用するだけでなく、何も心配する必要はありません。あなたは、日付と文字列や数値を格納することができますし、誰もが1つの巨大な、役に立たないコラム...で大きなパーティーを持つことができます
アーロン・ベルトラン

IPアドレスはどこから来ていますか?それらには、マスク/サブネット(つまり10.10.10.1/124)が含まれますか?これはWebサーバーのログから取得され、BIGINTに簡単に変換されないことを確認しました(もちろん、正規化を組み込んで0が実際には-2.14xxxx billionでないと仮定しない限り、計算は符号なしINTを必要とするため、INTは機能しません)。サブネットマスクは、追加のTINYINTフィールドである可能性があります。しかし、それらをマップして緯度/経度のDBに一致させたい場合は、BIGINTとして保存することを理解しています。しかし、Aaronが述べたように、これは永続的な計算されたcolになる可能性があります。
ソロモンルツキー2015年

回答:


12

実際のIPアドレスを保存する方法-テキストまたはバイト形式。どっちがいいの?

ここでの「テキスト」はを指しVARCHAR(45)、「バイト」はを指すのでVARBINARY(16)どちらとも言えません

次の情報が与えられた場合(IPv6に関するWikipediaの記事から):

アドレス表現
IPv6アドレスの128ビットは、それぞれ16ビットの8つのグループで表されます。各グループは4桁の16進数として記述され、グループはコロン(:)で区切られます。アドレス2001:0db8:0000:0000:0000:ff00:0042:8329は、この表現の例です。

便宜上、可能な場合は、次の規則を適用することにより、IPv6アドレスを短い表記に省略できます。

  • 16進数のグループから1つ以上の先行ゼロが削除されます。これは通常、先行ゼロのすべてに対して行われるか、ゼロに行われます。たとえば、グループ0042は42に変換されます。
  • ゼロの連続するセクションは、二重コロン(::)に置き換えられます。二重コロンは、複数回使用するとアドレスが不確定になるため、アドレスで1回だけ使用できます。RFC 5952は、ゼロの省略された単一セクションを示すために二重コロンを使用してはならないことを推奨しています。[41]

これらのルールの適用例:

        初期アドレス:2001:0db8:0000:0000:0000:ff00:0042:8329
        各グループの先行ゼロをすべて削除した後:2001:db8:0:0:0:ff00:42:8329
        ゼロの連続セクションを省略した後:2001 :db8 :: ff00:42:8329

まず、8つのVARBINARY(2)フィールドを使用して8 つのグループを表します。グループ5から8のフィールドはNULL、IPv6アドレスにのみ使用されるためです。グループ1〜4のフィールドはNOT NULL、IPv4とIPv6の両方のアドレスに使用されるためです。

(どちらかにそれらを組み合わせることとは対照的に、各グループの独立を保つことによってVARCHAR(45)VARBINARY(16)、あるいは2 BIGINT次の2つの主な利点を得るフィールド):

  1. アドレスを特定の表現に再構築する方がはるかに簡単です。それ以外の場合、連続するゼロのグループを(::)に置き換えるには、解析する必要があります。それらを別々にしておくと、これを容易にする簡単なIF/ IIF/ CASEステートメントが可能になります。
  2. ROW COMPRESSIONまたはのいずれかを有効にすると、IPv6アドレスのスペースを大幅に節約できますPAGE COMPRESSION。どちらのタイプのCOMPRESSIONでも0x000バイトを占めるフィールドが許可されるため、これらのゼロのグループのすべてでコストが発生することはありません。一方、上記の例のアドレスを(Wikipediaの引用で)保存した場合、中央にあるすべてのゼロの3セットは、スペースをすべて使用します(ただしVARCHAR(45)、 、しかしそれは索​​引付けにはうまく機能しないかもしれませんし、それを完全なフォーマットに再構築するために特別な解析が必要になるでしょうので、それはオプションではないと仮定しましょう;-)

ネットワークをキャプチャする必要がある場合は、そのためのTINYINTフィールドを作成します。ええと、[Network]:-)

ネットワーク値の詳細については、IPv6アドレスに関するウィキペディアの別の記事からの情報を以下に示します

ネットワーク

IPv6ネットワークは、2の累乗のサイズのIPv6アドレスの連続したグループであるアドレスブロックを使用します。アドレスの先頭ビットセットは、特定のネットワーク内のすべてのホストで同一であり、ネットワークのアドレスまたはルーティングプレフィックスと呼ばれます

ネットワークアドレス範囲はCIDR表記で記述されます。ネットワークは、ブロック内の最初のアドレス(すべてゼロで終わる)、スラッシュ(/)、および接頭辞のビット単位のサイズに等しい10進値で示されます。たとえば、2001:db8:1234 :: / 48と書かれたネットワークは、アドレス2001:db8:1234:0000:0000:0000:0000:0000で始まり、2001:db8:1234:ffff:ffff:ffff:ffffで終わります。 :ffff。

インターフェイスアドレスのルーティングプレフィックスは、CIDR表記によってアドレスで直接示される場合があります。たとえば、アドレス2001:db8:a :: 123がサブネット2001:db8:a :: / 64に接続されているインターフェースの構成は、2001:db8:a :: 123/64と記述されます。


インデックスを作成するために、8つのグループフィールド、および場合によってはネットワークフィールドを含めることにした場合は、ネットワークフィールドに非クラスター化インデックスを作成するとします。


最終結果は次のようになります。

CREATE TABLE [IPAddress]
(
  IPAddressID INT          NOT NULL IDENTITY(-2147483648, 1),
  Group8      VARBINARY(2) NULL, -- IPv6 only, NULL for IPv4
  Group7      VARBINARY(2) NULL, -- IPv6 only, NULL for IPv4
  Group6      VARBINARY(2) NULL, -- IPv6 only, NULL for IPv4
  Group5      VARBINARY(2) NULL, -- IPv6 only, NULL for IPv4
  Group4      VARBINARY(2) NOT NULL, -- both
  Group3      VARBINARY(2) NOT NULL, -- both
  Group2      VARBINARY(2) NOT NULL, -- both
  Group1      VARBINARY(2) NOT NULL, -- both
  Network     TINYINT      NULL
);

ALTER TABLE [IPAddress]
  ADD CONSTRAINT [PK_IPAddress]
  PRIMARY KEY CLUSTERED
  (IPAddressID ASC)
  WITH (FILLFACTOR = 100, DATA_COMPRESSION = PAGE);

CREATE NONCLUSTERED INDEX [IX_IPAddress_Groups]
  ON [IPAddress] (Group1 ASC, Group2 ASC, Group3 ASC, Group4 ASC,
         Group5 ASC, Group6 ASC, Group7 ASC, Group8 ASC, Network ASC)
  WITH (FILLFACTOR = 100, DATA_COMPRESSION = PAGE);

ノート:

  • BIGINTIDフィールドに使用する予定ですが、4,294,967,295を超える一意の値をキャプチャすることを本当に期待していますか?その場合は、フィールドをBIGINTに変更するだけで、シード値を0に変更することもできます。それ以外の場合は、INTを使用し、最小値から始めて、そのデータ型の範囲全体を利用できるようにする方がよいでしょう。 。
  • 必要に応じて、1つ以上のNONpersisted Computed Columnsをこのテーブルに追加して、IPAddressのテキスト表現を返すことができます。
  • Group *フィールドは、テーブル内で8から1へと意図的にに配置されているため、実行SELECT *するとフィールドが期待どおりの順序で返されます。しかし、インデックスは、1から8 へと上昇しています。これは、それらが入力される方法です。
  • テキスト形式で値を表す計算列の例(未完成)は次のとおりです。

    ALTER TABLE [IPAddress]
      ADD TextAddress AS (
    IIF([Group8] IS NULL,
        -- IPv4
        CONCAT(CONVERT(TINYINT, [Group4]), '.', CONVERT(TINYINT, [Group3]), '.',
          CONVERT(TINYINT, [Group2]), '.', CONVERT(TINYINT, [Group1]),
          IIF([Network] IS NOT NULL, CONCAT('/', [Network]), '')),
        -- IPv6
        LOWER(CONCAT(
          CONVERT(VARCHAR(4), [Group8], 2), ':', CONVERT(VARCHAR(4), [Group7], 2), ':',
          CONVERT(VARCHAR(4), [Group6], 2), ':', CONVERT(VARCHAR(4), [Group5], 2), ':',
          CONVERT(VARCHAR(4), [Group4], 2), ':', CONVERT(VARCHAR(4), [Group3], 2), ':',
          CONVERT(VARCHAR(4), [Group2], 2), ':', CONVERT(VARCHAR(4), [Group1], 2),
          IIF([Network] IS NOT NULL, CONCAT('/', [Network]), '')
         ))
       ) -- end of IIF
    );

    テスト:

    INSERT INTO IPAddress VALUES (127, 0, 0, 0, 4, 22, 222, 63, NULL); -- IPv6
    INSERT INTO IPAddress VALUES (27, 10, 1234, 0, 45673, 200, 1, 6363, 48); -- IPv6
    INSERT INTO IPAddress VALUES (NULL, NULL, NULL, NULL, 192, 168, 2, 63, NULL); -- v4
    INSERT INTO IPAddress VALUES (NULL, NULL, NULL, NULL, 192, 168, 137, 29, 16); -- v4
    
    SELECT [IPAddressID], [Group8], [Group1], [Network], [TextAddress]
    FROM IPAddress ORDER BY [IPAddressID];

    結果:

    IPAddressID   Group8   Group1   Network  TextAddress
    -----------   ------   ------   -------  ---------------------
    -2147483646   0x007F   0x003F   NULL     007f:0000:0000:0000:0004:0016:00de:003f
    -2147483645   0x001B   0x18DB   48       001b:000a:04d2:0000:b269:00c8:0001:18db/48
    -2147483644   NULL     0x003F   NULL     192.168.2.63
    -2147483643   NULL     0x001D   16       192.168.137.29/16

SQL Server 2005の場合、利用できないため、列をVARDECIMALオーバーと定義しますか?VARBINARYDATA_COMPRESSION
Matt

@SolomonRutzky詳しい説明ありがとうございます。知りたいのですが、アドレス範囲間でどのように検索しますか?たとえば、IPジオロケーションデータを開始IPアドレスと終了IPアドレスの形式で提供するデータベンダーがあります。私は、与えられたIPがに落ちるどの範囲見つける必要があります。
J Weezy

@JWeezyどういたしまして:) 開始IPアドレスと終了IPアドレスはどのように保存されますか?IPv4またはv6アドレスを使用していますか?
ソロモンルツキー

@SolomonRutzky両方。IPv4は整数として格納できるので問題ありません。残念ながら、SQL Serverにはそれを処理するのに十分な大きさの128ビット整数または数値関連のデータ型はありません。したがって、IPv6の場合はVARBINARY(16)に格納し、BETWEEN演算子を使用して範囲間を検索します。しかし、IP範囲で複数の結果が得られていますが、これは正しくないと思います。可能であれば、IPv4とIPv6の両方に同じデータ型を使用したいと思います。
J Weezy

@JWeezy私は提案するつもりだったBINARY(16);-)。開始/終了の範囲と、返される少なくとも2つの行の例を示してください。1つは有効で、もう1つは無効です。VARbinaryが一部の値を短縮する可能性があります。
ソロモンルツキー

1

小さいほど常に高速になります。値が小さいと、より多くの値を1つのページに収めることができるため、IOが少なくなり、Bツリーが浅くなる可能性があります。

もちろん、他のすべてのもの(翻訳のオーバーヘッド、読みやすさ、互換性、CPU負荷、インデックスの検索可能性など)は同じです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.