---
# System prepended metadata

title: Go IP如何储存进去MYSQL
tags: [mysql, ip, Golang]

---

# Go IP如何储存进去MYSQL
---

```javascript=
要了解IP是如何储存的进去MYSQL数据库的，需要从这个几个方面下手:

1. IP与数值类型的转换

2. IP地址跟MYSQL的数据存储之间的关系

3. MYSQL数据如何处理IP类型
```

## 1. IP与数值类型的转换
:::success
```go=
package main

import (
	"fmt"
	"net"
	"reflect"

	"github.com/thinkeridea/go-extend/exnet"
)

func main() {
	ip := "192.168.1.1"

	n, _ := exnet.IPString2Long(ip) // 以数值类型存储进去数据库
	s, _ := exnet.Long2IPString(n) // 以ip字符串形式读取数据

    // 192*256*256*256+168*256*256+1*256+1 = 3232235777
	fmt.Println(n, s == ip) // 3232235777 true

	Ip1 := net.ParseIP(ip) // 会得到一个16字节的byte，主要为了兼容ipv6
	n, _ = exnet.IP2Long(Ip1) // 以数值类型存储进去数据库

	Ip2, _ := exnet.Long2IP(n) // 以ip字符串形式读取数据
    
    fmt.Println(Ip1) // 192.168.1.1
	fmt.Println(n) // 3232235777
	fmt.Println(Ip1) // 192.168.1.1

	fmt.Println(n, reflect.DeepEqual(Ip1[12:], Ip2)) // 3232235777 true
}

// ParseIP，IPString2Long，Long2IPString，IP2Long，Long2IP 源码函数

// ParseIP 把ip字符串转为IP类型
type IP []byte

func ParseIP(s string) IP {
	for i := 0; i < len(s); i++ {
		switch s[i] {
		case '.': // ipv4地址
			return parseIPv4(s)
		case ':': // ipv6地址
			return parseIPv6(s)
		}
	}
	return nil
}

// IPString2Long 把ip字符串转为数值
func IPString2Long(ip string) (uint, error) {
	b := net.ParseIP(ip).To4()
	if b == nil {
		return 0, errors.New("invalid ipv4 format")
	}

	return uint(b[3]) | uint(b[2])<<8 | uint(b[1])<<16 | uint(b[0])<<24, nil
}

// Long2IPString 把数值转为ip字符串
func Long2IPString(i uint) (string, error) {
	if i > math.MaxUint32 {
		return "", errors.New("beyond the scope of ipv4")
	}

	ip := make(net.IP, net.IPv4len)
	ip[0] = byte(i >> 24)
	ip[1] = byte(i >> 16)
	ip[2] = byte(i >> 8)
	ip[3] = byte(i)

	return ip.String(), nil
}

// IP2Long 把net.IP转为数值
func IP2Long(ip net.IP) (uint, error) {
	b := ip.To4()
	if b == nil {
		return 0, errors.New("invalid ipv4 format")
	}

	return uint(b[3]) | uint(b[2])<<8 | uint(b[1])<<16 | uint(b[0])<<24, nil
}

// Long2IP 把数值转为net.IP
func Long2IP(i uint) (net.IP, error) {
	if i > math.MaxUint32 {
		return nil, errors.New("beyond the scope of ipv4")
	}

	ip := make(net.IP, net.IPv4len)
	ip[0] = byte(i >> 24)
	ip[1] = byte(i >> 16)
	ip[2] = byte(i >> 8)
	ip[3] = byte(i)

	return ip, nil
}
```
:::

## 2. IP地址跟MYSQL的数据存储之间的关系

```javascript=
ipv4大家都知道是从0.0.0.0到255.255.255.255;

然而使用IP2Long函数转换后并不是我们想象中的0到4294967295，

而是0.0.0.0-127.255.255.255,转换后为0到2147483647；

128.0.0.0-255.255.255.255转换后为-2147483647到-1；

IP地址转换成int类型正好是int的有符号取整范围，为什么呢？

MySQL中int类型占4个字节，每个字节占8位 // 0-255 0-127, -127--1

总大小为(2^8-1)*(2^8-1)*(2^8-1)*(2^8-1)=255*255*255*255，是不是和IP地址的范围很类似；
```

## 3. IP地址跟MYSQL的数据存储之间的关系

- 简要说明
```javascript=
在Mysql中并没有提供针对IP地址存储的数据格式

在开发中我们可以选择使用char、varchar、int来存储

根据mysql字段类型选择的规则：

1. 字段类型定义使用最合适（最小）、最简单的数据类型，优先选择使用int类型来存储

2. int在逻辑运算上也要比char、varchar更快
```

- int类型存储IP地址

```javascript=

在Mysql中提供了两个函数，用来把IP地址与数字类型的相互转化

这种算法(inet_aton())其实借用了国际上对各国IP地址的区分中使用的ip number。

A.B.C.D 的ip number是：

A * 256^3 + B * 256^2 + C * 256 + D
```

```javascript=
inet_aton()：把IP地址转化为数字
```

```sql=
mysql> select inet_aton('192.168.1.1');
+------------------------------+
| inet_aton('192.168.1.1')    |
+------------------------------+
|                   3232235777|
+------------------------------+
1 row in set (0.00 sec)
```

```javascript=
inet_ntoa()：把数字转化成IP地址
```

```sql=
mysql> select inet_ntoa(4294967295);
+-----------------------+
| inet_ntoa(3232235777) |
+-----------------------+
| 192.168.1.1       |
+-----------------------+
1 row in set (0.00 sec)

```

```javascript=
当前很多应用都适用字符串char(15)来存储IP地址（占用16个字节）；

利用inet_aton()和inet_ntoa()函数，来存储IP地址；

适用unsigned int 就可以满足需求，不需要使用bigint，只需要4个字节；

也节省存储空间，同时效率也高很多。

```

```javascript=
IPV4地址由4个组数字组成，每组数字之间以.分隔，每组数字的取值范围是0-255。
[0-99,100-199,200-249,250-255]
IPV4必须满足以下四条规则：
1、任何一个1位或2位数字，即0-99； 			正则\d{1,2}
2、任何一个以1开头的3位数字，即100-199； 					 正则1\d{2}
3、任何一个以2开头、第2位数字是0-4之间的3位数字，即200-249；   正则2[0-4]\d
4、任何一个以25开头，第3位数字在0-5之间的3位数字，即250-255。	正则25[0-5]

组合起来，就得到一个匹配0-255数字的正则表达式了：(\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5])
IPV4由四组这样的数字组成，中间由.隔开，或者说由三组数字和字符.和一组数字组成，所以匹配IPV4的正则表达式如下：

(((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))\.){3}((\d{1,2})|(1\d{2})|(2[0-4]\d)|(25[0-5]))

```
- 比较有价值的正则表达式（别人的博文）

https://blog.csdn.net/dangjun625/article/details/83428354?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param

###### tags: `Golang` `mysql` `ip`