2009-07-13 42 views
16

Tôi đang cố gắng lưu trữ địa chỉ IPv6 trong MySQL 5.0 một cách hiệu quả. Tôi đã đọc các câu hỏi khác liên quan đến điều này, such as this one. Tác giả của câu hỏi đó cuối cùng đã chọn cho hai trường BIGINT. Tìm kiếm của tôi cũng đã bật lên một cơ chế thường được sử dụng khác: Sử dụng DECIMAL (39,0) để lưu trữ địa chỉ IPv6. Tôi có hai câu hỏi về điều đó.Làm thế nào để chuyển đổi IPv6 từ nhị phân để lưu trữ trong MySQL

  1. Ưu điểm và nhược điểm của việc sử dụng DECIMAL (39,0) so với các phương pháp khác như 2 * BIGINT là gì?
  2. Làm cách nào để chuyển đổi (bằng PHP) từ định dạng nhị phân được trả về bởi inet_pton() thành định dạng chuỗi thập phân có thể sử dụng bởi MySQL và cách chuyển đổi lại để tôi có thể in đẹp bằng inet_ntop()?
+0

Sự cố khi sử dụng trường VARCHAR là gì? – shadowhand

+1

Kết hợp dải IP dễ dàng cho một. –

Trả lời

19

Dưới đây là các chức năng tôi hiện sử dụng để chuyển đổi địa chỉ IP từ và sang định dạng DECIMAL (39,0). Chúng được đặt tên inet_ptod và inet_dtop cho "presentation-to-decimal" và "decimal-to-presentation". Nó cần hỗ trợ IPv6 và bcmath trong PHP.

/** 
* Convert an IP address from presentation to decimal(39,0) format suitable for storage in MySQL 
* 
* @param string $ip_address An IP address in IPv4, IPv6 or decimal notation 
* @return string The IP address in decimal notation 
*/ 
function inet_ptod($ip_address) 
{ 
    // IPv4 address 
    if (strpos($ip_address, ':') === false && strpos($ip_address, '.') !== false) { 
     $ip_address = '::' . $ip_address; 
    } 

    // IPv6 address 
    if (strpos($ip_address, ':') !== false) { 
     $network = inet_pton($ip_address); 
     $parts = unpack('N*', $network); 

     foreach ($parts as &$part) { 
      if ($part < 0) { 
       $part = bcadd((string) $part, '4294967296'); 
      } 

      if (!is_string($part)) { 
       $part = (string) $part; 
      } 
     } 

     $decimal = $parts[4]; 
     $decimal = bcadd($decimal, bcmul($parts[3], '4294967296')); 
     $decimal = bcadd($decimal, bcmul($parts[2], '18446744073709551616')); 
     $decimal = bcadd($decimal, bcmul($parts[1], '79228162514264337593543950336')); 

     return $decimal; 
    } 

    // Decimal address 
    return $ip_address; 
} 

/** 
* Convert an IP address from decimal format to presentation format 
* 
* @param string $decimal An IP address in IPv4, IPv6 or decimal notation 
* @return string The IP address in presentation format 
*/ 
function inet_dtop($decimal) 
{ 
    // IPv4 or IPv6 format 
    if (strpos($decimal, ':') !== false || strpos($decimal, '.') !== false) { 
     return $decimal; 
    } 

    // Decimal format 
    $parts = array(); 
    $parts[1] = bcdiv($decimal, '79228162514264337593543950336', 0); 
    $decimal = bcsub($decimal, bcmul($parts[1], '79228162514264337593543950336')); 
    $parts[2] = bcdiv($decimal, '18446744073709551616', 0); 
    $decimal = bcsub($decimal, bcmul($parts[2], '18446744073709551616')); 
    $parts[3] = bcdiv($decimal, '4294967296', 0); 
    $decimal = bcsub($decimal, bcmul($parts[3], '4294967296')); 
    $parts[4] = $decimal; 

    foreach ($parts as &$part) { 
     if (bccomp($part, '2147483647') == 1) { 
      $part = bcsub($part, '4294967296'); 
     } 

     $part = (int) $part; 
    } 

    $network = pack('N4', $parts[1], $parts[2], $parts[3], $parts[4]); 
    $ip_address = inet_ntop($network); 

    // Turn IPv6 to IPv4 if it's IPv4 
    if (preg_match('/^::\d+.\d+.\d+.\d+$/', $ip_address)) { 
     return substr($ip_address, 2); 
    } 

    return $ip_address; 
} 
+21

Boy. Điều tốt là không có bất kỳ ma thuật tùy ý nào trong mã đó. –

+5

Chúng không phải là tùy ý hoặc ma thuật nếu bạn biết quyền hạn của mình là 2 ;-) –

+1

+1 Tôi ước có thể bỏ phiếu cho bạn 500 lần để đăng các chức năng đó! Cảm ơn. –

30

Chúng tôi đã đi cho một cột VARBINARY(16) thay vào đó và sử dụng inet_pton()inet_ntop() để làm chuyển đổi:

https://bitbucket.org/skion/mysql-udf-ipv6

Các chức năng có thể được nạp vào một máy chủ MySQL chạy và sẽ cung cấp cho bạn INET6_NTOPINET6_PTON trong SQL, giống như các hàm INET_NTOAINET_ATON quen thuộc cho IPv4.

Chỉnh sửa: Hiện có các chức năng tương thích trong MySQL ngay bây giờ, chỉ với differentnames. Chỉ sử dụng ở trên nếu bạn đang ở trên trước 5,6 MySQL và đang tìm kiếm một con đường nâng cấp tương lai thuận tiện.

+0

Rất đẹp. Không thể sử dụng trên lưu trữ được chia sẻ tiêu chuẩn, nhưng chắc chắn là một tùy chọn nếu bạn chạy máy chủ của riêng mình. –

+0

Tôi cũng sẽ xác nhận cho phương pháp này. Rõ ràng, nó không hoạt động trong nhiều môi trường lưu trữ được chia sẻ, nhưng việc lưu trữ địa chỉ theo cách này cũng cho phép MUCH kết hợp hiệu quả hơn (ví dụ, dựa trên netmask) so với trong PHP gốc. –

+0

Chỉ cần lưu ý rằng mô-đun ở trên đã được thực hiện tương thích với các chức năng IPv6 mới trong vanilla MySQL v5.6.3: INET6_ATON() và INET6_NTOA(). Vì vậy, bạn có thể sử dụng mô-đun ở trên cho các bản cài đặt trước 5.6 của bạn và chỉ tải xuống một lần sau bài đăng 5.6; mà không thay đổi truy vấn của bạn. –

0

thập phân (39)

Ưu điểm:

  • trình với những hoạt động số học cơ bản (chẳng hạn như + và -).
  • Làm việc với lập chỉ mục cơ bản (chính xác hoặc dải ô).
  • Định dạng hiển thị thân thiện.

Nhược điểm:

  • thể chấp nhận ra giá trị của phạm vi cho IPv6.
  • Không phải là cơ chế lưu trữ rất hiệu quả.
  • Có thể gây nhầm lẫn về việc toán tử hoặc chức năng nào hoạt động và không hoạt động.

Binary (16) ...

Ưu điểm:

  • định dạng hiệu quả nhất cho đại diện chính xác.
  • Làm việc với lập chỉ mục cơ bản (chính xác và phạm vi).
  • Làm việc với lập chỉ mục tiền tố cho tiền tố là bội số của 8 bit.
  • Chỉ lưu trữ các giá trị IPv6 hợp lệ (mặc dù không đảm bảo địa chỉ hợp lệ).
  • MySQL trong các phiên bản sau có các chức năng hỗ trợ chuyển đổi cho định dạng này đến và từ đại diện IPv6 (nhưng không phải 4in6).

Nhược điểm:

  • Không thân thiện để trưng bày.
  • Không thân thiện với các toán tử hoặc chức năng dành cho số.

Binary (39) ...

này là dành cho địa chỉ đầy đủ (sử dụng ngay cả đối với hexdec 4in6). Cũng có thể được ascii chứ không phải là nhị phân.

Ưu điểm:

  • Nhân có thể đọc được (nếu bạn có thể gọi IPv6 đó).
  • Hỗ trợ lập chỉ mục cơ bản (chính xác và phạm vi).
  • Hỗ trợ lập chỉ mục tiền tố cho nhiều trong số 4 bit.
  • Tương thích trực tiếp IPv6. Không cần chuyển đổi.

Nhược điểm:

  • Không làm việc tốt với bất kỳ chức năng toán học hoặc nhà khai thác.
  • Bộ nhớ không hiệu quả nhất.
  • Có thể cho phép các đại diện không hợp lệ.

điều lạ:

  • Gets phức tạp nếu bạn muốn những thứ như trường hợp nhạy cảm.
  • IPv6 có các định dạng hiển thị khác mặc dù sử dụng các định dạng này làm cho nhiều phức tạp hơn, chẳng hạn như bạn có thể có hai biểu diễn của cùng một địa chỉ hoặc bạn mất các lần tra cứu phạm vi. Thậm chí có thể kết thúc có để làm cho nó 45 byte dài hoặc sử dụng varchar/varbinary.
  • Các khác biệt về điều này có thể hỗ trợ bảo quản địa chỉ như đã nhận ban đầu. Điều đó hiếm khi được mong muốn nhưng khi bạn mất rất nhiều lợi ích.
  • Xóa bộ tách có định dạng đầy đủ và chỉ lưu trữ dưới dạng chuỗi hex cho ít phức tạp hơn và hiệu quả hơn một chút. Bạn có thể thực hiện điều này một cách dài nếu lập chỉ mục tiền tố là quan trọng (BINARY (128)).

bigint UNSIGNED * 2

Ưu điểm:

  • trình với những hoạt động toán học và chức năng với sự báo trước của việc phải làm thêm những điều xung quanh nó là hai cột.
  • Hiệu quả nhưng một lần nữa với báo trước rằng đó là hai cột sẽ thêm một số chi phí.
  • Làm việc với các chỉ mục cơ bản (chính xác, phạm vi).
  • Làm việc với chỉ mục tiền tố khi tiền tố là 64 bit.
  • Hiển thị định dạng thân thiện.

Nhược điểm:

  • Hai cột làm cho nó phi nguyên tử và phương tiện tăng gấp đôi rất nhiều hoạt động trên đó.

điều lạ:

  • Nhiều ngôn ngữ và hệ thống hiện đại cung cấp cho ints 64 bit nhưng không unsigned. Đã ký là có vấn đề. Các số âm hiện diện thấp hơn số dương nhưng chuỗi bit của chúng thực sự cao hơn. Vì lý do này, nó là phổ biến thay vì sử dụng 4 * INT UNSIGNED.
  • Tương tự, mọi người có thể phá vỡ nó để lập chỉ mục tiền tố và bạn có thể đi ít nhất là 8 bit (TINYINT UNSIGNED). Một số người cũng có thể sử dụng loại BIT (1) để lập chỉ mục tiền tố đầy đủ, giả sử chỉ số MySQL đồng nghĩa với các loại bit đúng cách. Một lần nữa tương tự với bốn cột một số hoạt động đòi hỏi những thứ như mang từ ngày này sang một số khác là trớ trêu thay dễ dàng hơn do bit slack trong quá trình tính toán (giá trị trung gian trong tính toán vẫn có thể là 64 bit).

Tóm tắt

Mọi người sẽ sử dụng các định dạng khác nhau vì những lý do khác nhau. Khả năng tương thích ngược có thể là một lý do và điều đó phụ thuộc vào những gì đã được thực hiện cho IPv4. Những người khác phụ thuộc vào cách các địa chỉ đang được sử dụng và tối ưu hóa xung quanh đó. Bạn có thể thấy nhiều cách tiếp cận đang được sử dụng.

B16 là phương pháp mặc định tốt vì đây là phương pháp hiệu quả nhất và không phức tạp.

Đối với chuyển đổi trong PHP bạn có thể làm cho họ bằng tay nếu bạn nghiên cứu:

  • gmp hoặc bcmath
  • xử lý số lượng và Bitwise nhà khai thác của PHP, hãy đặc biệt chú ý các hạn chế về int hoặc trôi nổi cũng như chức năng phụ thuộc vào chúng có vẻ khác hữu ích
  • Định dạng IPv6
  • gói/giải nén, bin2hex/hex2bin.

Tôi khuyên bạn nên sử dụng thư viện chung để xử lý các định dạng hiển thị khác nhau của IPv6.

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