上周帮一个客户看访问日志,江苏的订单被标成北京,广东的用户显示在内蒙古。他一口咬定是代码写错了,我看了一眼配置文件,ip 库文件的修改时间停在 2021 年。
IP 段每年都在重新分配,库不更新,查得越快错得越多。
常见的三种库差别在哪
纯真库是 dat 格式,免费版更新慢,一般一个月一次。MaxMind 的 GeoLite2 是 mmdb 格式,官方每周更新,国家准,城市级别要付费版才靠得住。国内的商业库按运营商和省份细分,价格从几百到几万不等,做风控的才需要。
mmdb 在代码里怎么读
PHP 用 geoip2 扩展,Python 用 maxminddb,Node 用 maxmind 包。加载后调 city 方法,返回里 country、subdivisions、city 三层,字段缺失就取上一级,别直接取 city 名,很多 IP 只到省。
内网地址必须先排掉
- 10.0.0.0/8、172.16.0.0/12、192.168.0.0/16 查出来全是空
- 127.0.0.1 和 ::1 要单独判断,别进查询函数
- 反向代理场景取 X-Forwarded-For 的第一段,直连取 remote_addr
IPv6 是另一个坑
不少免费库只收 IPv4,遇到 2408 开头的地址直接返回 null。手机流量现在大量走 IPv6,只看 IPv4 会把真实用户判成未知。选库时确认支持 IPv6,查询前统一用 inet_pton 校验格式。
多久更新一次合适
做展示用途,季度更新够用。做风控、做地域限制,至少每月一次,重要节点前手动更。更新时保留旧文件,新库读不出来能立刻换回去。
自建查询服务的几个参数
把 mmdb 常驻内存,进程启动时加载一次,别每次请求都读文件。查询结果加一层本地缓存,同一 IP 一天内复用;批量查日志用脚本跑,别在网页请求里循环几千次。库文件放本地磁盘,放网络盘每次读取多几毫秒,十万条就是几十秒。
客户换成 GeoLite2 的当月版本后,江苏标北京的投诉没了。IP 归属地本来就是个概率问题,库新、字段取对,能到九成准,剩下那一成靠人工兜。