ip2region 实战指南:10 微秒级离线 IP 定位,城市级 IP 归属地查询与 xdb 数据自管
ip2region 是一个离线 IP 定位库与 IP 数据管理框架,同时支持 IPv4/IPv6 双栈。单次 xdb 查询处于 10 微秒级别:既能回答"某 IP 属于哪",也能管理你自有的上亿段 IP 数据。
从第一个查询跑通:xdb 文件到城市级 IP 归属地结果
先认识 data/ 目录的四份文件:ipv4_source.txt、ipv6_source.txt 是覆盖全部 IPv4/IPv6 地址段的原始源数据;ip2region_v4.xdb、ip2region_v6.xdb 是由源数据生成的二进制文件,可直接被各语言客户端加载查询。内置数据的区域信息固定为五段格式:
国家|省份|城市|运营商|ISO 3166-1 两位国家代码
例如 1.2.3.4 返回 Australia|Queensland|Brisbane|0|AU,240e:3b7:... 返回 中国|广东省|深圳市|电信|CN。两条约定:中国区域全部用中文,非中国区域全部用英文;五段字段不固定——生成时可用 --field-list 按字段索引重组,把 GPS 坐标、邮编等业务字段挂进去,从而用 ip2region 管理自有 IP 定位数据。
查询路径很短:读文件头确认版本,经向量索引定位段索引块,再读出区域字符串。binding/golang/ 的交互式查询程序每次查询都会打印 iocount 字段,它来自 searcher 内部 ioCount 的累计——这是观察磁盘 IO 次数、对比不同缓存策略最直接的出口。
选对缓存策略:最省内存 / 默认推荐 / 极致性能
各语言客户端的三种模式设计一致。以 Go 实现为例,Searcher 结构体同时持有 vectorIndex 与 contentBuff 两块缓冲,read() 优先从 contentBuff 拷贝,落空才走 Seek + Read:
| 档位 | 构造方式 | 内存 | 延迟 | 适用场景 |
|---|---|---|---|---|
| 最省内存:纯文件 | NewWithFileOnly | 无常驻内存 | 每次查询有磁盘 IO | 低 QPS、内存紧张的工具服务 |
| 默认推荐:VectorIndex | NewWithVectorIndex | 固定 512KiB | 平均 100 微秒以内 | 大多数线上查询服务 |
| 极致性能:整文件 | NewWithBuffer | 等于 xdb 文件大小 | 10 微秒级、零 IO | 高并发网关、极致延迟场景 |
512KiB 索引缓存为什么够用
512KiB 不是拍出来的,是常量相乘:binding/golang/xdb/header.go 中 VectorIndexRows = 256、VectorIndexCols = 256、VectorIndexSize = 8 字节,256×256×8 = 524288 字节。把这块区域理解成大图书馆的"楼层导览牌":段索引区被切成 256×256 个分组,每个 8 字节的 super block 只记该组的"首个索引块指针 + 末个索引块指针",查询先查导览牌缩小范围,再在块内二分,省掉了寻找起点的磁盘 IO。vIndex 是只读数据,可在进程启动时 LoadVectorIndexFromFile 一次,全局共享,每个 goroutine 各自创建 searcher,减少固定 IO 与系统压力。
要零 IO 就上 NewWithBuffer:整文件入内存后创建的 searcher 天然并发安全、可直接共享,延迟钉在 10 微秒级;验证方式是跑 bench,输出的 Cost 字段即单次查询平均耗时(ns)。
多语言生产环境:并发安全查询与 xdb 文件校验
查询客户端覆盖主流语言,全部同时支持 IPv4 与 IPv6:
| 语言 | 目录 | 语言 | 目录 |
|---|---|---|---|
| Golang | binding/golang/ | C(std=c99) | binding/c/ |
| Java | binding/java/ | Rust | binding/rust/ |
| PHP | binding/php/ | Python | binding/python/ |
| JavaScript | binding/javascript/ | C# | binding/csharp/ |
| Lua / Lua_C | binding/lua/、binding/lua_c/ | Erlang | binding/erlang/ |
| Nginx 扩展 | binding/nginx/ | C++ | binding/cpp/ |
| Cangjie(仓颉) | binding/cangjie/ |
另有社区开发者通过第三方仓库贡献了 php composer 客户端、node.js addon、ruby 查询实现与数据转换工具。
Go 并发查询:searcher 池怎么配
自 3.11.0 起,官方建议直接用 binding/golang/service/ip2region.go 的 Ip2Region 统一服务接入,分四步:
- v4、v6 配置分开创建(
NewV4Config/NewV6Config),参数依次为缓存策略(NoCache/VIndexCache/BufferCache)、xdb 路径、searcher 数量;某参数传nil即禁用该版本查询,对应查询返回""; - 用
service.NewIp2Region(v4Cfg, v6Cfg)创建服务,API 并发安全,内部按 IP 字节长度(4 或 16)自动识别版本; - searcher 数量按你的服务并发量设置,运行期固定——每次查询从池中借出一个 searcher、用完归还,池空时借出操作会阻塞等待可用实例;
- 缓存策略为
BufferCache时数量参数被忽略,内部使用单实例内存 searcher;关闭时调用Close(),默认最多等 10 秒让 searcher 归还,可用CloseTimeout自定义。
基础 API 为 Search(string | []byte) (string, error):失败时 error 携带具体原因,IP 未命中返回空串。
import "github.com/lionsoul2014/ip2region/binding/golang/service"
// v4/v6 配置分开创建,参数传 nil 表示禁用该版本查询
v4Cfg, _ := service.NewV4Config(service.VIndexCache, "data/ip2region_v4.xdb", 20)
v6Cfg, _ := service.NewV6Config(service.VIndexCache, "data/ip2region_v6.xdb", 20)
ip2, _ := service.NewIp2Region(v4Cfg, v6Cfg)
defer ip2.Close() // 默认等待 10 秒让 searcher 归还
region, err := ip2.Search("1.2.3.4") // 自动识别 v4/v6,未命中返回 ""
VerifyFromFile:为什么只在启动时校验一次
// 服务启动时执行一次;严禁在每次创建 Searcher 时校验
err := xdb.VerifyFromFile(dbPath)
校验逻辑见 binding/golang/xdb/util.go 的 Verify:读取 256 字节文件头,按结构版本(Structure20/Structure30)推导运行时指针字节数,并确认 xdb 文件大小不超过指针可表达的最大值,防止文件指针溢出。后续版本的新特性可能让旧 searcher 与新文件不兼容,不做校验会在运行期出意外错误;但校验本身要读盘,放进高并发查询路径会拖慢响应,所以只在启动时(或命令行手工)做一次。
自己造 xdb:从文本源文件生成的完整命令
xdb 文件四段结构与字节级细节
生成程序位于 maker/,语言覆盖:Golang、Java、Rust、C++ 均支持 IPv4 与 IPv6,Python、C# 仅支持 IPv4。maker/golang/xdb/maker.go 头注释给出的文件布局如下:
+----------------+-------------------+---------------+--------------+
| header space | speed up index | data payload | block index |
+----------------+-------------------+---------------+--------------+
| 256 bytes | 512 KiB (fixed) | dynamic size | dynamic size |
+----------------+-------------------+---------------+--------------+
- 头部区 256 字节:2 字节版本号(IPv6 支持前固定为 2,其后更新为 3)、2 字节索引算法编码、4 字节生成时间戳、4 字节索引块起始/结束指针、2 字节 IP 版本(4/6)、2 字节运行时指针字节数;
- 数据负载区:每条区域信息为 2 字节长度 + 动态长度内容;
- 段索引区:每条索引项 = 起始 IP + 结束 IP + 2 字节数据长度 + 4 字节数据指针,IPv4 每项 14 字节、IPv6 每项 38 字节(见
binding/golang/xdb/version.go的SegmentIndexSize常量); - 向量索引区:256×256 个 super block、每个 8 字节,即前文的"楼层导览牌"。
生成过程自动完成去重与压缩:相邻、重叠的 IP 段会被检查并合并补全;Maker 内部用 regionPool map[string]uint32 充当"中心仓库"——首个出现的区域字符串分配一个数据指针,后续相同区域只存指针引用,同一份内容在文件里只落盘一次。
# 在 maker/golang 目录 make 编译后执行
./xdb_maker gen --src=../../data/ipv4_source.txt --dst=./ip2region_v4.xdb --version=ipv4
# --field-list 可按索引重组字段,如 0,1,2,3-6,7;--log-level: debug/info/warn/error
./xdb_maker bench --db=./ip2region_v4.xdb --src=../../data/ipv4_source.txt --version=ipv4
生成验证:bench 怎么用
自行生成的 xdb 必须跑 bench:它逐行读取源文件,从每个 IP 段中取起始与结束 IP 做查询,与原始区域逐一比对,不一致即打印错误并终止,看到 Bench finished 即代表通过。两个硬约束:--src 必须与生成该 xdb 时使用的源文件一致;默认出错立即终止,加 --ignore-error=true 可跳过继续、最后查看失败统计。maker/golang/ 还附带 Dockerfile,可在隔离环境里构建镜像完成生成、查询与验证。
xdb 数据更新:手工编辑与检测式自动更新
仓库自带的离线 IP 定位源数据只是不定期更新。对数据准确性、更新频率要求高的场景,官方建议购买 Ip2Region 社区或第三方厂商的商业离线数据;要自行维护则走两条路。
用 edit 子命令手工编辑
editor>> put 36.132.128.0|36.132.147.255|中国|黑龙江省|哈尔滨市|移动|CN
Put(...): Ok, with 1 deletes and 2 additions
editor>> put_file ../../data/sample/ip.test.txt
editor>> save
在 maker/golang 目录 make 后执行 ./xdb_maker edit --src=../../data/ipv4_source.txt --version=ipv4 进入交互编辑器,命令分步如下:
put [segment]:段格式为起始IP|结束IP|国家|省份|城市|运营商|国家代码;输出的1 deletes and 2 additions表示编辑器已自动完成对原有段的拆分与替换;put_file [file]:批量加载,文件中的 IP 段不必严格连续、允许重叠,编辑器自动分析处理,仓库自带示例data/sample/ip.test.txt;list [offset] [size]:分页列出数据段用于核对;save:写回源文件,随后用gen重新生成 xdb;quit:退出。
maker/cpp/ 同样提供 C++ 数据编辑器,支持 IPv4/IPv6。
检测式自动更新
如果你有海量真实访问来源、能持续采集归属反馈,可参考官方 "Detection Algorithm"(检测算法)编写自己的更新程序:以现有数据为基准,与真实反馈比对,识别漂移段并增量修正。官方社区提供 Data Update 两期实现视频讲解该流程。
⚠️ 上线检查清单
- xdb 已通过
VerifyFromFile校验(启动时一次,不做进每次创建 Searcher 的路径); - 缓存策略已按三档选定:最省内存 / 默认推荐(512KiB VectorIndex)/ 极致性能(整文件),并用
iocount与 bench 的Cost复核过; - searcher 池数量与你的服务并发量匹配,且明白运行期数量固定;
- 每次数据更新后重跑 bench,
--src与生成时的源文件严格一致; - IPv4/IPv6 各自的 xdb 用对应
--version参数生成与校验; - 区域字段(含
--field-list扩展字段)与业务侧解析逻辑的字段数一致。
想再深入,可前往 ip2region.net 官方社区(2025-06-12 正式上线),那里提供商业离线数据服务、查询测试与数据纠错,以及覆盖 xdb 数据结构、查询过程、生成过程、文件生成教程、并发安全查询、数据更新共 6 篇系列技术文档——建议从"数据结构描述"读起,再对照本清单落地;作者的技术分享公众号为 lionsoul-org。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



