inet_aton可用但需谨慎:仅支持ipv4,解析宽松(如'127.0.0.1abc'截断为127.0.0.1)、必须用int unsigned存储、需正则校验+convert(inet_ntoa(), char)防乱码,ipv6须用inet6_aton。

INET_ATON 能用,但直接套用会埋坑——它只支持 IPv4,且解析宽松、类型不安全、字符集易乱码,IPv6 完全不兼容。
为什么不能直接用 INET_ATON 插入就完事?
常见错误现象是:插入 '127.0.0.1abc' 或 '1.2.3' 也能成功,但实际存的是截断或补零后的值(前者变成 127.0.0.1,后者变成 1.2.0.3)。更隐蔽的问题是字段类型没设对:INT(有符号)会导致 INET_ATON('255.255.255.255') 存成 -1,查出来完全失真。
- 必须用
INT UNSIGNED存储,不是INT,也不是BIGINT - 插入前加校验:先
WHERE ip REGEXP '^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$',再INET_ATON(ip) IS NOT NULL - 空字符串、
NULL、纯字母都返回NULL,但缺段或带后缀的非法输入不会报错,得靠正则兜底
查询时怎么避免 INET_NTOA 返回乱码或空字符串?
INET_NTOA 返回的是 VARBINARY(4),不是字符串。在客户端字符集为 utf8mb4 但字段无显式字符集声明时,容易显示为 ??? 或空白。
- 显示时务必包装一层:
CONVERT(INET_NTOA(ip_int), CHAR) - 不要依赖 SELECT 直出结果来判断是否正常——用
HEX(INET_NTOA(ip_int))看是不是 8 位十六进制(如C0A80101对应192.168.1.1) - 如果字段类型误设为
BIGINT,INET_NTOA仍能执行,但高位字节被静默丢弃,不报错也不警告
IPv6 怎么办?别硬套 INET_ATON
INET_ATON 和 INET_NTOA 对任何 IPv6 地址都返回 NULL,MySQL 5.6 及更早版本原生不支持;MySQL 8.0+ 才有 INET6_ATON/INET6_NTOA,但它们返回 VARBINARY(16),和 IPv4 的整数类型语义完全不同。
- 混合存储 IPv4/IPv6 时,绝不能共用一个整数字段——类型已断裂,后续无法可靠区分
- 推荐方案:单独用
VARBINARY(16)存原始二进制,或用CHAR(39)存标准化字符串(应用层统一处理格式) - 若必须用 MySQL 原生函数,MySQL 8.0+ 中
INET6_ATON('::1')返回0x00000000000000000000000000000001,注意它不能和INET_ATON结果做数值比较
索引和范围查询要注意什么?
用整数存 IP 的核心优势是支持高效范围查询(比如查某网段),但有几个关键点常被忽略:
- WHERE 条件里写
ip BETWEEN INET_ATON('192.168.1.0') AND INET_ATON('192.168.1.255')是可行的,但每次查询都会重复计算——建议把边界值预计算好传入 - 如果字段上有索引,
WHERE ip = INET_ATON('...')能走索引;但WHERE INET_NTOA(ip) = '...'一定全表扫描 - 网段计算要小心:CIDR 如
10.0.0.0/8对应ip >= INET_ATON('10.0.0.0') AND ip ,不是简单加减
UNSIGNED 类型、CONVERT(..., CHAR) 显式转串,少一个环节,数据就 quietly 腐蚀掉一点。











