2011-02-09 32 views
14

Trong Oracle, loại dữ liệu hoặc kỹ thuật thích hợp để đại diện cho địa chỉ mạng, địa chỉ nào có thể là IPv4 hoặc IPv6?Đại diện cho địa chỉ IPv4/IPv6 trong Oracle

Thông tin cơ bản: Tôi đang chuyển đổi hoạt động mạng ghi bảng, được xây dựng bằng cách sử dụng PostgreSQL inet data type để giữ cả địa chỉ v4 và v6 trong cùng một bảng.

Tuy nhiên, không có hàng nào chứa cả địa chỉ v4 và v6. (Tức là, một kỷ lục là một trong hai từ đống v4 của máy, hoặc v6 chồng của máy.)

+1

Bạn có tạo tổng hợp/tìm kiếm trên bảng đó theo địa chỉ IP không? bạn mong đợi có bao nhiêu hàng trong 1 năm? – jachguate

+0

Tổng hợp và tìm kiếm theo IP, có. Số hàng, có thể là hàng trăm triệu. (Bạn có khuyến nghị nhỏ/trung bình/lớn không?) – pilcrow

+0

hãy sử dụng ký hiệu @jachguate trong nhận xét của bạn nếu bạn muốn tôi nhận thông báo về nó. Đối với những gì bạn nói, tôi nghĩ cách tiếp cận tốt hơn là câu trả lời đầu tiên của @Alain. – jachguate

Trả lời

14

Trong Oracle, những gì là loại dữ liệu thích hợp hoặc kỹ thuật cho đại diện cho các địa chỉ mạng, mà địa chỉ có thể IPv4 hay IPv6

có hai phương pháp:

  1. chỉ lưu trữ.
  2. lưu trữ đại diện thông thường

Chỉ để lưu trữ. Địa chỉ IPV4 phải là số nguyên (32 bit là đủ). Đối với IP V6, 128 bit, INTEGER (tương tự với Số (38)) sẽ thực hiện. Tất nhiên, đó là lưu trữ. Cách tiếp cận đó đưa ra quan điểm rằng biểu diễn là một vấn đề cho ứng dụng.

Nếu một chiến lược ngược lại, lưu trữ biểu diễn thông thường, người ta cần đảm bảo rằng địa chỉ IP V4 và IPV6 chỉ có một biểu diễn (chuỗi) thông thường. Nó nổi tiếng với ipV4. Đối với IPV6, đó cũng là một định dạng chuẩn.

Tùy chọn của tôi đi đến chiến lược đầu tiên. Trong trường hợp xấu nhất, bạn có thể áp dụng một phương pháp lai (không axit) và lưu trữ cả nhị phân lẫn đại diện ascii cạnh nhau với "ưu tiên" cho giá trị nhị phân.

Không có hàng nào chứa cả địa chỉ v4 và v6 .

Biểu diễn tiêu chuẩn của địa chỉ IPV4 ở định dạng IPV6 là: ::ffff:192.0.2.128.

Tôi không biết ngữ cảnh nhưng tôi tuy nhiên sẽ đặt trước 2 cột, một cho IPV4 và một cho địa chỉ ipV6 riêng biệt.

Cập nhật
Sau một lời nhận xét tốt bởi @ sleepyMonad, tôi muốn chỉ ra rằng thay vì Number dữ liệu gõ nó là thích hợp hơn để sử dụng các kiểu dữ liệu INTEGER, mà hạnh phúc sẽ thích ứng với khả năng cao nhất giá trị có thể được biểu diễn bằng số nguyên 128 bit 'ff ... ff' (cần số chữ số thập phân). 38 là công suất cao nhất trong số 10 khác nhau, từ 0 đến 9 có thể được mã hóa trên 128 bit nhưng vẫn có thể chèn giá trị chưa được gán tối đa cho 2 ** 128 - 1 (thập phân 340282366920938463463374607431768211455). Đây là một thử nghiệm nhỏ để minh họa cho khả năng này.

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

Chắc chắn đồng ý với các cột riêng biệt cho các giá trị IPv4 và IPv6. –

+0

Bạn cũng sẽ hạn chế các cột v4 và v6 sao cho một và chỉ một trong số chúng phải là NULL? (Giống như 'CHECK ((src_v4 IS NULL và src_v6 IS NOT NULL) hoặc (src_v4 IS NOT NULL và src_v6 IS NULL)))? – pilcrow

+0

Tất cả đều phụ thuộc vào dữ liệu của bảng. Giả sử địa chỉ IP không phải là chìa khóa (có vẻ hiển nhiên từ câu hỏi của bạn), và bạn đang theo dõi người dùng, sau đó tôi sẽ giữ cửa mở vì mọi người có thể kết nối từ nhiều địa chỉ IP khác nhau. Nếu thay vào đó, bảng của bạn theo dõi các sự kiện kết nối thay vì người dùng (có lẽ với mối quan hệ một đến n), thì có, bởi vì bạn kết nối với IP v4 hoặc V6 (mặc dù, như tôi đã nói, địa chỉ IP v4 có biểu diễn V6) . –

3

@Alain Pannetier (vì tôi không thể bình luận nào): Các bản đồ datatype ANSI INTEGER NUMBER (38) trong Oracle theo http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/sql_elements001.htm#i54335. Bên dưới bảng bạn tìm thấy thông tin mà NUMBER chỉ cung cấp độ chính xác nhị phân 126 bit, không đủ cho địa chỉ IPv6 128 bit. Giá trị tối đa có thể lưu trữ tốt nhưng sẽ có địa chỉ được chuyển sang giá trị thấp hơn kế tiếp.

Định dạng số bên trong là ROUND ((chiều dài (p) + s)/2)) + 1 (http://download.oracle.com/docs/cd/B19306_01/server.102/b14220/datatype.htm#i16209).

Cập nhật: Sau khi giải quyết vấn đề một lần nữa, tôi đã tìm thấy giải pháp cho phép truy vấn hiệu suất cao của mạng có chứa địa chỉ IPv6: lưu trữ địa chỉ IPv6 và mặt nạ mạng con trong cột RAW (16) và so sánh họ sử dụng 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

các tài liệu Oracle không INTEGER nhà nước là một bí danh để NUMBER (38), nhưng điều đó có lẽ là một lỗi đánh máy, vì đoạn trên nó khẳng định:

NUMBER (p, s) trong đó: pi s độ chính xác ... Oracle đảm bảo khả năng di chuyển của các con số với độ chính xác lên đến 20 chữ số cơ bản-100, tương đương với 39 hoặc 40 chữ số thập phân tùy thuộc vào vị trí của dấu thập phân.

Vì vậy, NUMBER có thể lưu trữ 39 đến 40 chữ số và INTEGER có thể là bí danh cho NUMBER (độ chính xác tối đa) thay vì NUMBER (38). Có lý do tại sao ví dụ được cung cấp các công trình (và nó hoạt động nếu bạn thay đổi INTEGER thành NUMBER).

+0

Làm cách nào để trả lời câu hỏi? – wallyk

6

Lưu trữ ở dạng RAW.

RAW có thể thay đổi độ dài mảng byte, vì vậy ....

  • chỉ đối xử với IPv4 như một mảng của 4 byte
  • và IPv6 như là một mảng của 16 byte

... và lưu trữ một trong số chúng trực tiếp trong RAW (16).


RAW có thể được lập chỉ mục, là một PK, UNIQUE KEY hoặc NƯỚC NGOÀI, vì vậy bạn có thể làm bất cứ điều gì bạn thường có thể với VARCHAR2 hoặc INT/NUMBER/thập phân, nhưng với ít chuyển đổi và chi phí lưu trữ.

Để minh họa cho overhead lưu trữ của INT trên RAW, hãy xem xét ví dụ sau:

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; 

Kết quả (dưới 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 Tôi thích phương pháp RAW. – pilcrow

1

bạn cũng có thể sử dụng một đối tượng tùy chỉnh oracle.

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 

tôi chỉ đặt điều này cùng nhau trong giờ trước (và tự dạy các vật thể cùng một lúc) để đảm bảo rằng nó có thể được cải thiện.nếu tôi thực hiện cập nhật tôi sẽ repost chúng ở đây

1

Tôi muốn lưu trữ địa chỉ IP chỉ trong chuỗi, ở định dạng, được trả về bởi SYS_CONTEXT ('Userenv', 'IP_ADDRESS')

Trong refference của SYS_CONTEXT trong 11g là chỉ mô tả chiều dài giá trị trả về mặc định là 256 byte và không mô tả kích thước giá trị trả lại cho ngữ cảnh 'IP_ADDRESS' ngoại lệ.

Trong tài liệu Oracle Database and IPv6 Statement of Direction mô tả:

Oracle Database 11g Release 2 hỗ trợ các địa chỉ IPv6 chuẩn ký hiệu theo quy định của RFC2732. Địa chỉ IP 128 bit thường là được biểu thị dưới dạng 8 nhóm gồm 4 chữ số thập phân, với ký hiệu “:” là dấu tách nhóm . Các số 0 đứng đầu trong mỗi nhóm sẽ bị xóa. Ví dụ: , 1080: 0: 0: 0: 8: 800: 200C: 417A sẽ là địa chỉ IPv6 hợp lệ. Một hoặc nhiều trường số không liên tiếp hơn có thể được nén tùy chọn bằng dấu phân cách “::”. Ví dụ: 1080 :: 8: 800: 200C: 417A.

Từ ghi chú này tôi thích để làm cho cột IP_ADDRESS varchar2 (39) để cho phép lưu trữ 8 nhóm 4 chữ số và 7 dải phân cách giữa các nhóm này.

Các vấn đề liên quan