2011-02-09 5 views
14

В Oracle, каков соответствующий тип данных или метод для представления сетевых адресов, какие адреса могут быть IPv4 или IPv6?Представление адресов IPv4/IPv6 в Oracle

Справочная информация: Я конвертирую сетевую активность записи таблицы, построенную с использованием PostgreSQL inet data type, для хранения адресов v4 и v6 в одной таблице.

Ни одна строка не содержит как адреса v4, так и v6. (То есть, запись либо из стека v4 машина, либо стек v6 машины в.)

+1

Выполняете ли вы агрегирование/поиск по этой таблице по IP-адресу? сколько строк вы ожидаете за 1 год? – jachguate

+0

Агрегирование и поиск по IP, да. Количество строк, возможно, сотни миллионов. (У вас есть рекомендации для малых/средних/больших?) – pilcrow

+0

, пожалуйста, используйте примечание @jachguate в своих комментариях, если вы хотите, чтобы я получил уведомление об этом. Для того, что вы говорите, я думаю, что лучший подход - это первый из ответов @Alain. – jachguate

ответ

14

В Oracle, что является соответствующим типом данных или метода для , представляющей сетевые адреса, которые могут быть адреса IPv4 или IPv6

Есть два подхода:

  1. хранящих только.
  2. хранения обычного представления

Для хранения только. IPV4-адрес должен быть целым числом (достаточно 32 бита). Для IP V6, 128 бит, INTEGER (который похож на Number (38)) будет делать. Конечно, это хранение. Этот подход предполагает, что представление является вопросом для приложения.

Если вы берете противоположную стратегию хранения обычного представления, необходимо убедиться, что адреса IP V4 и IPV6 имеют только одно обычное (строковое) представление. Это хорошо известно для ipV4. Что касается IPV6, также существует стандартный формат.

Мои предпочтения касаются первой стратегии. В худшем случае вы можете использовать гибридный подход (хотя и некислотный) и хранить как двоичное, так и представление ascii бок о бок с «приоритетом» к двоичному значению.

Ни одна строка содержит как v4, так и v6 адреса, однако.

Стандартное представление IPV4-адреса в формате IPV6: ::ffff:192.0.2.128.

Я не знаю контекста, но я бы сохранил 2 столбца, один для IPV4, а другой для отдельного адреса ipV6.

Update
После хорошего замечания @ sleepyMonad, я хотел бы отметить, что вместо Номера типа данных предпочтительно использовать целочисленный тип данных, который будет счастливо вместить максимально возможный значение, которое может быть выражено с 128-битным целым числом «ff ... ff» (для чего потребуется десятичных цифр). 38 - наивысшая мощность десяти в диапазоне от 0 до 9, которая может быть закодирована на 128 бит, но все еще можно вставить максимальное значение без знака для 2 ** 128 - 1 (десятичный 340282366920938463463374607431768211455). Вот небольшой тест, иллюстрирующий эту возможность.

create table test (
    id integer primary key, 
    ipv6_address_bin INTEGER); 

-- Let's enter 2**128 - 1 in the nueric field 
insert into test (id, ipv6_address_bin) values (1, to_number ('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX')) ; 

-- retrieve it to make sure it's not "truncated". 
select to_char (ipv6_address_bin, 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX') from test where id = 1 ; 
-- yields 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF' 

select to_char (ipv6_address_bin) from test where id = 1 ; 
-- yields 340282366920938463463374607431768211455 

select LOG(2, ipv6_address_bin) from test where id = 1 ; 
-- yields 128 

select LOG(10, ipv6_address_bin) from test where id = 1 ; 
-- yields > 38 
+0

Определенно согласуются с отдельными столбцами для значений IPv4 и IPv6. –

+0

Не могли бы вы также ограничить столбцы v4 и v6, чтобы один и только один из них должен был быть NULL? (Что-то вроде 'CHECK ((src_v4 IS NULL и src_v6 не является NULL) или (src_v4 IS NOT NULL и src_v6 IS NULL))')? – pilcrow

+0

Все зависит от того, какие данные таблицы должны содержать. Предполагая, что IP-адрес не является ключевым (кажется очевидным из вашего вопроса), и вы отслеживаете пользователей, я бы оставил дверь открытой, потому что люди могли подключаться с разных IP-адресов. Если вместо этого ваша таблица отслеживает события соединения, а не пользователей (предположительно с отношением один к n), то да, потому что вы подключаетесь либо в IP-версии v4, либо в V6 (хотя, как я уже сказал, адреса IP-версии V4 имеют представление V6) , –

3

@Alain Pannetier (потому что я еще не могу комментировать): тип данных карты ANSI INTEGER на номер (38) в Oracle по http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/sql_elements001.htm#i54335. Ниже таблицы вы найдете информацию о том, что NUMBER обеспечивает только битовую точность 126 бит, что недостаточно для 128-битного IPv6-адреса. Максимальное значение может хранить штраф, но будут адреса, которые будут подключены к следующей нижней.

Внутренний цифровой формат: ROUND ((длина (p) + s)/2)) + 1 (http://download.oracle.com/docs/cd/B19306_01/server.102/b14220/datatype.htm#i16209).

Update: После возиться с вопросом снова Я теперь нашел решение, которое обеспечивает высокую производительность запросы к сети, которые содержат адреса IPv6: хранить адреса IPv6 и маску подсети в формате RAW (16) столбцов и сравнить их с помощью UTL_RAW.BIT_AND:

SELECT name, DECODE(UTL_RAW.BIT_AND('20010DB8000000000000000000000001', ipv6_mask), ipv6_net, 1, 0) 
FROM ip_net 
WHERE ipv6_net IS NOT NULL; 
1

документация Oracle делает состояние INTEGER является псевдонимом ЧИСЛО (38), но это, вероятно, опечатка, так как пункт выше него гласит:

НОМЕР (p, s) где: pi с точностью ... Oracle гарантирует переносимость номеров с точностью до 20 базовых 100 цифр, , что эквивалентно 39 или 40 десятичным разрядам в зависимости от позиции десятичной точки .

Таким образом, NUMBER может хранить от 39 до 40 цифр, а INTEGER, скорее всего, является псевдонимом NUMBER (максимальная точность) вместо NUMBER (38). Вот почему приведенный пример работает (и он работает, если вы меняете INTEGER на NUMBER).

+0

Как это ответить на вопрос? – wallyk

6

Хранить его в RAW.

RAW является массив с переменной длиной слова, так ....

  • просто относиться к IPv4 как массив байтов 4
  • и IPv6 как массив 16 байт

... и сохраните одну из них непосредственно в RAW (16).


RAW может быть проиндексирован, быть PK, UNIQUE или FOREIGN KEY, так что вы можете сделать все, что вы обычно могли с VARCHAR2 или INT/НОМЕР/DECIMAL, но с меньшим количеством преобразования и накладными расходами хранения.

Чтобы проиллюстрировать накладные расходы хранения INT над RAW, рассмотрим следующий пример:

CREATE TABLE IP_TABLE (
    ID INT PRIMARY KEY, 
    IP_RAW RAW(16), 
    IP_INT INT 
); 

INSERT INTO IP_TABLE (ID, IP_RAW, IP_INT) VALUES (
    1, 
    HEXTORAW('FFFFFFFF'), 
    TO_NUMBER('FFFFFFFF', 'XXXXXXXX') 
); 

INSERT INTO IP_TABLE (ID, IP_RAW, IP_INT) VALUES (
    2, 
    HEXTORAW('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF'), 
    TO_NUMBER('FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF', 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX') 
); 

SELECT VSIZE(IP_RAW), VSIZE(IP_INT), IP_TABLE.* FROM IP_TABLE; 

Результат (под Oracle 10.2):

table IP_TABLE created. 
1 rows inserted. 
1 rows inserted. 
VSIZE(IP_RAW)   VSIZE(IP_INT)   ID      IP_RAW       IP_INT     
---------------------- ---------------------- ---------------------- -------------------------------- ---------------------- 
4      6      1      FFFFFFFF       4294967295    
16      21      2      FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 340282366920938463463374607431768211455 
+0

+1 Мне нравится подход RAW. – pilcrow

1

можно также использовать пользовательский объект оракула.

SQL>set SERVEROUTPUT on 
SQL>drop table test; 

Table dropped. 

SQL>drop type body inaddr; 

Type body dropped. 

SQL>drop type inaddr; 

Type dropped. 

SQL>create type inaddr as object 
    2 (/* TODO enter attribute and method declarations here */ 
    3 A number(5), 
    4 B number(5), 
    5 C number(5), 
    6 D number(5), 
    7 E number(5), 
    8 F number(5), 
    9 G number(5), 
10 H NUMBER(5), 
11 MAP MEMBER FUNCTION display RETURN VARCHAR2, 
12 MEMBER FUNCTION toString(SELF IN INADDR , CONTRACT BOOLEAN DEFAULT TRUE) RETURN VARCHAR2, 
13 CONSTRUCTOR FUNCTION INADDR(SELF IN OUT NOCOPY INADDR, INADDRASSTRING VARCHAR2) RETURN SELF AS RESULT 
14 
15 ) NOT FINAL; 
16/

SP2-0816: Type created with compilation warnings 

SQL> 
SQL> 
SQL>CREATE TYPE BODY INADDR AS 
    2 
    3 MAP MEMBER FUNCTION display RETURN VARCHAR2 
    4 IS BEGIN 
    5 return tostring(FALSE); 
    6 END; 
    7 
    8 
    9 MEMBER FUNCTION TOSTRING(SELF IN INADDR , CONTRACT BOOLEAN DEFAULT TRUE) RETURN VARCHAR2 IS 
10 IP4 VARCHAR2(6) := 'FM990'; 
11 ip6 varchar2(6) := 'FM0XXX'; 
12 BEGIN 
13 IF CONTRACT THEN 
14 ip6 := 'FMXXXX'; 
15 end if; 
16 
17 IF CONTRACT AND A =0 AND B=0 AND C = 0 AND D=0 AND E =0 AND F = 65535 THEN --ipv4 
18  RETURN '::FFFF:'||TO_CHAR(TRUNC(G/256),'FM990.')||TO_CHAR(MOD(G,256),'FM990.')||TO_CHAR(TRUNC(H/256),'FM990.')||TO_CHAR(MOD(H,256),'FM990'); 
19 ELSE 
20  RETURN 
21 TO_CHAR(A,ip6)||':'|| 
22 TO_CHAR(B,IP6)||':'|| 
23 TO_CHAR(C,ip6)||':'|| 
24 TO_CHAR(D,ip6)||':'|| 
25 TO_CHAR(E,ip6)||':'|| 
26 TO_CHAR(F,ip6)||':'|| 
27 TO_CHAR(G,ip6)||':'|| 
28 TO_CHAR(H,ip6); 
29 end if; 
30 end; 
31 
32  CONSTRUCTOR FUNCTION inaddr(SELF IN OUT NOCOPY inaddr, inaddrasstring VARCHAR2) 
33         RETURN SELF AS RESULT IS 
34  begin 
35   if instr(inaddrasstring,'.') > 0 then 
36   --ip4 
37 null; 
38    a := 0; 
39    B := 0; 
40    C := 0; 
41    D := 0; 
42    E := 0; 
43    F := TO_NUMBER('FFFF', 'XXXX'); 
44    G := TO_NUMBER(TO_CHAR(TO_NUMBER(REGEXP_SUBSTR(INADDRASSTRING,'([0-9]{1,3}).',1,1,'i',1),'999'),'FM0X') 
45 ||TO_CHAR(TO_NUMBER(REGEXP_SUBSTR(INADDRASSTRING,'([0-9]{1,3}).',1,2,'i',1),'999'),'FM0X') 
46 ,'XXXX'); 
47    h := TO_NUMBER(TO_CHAR(TO_NUMBER(REGEXP_SUBSTR(INADDRASSTRING,'([0-9]{1,3}).',1,3,'i',1),'999'),'FM0X') 
48 ||TO_CHAR(TO_NUMBER(REGEXP_SUBSTR(INADDRASSTRING,'([0-9]{1,3})',1,4,'i',1),'999'),'FM0X') 
49 ,'XXXX'); 
50 
51   ELSIF instr(inaddrasstring,':') > 0 then 
52    --ip6 
53    a := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,1,'i',1),'XXXX'); 
54    b := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,2,'i',1),'XXXX'); 
55    c := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,3,'i',1),'XXXX'); 
56    d := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,4,'i',1),'XXXX'); 
57    E := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,5,'i',1),'XXXX'); 
58    f := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,6,'i',1),'XXXX'); 
59    g := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,7,'i',1),'XXXX'); 
60    H := TO_NUMBER(REGEXP_SUBSTR(inaddrasstring,'([0-9a-fA-F]{1,4})',1,8,'i',1),'XXXX'); 
61   end if; 
62 
63   RETURN; 
64  END; 
65 end; 
66/

Type body created. 

SQL> 
SQL>create table test 
    2 (id integer primary key, 
    3 address inaddr); 

Table created. 

SQL> 
SQL>select * from test; 

no rows selected 

SQL> 
SQL> 
SQL>insert into test values (1, INADDR('fe80:0000:0000:0000:0202:b3ff:fe1e:8329')); 

1 row created. 

SQL>INSERT INTO TEST VALUES (2, INADDR('192.0.2.128')); 

1 row created. 

SQL>insert into test values (3, INADDR('20.0.20.1')); 

1 row created. 

SQL>insert into test values (4, INADDR('fe80:0001:0002:0003:0202:b3ff:fe1e:8329')); 

1 row created. 

SQL>insert into test values (5, INADDR('fe80:0003:0002:0003:0202:b3ff:fe1e:8329')); 

1 row created. 

SQL>INSERT INTO TEST VALUES (6, INADDR('fe80:0003:0001:0003:0202:b3ff:fe1e:8329')); 

1 row created. 

SQL>INSERT INTO TEST VALUES (7, INADDR('fe80:0003:0001:0003:0202:b3ff:fe1e:8328')); 

1 row created. 

SQL>INSERT INTO TEST VALUES (8, INADDR('dead:beef:f00d:cafe:dea1:aced:b00b:1234')); 

1 row created. 

SQL> 
SQL>COLUMN INET_ADDRESS_SHORT FORMAT A40 
SQL>column inet_address_full format a40 
SQL> 
SQL>select t.address.toString() inet_address_short, t.address.display() inet_address_full 
    2 from test T 
    3 order by t.address ; 

INET_ADDRESS_SHORT      INET_ADDRESS_FULL 
---------------------------------------- ---------------------------------------- 
::FFFF:20.0.20.1       0000:0000:0000:0000:0000:FFFF:1400:1401 
::FFFF:192.0.2.128      0000:0000:0000:0000:0000:FFFF:C000:0280 
DEAD:BEEF:F00D:CAFE:DEA1:ACED:B00B:1234 DEAD:BEEF:F00D:CAFE:DEA1:ACED:B00B:1234 
FE80:0:0:0:202:B3FF:FE1E:8329   FE80:0000:0000:0000:0202:B3FF:FE1E:8329 
FE80:1:2:3:202:B3FF:FE1E:8329   FE80:0001:0002:0003:0202:B3FF:FE1E:8329 
FE80:3:1:3:202:B3FF:FE1E:8328   FE80:0003:0001:0003:0202:B3FF:FE1E:8328 
FE80:3:1:3:202:B3FF:FE1E:8329   FE80:0003:0001:0003:0202:B3FF:FE1E:8329 
FE80:3:2:3:202:B3FF:FE1E:8329   FE80:0003:0002:0003:0202:B3FF:FE1E:8329 

8 rows selected. 

SQL>spool off 

я просто поставить это вместе в последний час (и учил сам объектов одновременно), поэтому я уверен, что может быть улучшено.если я делать обновления я буду перепечатывать их здесь

1

Я предпочел бы хранить IP-адреса только в строке, в формате, возвращаемый sys_context («USERENV», «IP_Address»)

В референс из SYS_CONTEXT в 11g являются описал только длину возвращаемого значения по умолчанию как 256 байт и не описал размер возвращаемого значения для exacly 'IP_ADDRESS' контекста.

В документе Oracle Database and IPv6 Statement of Direction описано:

Oracle Database 11g Release 2 поддерживает стандарт IPv6 адреса обозначения, указанные RFC2732. 128-битный IP-адрес, как правило, представлен как 8 групп из 4 шестнадцатеричных цифр, с символом «:» в качестве разделителя групп . Ведущие нули в каждой группе удаляются. Для пример 1080: 0: 0: 0: 8: 800: 200C: 417A будет действительным адресом IPv6. Один или более последовательные нулевые поля могут быть дополнительно сжаты с помощью разделителя «::». Например, 1080 :: 8: 800: 200C: 417A.

Из этого примечания я предпочитаю, чтобы сделать столбец IP_Address varchar2 (39), чтобы хранить 8 группы по 4 цифры и 7 разделителей между этой группой.