MySQL存储IP地址推荐整数型:IPv4用INTUNSIGNED及INET_ATON/INET_NTOA转换,仅4字节且支持高效范围查询;IPv6用VARBINARY(16)及INET6_ATON/INET6_NTOA。字符串存储占用大、性能差。
存储 IP 地址几乎是每个后端系统都必备的基本操作。然而,这个看似简单的任务却蕴含着不少技术细节——如何存储、采用何种格式,直接关系到后续查询效率、存储开销乃至整个系统的性能表现。

长期稳定更新的攒劲资源: >>>点此立即查看<<<
通常情况下,IP 地址的存储需求来源于以下几个场景:
IPv4 地址本质是一个 32 位二进制数,通常以点分十进制表示,例如 192.168.1.1。那么在数据库中到底采用哪种存储方式最合适?主流做法主要有两种。
最直接的做法是将这个字符串原样存入。IPv4 地址最长 15 个字符,使用 VARCHAR(15) 即可满足需求。
CREATE TABLE ip_records ( id INT AUTO_INCREMENT PRIMARY KEY, ip_address VARCHAR(15));INSERT INTO ip_records (ip_address) VALUES ('192.168.1.1');| 维度 | 说明 |
|---|---|
| 优点 | 直观易懂,直接插入、查询和显示,无需额外转换 |
| 缺点 | 占用存储空间较大;字符串比较性能较低;不利于范围查询 |
另一种思路是将 IP 地址视为一个 32 位整数来存储。MySQL 提供了专门的内置函数来协助完成转换。
CREATE TABLE ip_records ( id INT AUTO_INCREMENT PRIMARY KEY, ip_address INT UNSIGNED);-- 插入时转换INSERT INTO ip_records (ip_address) VALUES (INET_ATON('192.168.1.1'));-- 查询时还原SELECT INET_NTOA(ip_address) AS ip FROM ip_records;| 维度 | 说明 |
|---|---|
| 优点 | 仅占 4 字节,空间小;整数比较性能高;天然支持范围查询(BETWEEN) |
| 缺点 | 需要额外转换,不够直观,增加开发复杂度 |
通过简单对比,差异一目了然:
| 对比维度 | VARCHAR(15) | INT UNSIGNED |
|---|---|---|
| 存储空间 | ~15 字节 | 4 字节 |
| 索引效率 | 较低 | 较高 |
| 范围查询 | 不友好 | 天然支持 BETWEEN |
| 可读性 | 直接可读 | 需 INET_NTOA() 转换 |
| 开发复杂度 | 低 | 中 |
推荐:对性能有要求、需要范围查询(如 IP 段匹配)的场景,优先使用整数存储。
上文提到,MySQL 提供了几个专用转换函数,使整数存储方案变得非常便捷。
| 函数 | 方向 | 示例 |
|---|---|---|
INET_ATON() | 字符串 → 整数 | INET_ATON('192.168.1.1') → 3232235777 |
INET_NTOA() | 整数 → 字符串 | INET_NTOA(3232235777) → '192.168.1.1' |
对于 IPv6,情况则不同。IPv6 地址为 128 位,无法使用 INT UNSIGNED 存储,需采用 VARBINARY(16)。
| 函数 | 方向 | 示例 |
|---|---|---|
INET6_ATON() | 字符串 → 二进制 | INET6_ATON('2001:db8::1') → VARBINARY(16) |
INET6_NTOA() | 二进制 → 字符串 | INET6_NTOA(...) → '2001:db8::1' |
-- IPv6 存储示例CREATE TABLE ip_records_v6 ( id INT AUTO_INCREMENT PRIMARY KEY, ip_address VARBINARY(16));INSERT INTO ip_records_v6 (ip_address) VALUES (INET6_ATON('2001:db8::1'));SELECT INET6_NTOA(ip_address) AS ip FROM ip_records_v6;从原理上看,IPv4 是 32 位二进制数,分为 4 个字节(Octet),每个字节对应点分十进制中的一段。计算方式很简单:每段 × 256 的幂次,然后求和,等价于将 4 个字节拼成一个 32 位无符号整数。
192.168.1.1192 × 256 = 192 × 16,777,216 = 3,221,225,472168 × 256 = 168 × 65,536 = 11,010,048 1 × 256 = 1 × 256 = 256 1 × 256 = 1 × 1 = 1─────────────────────────────────────────────── 总和 = 3,232,235,777
从二进制视角看更直观:
192 → 11000000168 → 10101000 1 → 00000001 1 → 00000001拼接为 32 位:11000000 10101000 00000001 00000001转为十进制:3,232,235,777
公式:INET_ATON(A.B.C.D) = A × 2 + B × 2 + C × 2 + D
反向转换 INET_NTOA() 就是将整数按每 8 位拆开,转回点分十进制。
IPv6 是 128 位,分为 8 组,每组 16 位(2 字节),用冒号分隔的十六进制表示。:: 是 零压缩(zero compression),表示中间全是 0。先展开:
2001:db8::1 ↓ 展开 ::2001:0db8:0000:0000:0000:0000:0000:0001
每组是 16 位(2 字节),8 组 × 2 字节 = 16 字节,所以用 VARBINARY(16) 存储:
组1: 2001 → 0x20 0x01组2: 0db8 → 0x0d 0xb8组3: 0000 → 0x00 0x00组4: 0000 → 0x00 0x00组5: 0000 → 0x00 0x00组6: 0000 → 0x00 0x00组7: 0000 → 0x00 0x00组8: 0001 → 0x00 0x01───────────────────────最终 16 字节(十六进制):0x20 0x01 0x0D 0xB8 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0x01
要点:IPv6 没有"转成一个巨大整数"的做法,因为 128 位超出了 MySQL 整型的最大范围(BIGINT UNSIGNED 也只有 64 位)。所以直接用原始二进制 VARBINARY(16) 存储,不做数值运算,只做字节序列比较。
整数存储的一大优势是范围查询非常高效。例如需要查询一个网段内的所有 IP,使用 BETWEEN 即可轻松实现:
-- 查询 192.168.1.0 ~ 192.168.1.255 网段内的所有 IPSELECT INET_NTOA(ip_address) AS ipFROM ip_recordsWHERE ip_address BETWEEN INET_ATON('192.168.1.0') AND INET_ATON('192.168.1.255');如果使用 VARCHAR 存储,这种范围查询几乎无法高效实现。字符串比较是按字符顺序逐位匹配的,无法直接进行范围定位。
INT UNSIGNED(4 字节)远优于 VARCHAR(15)(15 字节),且查询性能更好。INET_ATON / INET_NTOA 做转换,简单可靠。VARBINARY(16) + INET6_ATON / INET6_NTOA,注意 IPv6 是 128 位,不能用整数类型。侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述