Files
SuBlog/content/posts/ip-geolocation-guide/index.md
T

142 lines
6.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: IP 属地是怎么查出来的?本地查询与在线查询方案对比
published: 2026-08-07T12:00:00
updated: 2026-08-07
description: 科普 IP 属地查询的原理:IP 段与归属地的映射、纯本地离线查询为何能做到毫秒级,并对比 ip2region、在线 API 与 MaxMind 三种方案的优劣与适用场景。
tags: ['IP', '网络', '科普']
draft: false
difficulty: 入门
---
## 写在前面
很多网站会在「登录记录」「安全中心」里显示你的登录 IP 归属地,比如「福建·福州 · 中国移动」。你可能好奇过:**服务器是怎么根据一个 IP 就判断出我在哪个城市、哪个运营商的?**
这篇科普文章讲清楚三件事:
1. IP 属地到底查的是什么;
2. 纯本地查询为什么能做到毫秒级且不联网;
3. 几种主流查询方案的优缺点对比,方便你按场景选型。
> 说明:文中用到的 IP 均为 RFC 5737 保留的文档示例段(TEST-NET-2,`198.51.100.0/24`),不会对应任何真实地址。
## 先想清楚:IP 属地查的是什么
一个常见误区是「每个 IP 对应一个具体地址」。实际上 IP 属地的本质是 **IP 段与归属地的映射**。
运营商和机构在申请 IP 时,是按**网段(CIDR)**成块分配的。比如 `198.51.100.0/24` 这一个段,一整块都归属同一地区、同一运营商。所以归属地数据天然可以**按段存储**:
```text
IP 起始 IP 结束 归属地
────────────────────────────────────────
198.51.100.0 198.51.100.255 中国|福建|福州|移动
198.51.101.0 198.51.101.255 中国|浙江|杭州|电信
```
全球公网 IP 约有 42 亿个,但**网段只有几十万条**。正因为存的是「段」而不是「每个 IP」,完整的归属地数据文件才只有几 MB(ip2region 的 `.xdb` 约 11MB)。这是「本地离线查询」可行的第一前提。
所以,**IP 属地从来不是精确到街道的定位**,它返回的是段级的「省|市|运营商」粗粒度信息。
## 纯本地查询的原理
以开源的 **ip2region** 为例,它的查询完全在本地完成,不联网、不请求第三方,几十微秒到毫秒级返回。
### 第一步:把 IP 变成整数
以 `198.51.100.42` 为例,它会转成一个 32 位整数 `0xC633642A`。IP 的四个数字本质就是 4 个字节,拼起来就是一个整数,这一步没有任何玄学。
### 第二步:二分查找
在按起始 IP 排序的段列表里做**二分查找**,找到满足 `段的起始IP ≤ 目标整数 ≤ 段的结束IP` 的那一段。
二分查找的复杂度是 O(log N),N 是几十万条,所以很快——这就是「不靠网络也能毫秒返回」的核心。
### 第三步:.xdb 文件的两级结构
ip2region 的 `.xdb` 不是简单顺序文件,内部分两个区,进一步加速:
```text
┌─────────────────────────────┐
│ 索引区 (Index) │ // 每段一条:startIP / endIP / 数据位置
│ …… 二分快速定位段 …… │
├─────────────────────────────┤
│ 数据区 (Data) │ // 真正存放各段的地点字符串
│ …… │
└─────────────────────────────┘
```
先二分索引区定位到「是哪一段」,再按指针去数据区读出地点字符串,一次磁盘读就能拿到结果。
### 为什么「离线数据」不会很快过期
IP 归属取决于**注册机构和运营商的分配**,一个网段通常长期稳定属于某地区、某运营商。所以:
- 下载一次数据文件,可以用很久;
- 归属偶尔变化时,**每月更新一次 `.xdb` 文件**即可(替换文件、不用重启服务);
- 数据源来自 IANA/APNIC 等机构的分配表与公开归属信息,不是实时探测。
## 主流查询方案对比
实际开发中,查询 IP 属地主要有三条路线。
### 方案一:ip2region(纯本地离线,推荐)
```go
// Go 中的用法(github.com/lionsoul2014/ip2region/binding/golang)
region, err := searcher.SearchByStr(ip)
// 返回类似 "中国|福建省|福州市|移动"
```
| 优点 | 缺点 |
|------|------|
| 免费、无网络依赖、无 QPS 限制 | 段级粒度,个别段可能显示到相邻地区 |
| 毫秒级查询,永不因外部服务挂掉 | 数据按月更新,最新分配的段可能短暂缺失 |
| 数据文件可随版本更新,不重启 | 只返回省市区+运营商,无经纬度 |
**适用**:博客/后台的「登录记录显示归属地」这类展示用途。
### 方案二:在线 API(ip-api / 高德 / 腾讯位置服务)
```http
GET https://ipapi.co/198.51.100.42/json/
```
| 优点 | 缺点 |
|------|------|
| 数据最新、可能更精确 | 有频率限制(免费版限 QPS) |
| 部分服务还返回经纬度/时区 | 依赖第三方,断网/被墙/限流时拿不到 |
| 无需维护数据文件 | 每次查询都要发一次 HTTP 请求 |
**适用**:对精度要求高、且能接受外部依赖与限流的场景。
### 方案三:MaxMind GeoLite2(离线 .mmdb)
| 优点 | 缺点 |
|------|------|
| 全球数据,覆盖广、精度较好 | 需要注册账号下载,商用需授权 |
| 标准 `.mmdb` 格式,各语言都有库 | 国内 IP 的省市粒度有时不如 ip2region 细 |
| 离线查询 | 数据同样要定期更新 |
**适用**:面向全球用户的场景。
## 局限与诚实提醒
无论用哪种方案,都要清楚 IP 属地的边界:
- **段级精度**:网段归属是「绝大多数」准确,个别 IP(大型企业自建段、跨省调度段)可能显示到相邻地区或只到省。
- **运营商级,不是定位**:拿到的是「省|市|运营商」,不是精确街道或基站位置——精确位置需要 GPS 或运营商三角定位,纯 IP 归属做不到。
- **动态 IP**:家宽通常是动态 IP,重拨会变,所以「上次登录在福州,这次在广州」不一定是异常,可能是 IP 变了。
- **别用于安全判定**:用 IP 段做封禁或风控判定风险自负,段级数据不适合当精确依据。
## 小结
一句话总结:
> **IP 属地 = 几十万条「IP 段 → 归属地」映射 + 一次二分查找。** 它查的是网段归属,不是精确位置。
- 想省心、免费、稳定,用 **ip2region** 本地离线查询;
- 想要最新最细、能接受外部依赖,用**在线 API**;
- 面向全球用户,用 **MaxMind GeoLite2**。
对个人博客「登录记录显示归属地」这种场景,本地离线方案通常是性价比最高的选择。