當(dāng)我們?cè)谑褂?MySQL 設(shè)計(jì)表結(jié)構(gòu)的時(shí)候都會(huì)遇到一個(gè)經(jīng)典問題:“如果要存 IP 地址,到底該用什么數(shù)據(jù)類型?”
別小看這個(gè)問題,如果存儲(chǔ)方式選錯(cuò)了,未來(lái)不僅浪費(fèi)空間,還可能導(dǎo)致查詢性能低下。今天我就帶你一步一步搞懂如何在 MySQL 里優(yōu)雅地存儲(chǔ) IP 地址。
為啥不能直接用字符串?
很多小伙伴第一反應(yīng):IP 地址不就是 "192.168.0.1"
這種字符串嘛?那我直接用 VARCHAR(15)
或者 CHAR(15)
存不就行了嗎?
表面上來(lái)看沒啥大的問題,但這樣做有幾個(gè)弊端:
- 空間浪費(fèi):字符串比數(shù)字類型占用更多字節(jié),還要額外存儲(chǔ)長(zhǎng)度信息。
- 查詢效率低:字符串比較比數(shù)字比較要慢,索引效果也差。
- 不方便計(jì)算:比如要判斷 IP 是否落在某個(gè)網(wǎng)段里,字符串處理就很麻煩。
所以,直接用字符串存 IP 地址,只能算是最簡(jiǎn)單但不優(yōu)雅的做法。
那么,到底該怎么做才比較合適呢?
IPv4:用 INT UNSIGNED
存整數(shù)更高效
IPv4 地址其實(shí)就是一個(gè) 32 位的整數(shù),比如:
192.168.0.1 → 3232235521
在 MySQL 里我們完全可以用 INT UNSIGNED
類型來(lái)存儲(chǔ),既節(jié)省空間(4 字節(jié)),又利于索引和范圍查詢。
- 插入時(shí),用
INET_ATON()
轉(zhuǎn)換:
INSERT INTO user_logs(ip) VALUES (INET_ATON('192.168.0.1'));
- 查詢時(shí),用
INET_NTOA()
再轉(zhuǎn)回字符串:
SELECT INET_NTOA(ip) FROM user_logs;
這樣就能同時(shí)兼顧 空間效率 和 查詢效率。
IPv6:用 BINARY(16)
存二進(jìn)制
IPv6 地址更長(zhǎng),是 128 位,像這樣:
2001:0db8:85a3:0000:0000:8a2e:0370:7334
如果直接用字符串存,需要 CHAR(39)
,非常浪費(fèi)空間。
更推薦的做法是用 BINARY(16)
或 VARBINARY(16)
存二進(jìn)制形式。
MySQL 也有直接現(xiàn)成的函數(shù):
INSERT INTO user_logs(ipv6) VALUES (INET6_ATON('2001:db8::1'));
- 查詢時(shí),用
INET6_NTOA()
轉(zhuǎn)回:
SELECT INET6_NTOA(ipv6) FROM user_logs;
占用空間 16 字節(jié),支持索引,性能杠杠滴。
那么,如果既要支持 IPv4,又要支持 IPv6 怎么辦呢?
這就更簡(jiǎn)單了,直接:
- 字段類型設(shè)為
VARBINARY(16)
或 BINARY(16)
- 統(tǒng)一用
INET6_ATON()
/ INET6_NTOA()
存取
MySQL 會(huì)自動(dòng)把 IPv4 地址映射到 IPv6 格式里,做到兼容存儲(chǔ),是不是非常 nice?
來(lái)個(gè)實(shí)戰(zhàn)
以下面的建表語(yǔ)句為例:
CREATE TABLE user_logs (
idBIGINTUNSIGNED AUTO_INCREMENT PRIMARY KEY,
ip BINARY(16) NOTNULLCOMMENT'存儲(chǔ)IPv4/IPv6地址',
created_at TIMESTAMPDEFAULTCURRENT_TIMESTAMP
);
-- 插入IPv4
INSERTINTO user_logs(ip) VALUES (INET6_ATON('192.168.0.1'));
-- 插入IPv6
INSERTINTO user_logs(ip) VALUES (INET6_ATON('2001:db8::1'));
-- 查詢
SELECTINET6_NTOA(ip) AS ip_address FROM user_logs;
一套方案,IPv4 + IPv6 通吃,相當(dāng) OK ??
總結(jié)
- IPv4-only →
INT UNSIGNED
(配合 INET_ATON
/ INET_NTOA
) - IPv6-only →
BINARY(16)
(配合 INET6_ATON
/ INET6_NTOA
) - 通用方案 →
BINARY(16)
或 VARBINARY(16)
,一表解決 IPv4 和 IPv6
老鐵們,下次設(shè)計(jì)數(shù)據(jù)庫(kù)表的時(shí)候,就可以考慮不用 VARCHAR(15)
去存 IP 了,用這種方式,性能和優(yōu)雅度都會(huì)大幅提升!?? 趕緊用起來(lái)吧。
該文章在 2025/10/9 11:25:16 編輯過