문제

나는 효율적인 방법으로 MySQL 5.0에 IPv6 주소를 저장하려고 합니다.이와 관련된 다른 질문을 읽었습니다. 이것과 같은.해당 질문의 작성자는 결국 두 개의 BIGINT 필드를 선택했습니다.내 검색을 통해 자주 사용되는 또 다른 메커니즘도 나타났습니다.DECIMAL(39,0)을 사용하여 IPv6 주소를 저장합니다.그것에 대해 두 가지 질문이 있습니다.

  1. 2*BIGINT와 같은 다른 방법에 비해 DECIMAL(39,0)을 사용할 때의 장점과 단점은 무엇입니까?
  2. 반환된 바이너리 형식에서 PHP로 어떻게 변환합니까? inet_pton() MySQL에서 사용할 수 있는 10진수 문자열 형식으로 변환하고 inet_ntop()으로 예쁘게 인쇄할 수 있도록 어떻게 다시 변환합니까?
도움이 되었습니까?

해결책 2

다음은 IP 주소를 10 진수 (39,0) 형식으로 변환하는 데 사용하는 기능입니다. 그것들은 "프리젠 테이션------프레젠테이션"을 위해 inet_ptod 및 inet_dtop으로 명명되었습니다. PHP에서 IPv6 및 BCMath 지원이 필요합니다.

/**
 * 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;
}

다른 팁

우리는 갔다 VARBINARY(16) 대신 칼럼을 사용합니다 inet_pton() 그리고 inet_ntop() 전환을하기 위해 :

https://github.com/skion/mysql-udf-ipv6

기능은 실행중인 MySQL 서버에로드 할 수 있으며 INET6_NTOP 그리고 INET6_PTON SQL에서 친숙한 것과 마찬가지로 INET_NTOA 그리고 INET_ATON IPv4의 기능.

편집 : MySQL에는 현재와 호환되는 기능이 있습니다. 다른 이름. 5.6 Pre-5.6 MySQL에 있고 편리한 미래 업그레이드 경로를 찾고있는 경우에만 사용하십시오.

십진수(39)

장점:

  • 기본 산술 연산자(예: + 및 -)와 함께 작동합니다.
  • 기본 인덱싱(정확 또는 범위)과 함께 작동합니다.
  • 형식은 표시 친화적입니다.

단점:

  • IPv6에 대해 범위를 벗어난 값을 허용할 수 있습니다.
  • 매우 효율적인 저장 메커니즘은 아닙니다.
  • 어떤 수학 연산자나 함수가 작동하고 작동하지 않는지 혼동을 일으킬 수 있습니다.

바이너리(16)...

장점:

  • 정확한 표현을 위한 가장 효율적인 형식입니다.
  • 기본 인덱싱(정확 및 범위)과 함께 작동합니다.
  • 8비트의 배수인 접두사에 대한 접두사 인덱싱과 함께 작동합니다.
  • 유효한 IPv6 값만 저장합니다(유효한 주소 지정을 보장하지는 않지만).
  • 이후 버전의 MySQL에는 이 형식과 IPv6 표현 간의 변환을 지원하는 기능이 있습니다(4in6은 아님).

단점:

  • 전시용으로 적합하지 않습니다.
  • 숫자를 위한 연산자나 함수에 익숙하지 않습니다.

바이너리(39)...

이는 전체 주소용입니다(4in6의 경우에도 hexdec 사용).바이너리가 아닌 ASCII일 수도 있습니다.

장점:

  • 사람이 읽을 수 있습니다(IPv6를 호출할 수 있는 경우).
  • 기본 인덱싱(정확 및 범위)을 지원합니다.
  • 4비트 배수에 대한 접두사 인덱싱을 지원합니다.
  • IPv6와 직접 호환됩니다.변환이 필요하지 않습니다.

단점:

  • 어떤 수학 함수나 연산자와도 잘 작동하지 않습니다.
  • 가장 비효율적인 스토리지.
  • 잘못된 표현을 허용할 수 있습니다.

특이한 점:

  • 대소 문자를 구분하지 않는 등의 기능을 원하면 복잡해집니다.
  • IPv6에는 다른 표시 형식이 있지만 이를 사용하면 동일한 주소에 대한 두 가지 표현이 있거나 범위 조회가 손실되는 등 더 복잡해집니다.심지어 45바이트 길이로 만들거나 varchar/varbinary를 사용해야 할 수도 있습니다.
  • 이것의 변형은 원래 수신된 주소를 보존하는 것을 지원할 수 있습니다.이는 거의 바람직하지 않을 수 있지만 그렇게 되면 많은 이점을 잃게 됩니다.
  • 전체 형식으로 구분 기호를 제거하고 16진수 문자열로 저장하면 번거로움이 덜하고 효율성이 조금 더 높아집니다.접두사 인덱싱이 중요한 경우 이 방법을 사용할 수 있습니다(BINARY(128)).

서명되지 않은 빅트 * 2

장점:

  • 두 개의 열이 있는 경우 추가 작업을 수행해야 한다는 주의 사항과 함께 수학 연산자 및 함수와 함께 작동합니다.
  • 효율적이지만 두 개의 열이 있으면 약간의 오버헤드가 추가된다는 점에 주의하세요.
  • 기본 인덱스(정확도, 범위)와 함께 작동합니다.
  • 접두사가 64비트인 경우 접두사 인덱스와 함께 작동합니다.
  • 친숙한 형식을 표시합니다.

단점:

  • 두 개의 열은 비원자적이며 이에 대한 많은 작업이 두 배로 늘어남을 의미합니다.

특이한 점:

  • 많은 현대 언어와 시스템은 64비트 정수를 제공하지만 unsigned는 제공하지 않습니다.서명이 문제입니다.음수는 양수보다 낮지만 해당 비트 시퀀스는 실제로 더 높습니다.이러한 이유로 대신 4 * INT UNSIGNED를 사용하는 것이 일반적입니다.
  • 마찬가지로 사람들은 접두사 인덱싱을 위해 이를 분할할 수 있으며 최소한 8비트(TINYINT UNSIGNED)까지 갈 수 있습니다.일부 사람들은 MySQL이 비트 유형에 대한 인덱스를 적절하게 사용한다고 가정하고 전체 접두어 인덱싱을 위해 BIT(1) 유형을 사용할 수도 있습니다.
  • 마찬가지로 4개 열의 경우 계산 중 비트 여유가 있기 때문에 한 열에서 다른 열로 전달하는 것과 같은 일부 작업이 아이러니하게도 더 쉽습니다(계산의 중간 값은 여전히 ​​64비트일 수 있음).

요약

사람들은 다양한 이유로 다양한 형식을 사용합니다.이전 버전과의 호환성이 한 가지 이유일 수 있으며 이는 IPv4에 대해 수행된 작업에 따라 달라집니다.다른 것들은 주소가 어떻게 사용되는지와 그에 대한 최적화에 따라 달라집니다.두 가지 이상의 접근 방식이 사용되는 것을 볼 수 있습니다.

B16은 가장 효율적이고 번거로움이 없기 때문에 좋은 기본 접근 방식입니다.

PHP 변환의 경우 다음을 조사하면 직접 수행할 수 있습니다.

  • gmp 또는 bcmath
  • PHP의 숫자 처리 및 비트 연산자, 특히 int 또는 float에 대한 제한 사항과 유용해 보일 수 있는 이에 의존하는 함수에 유의하세요.
  • IPv6 형식
  • 포장/포장 풀기, bin2hex/hex2bin.

그러나 IPv6의 다양한 표시 형식을 처리하려면 공용 라이브러리를 사용하는 것이 좋습니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top