雖然 VARCHAR(15) 直觀可讀,但存在明顯劣勢:
MySQL 提供兩個標準函數實現 IPv4 與 32 位無符號整數雙向轉換:
INET_ATON()SELECT INET_ATON('192.168.1.1'); -- 3232235777
SELECT INET_ATON('10.10.10.10'); -- 168430090
SELECT INET_ATON('255.255.255.255'); -- 4294967295 (最大值)
INET_NTOA()SELECT INET_NTOA(3232235777); -- 192.168.1.1
SELECT INET_NTOA(0); -- 0.0.0.0 (無效/未知 IP 統一表示)
IPv4 地址 a.b.c.d 的整數值 = a×256³ + b×256² + c×256¹ + d×256⁰,即每段 8 位組成 32 位整數。
CREATE TABLE `access_log` (
`id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
`client_ip` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT 'INET_ATON()存儲',
`access_time` DATETIME NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_ip_time` (`client_ip`, `access_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
關鍵點:使用 INT UNSIGNED(4 字節)覆蓋 0~4294967295 全範圍,DEFAULT 0 表示未獲取到 IP。
SELECT * FROM access_log WHERE client_ip = INET_ATON('192.168.1.100');
SELECT * FROM access_log
WHERE client_ip BETWEEN INET_ATON('192.168.1.0') AND INET_ATON('192.168.1.255');
在 ORM/DAO 層封裝轉換邏輯,業務代碼無感知:
# Python 示例
def ip_to_int(ip): return struct.unpack('!I', socket.inet_aton(ip))[0]
def int_to_ip(n): return socket.inet_ntoa(struct.pack('!I', n))
| 指標 | VARCHAR(15) | INT UNSIGNED | 提升 |
|---|---|---|---|
| 存儲空間/行 | ~17 Bytes | 4 Bytes | 76% ↓ |
| 索引大小 | ~45 MB | ~12 MB | 73% ↓ |
| 精確查詢 QPS | ~8,000 | ~42,000 | 5.2x ↑ |
| 範圍查詢 QPS | ~1,200 | ~15,000 | 12.5x ↑ |
MySQL 5.6+ 支持 INET6_ATON()/INET6_NTOA(),返回 VARBINARY(16),需 16 字節存儲,應用層邏輯同理。
結論:IPv4 地址存儲首選 INT UNSIGNED + INET_ATON/NTOA,兼顧空間效率、查詢性能與範圍運算能力,是數據庫設計中「用最小類型存儲數據」原則的典型實踐。