如何高效访问十亿行数据

问题描述 投票:0回答:2

我在下面的 ip_location 表中存储了 30 亿个 IP。

CREATE TABLE ip_location (
        id BIGINT AUTO_INCREMENT PRIMARY KEY,
        ip VARCHAR(15) NOT NULL,
        country VARCHAR(2),
        region_1 VARCHAR(255),
        region_2 VARCHAR(255),
        latitude DECIMAL(10, 6) NOT NULL,
        longitude DECIMAL(10, 6) NOT NULL,
        INDEX idx_ip (ip)
);

// example json data
{
        "ip": "1.2.3.4",
        "country": "US",
        "region_1": "California",
        "region_2": "San Francisco",
        "latitude": 37.769700,
        "longitude": -122.393300
}

我们推出的服务受到世界各地人们的欢迎。 我想将我们的一些 API 限制在特定区域。 检查用户的访问位置,每次用户调用时检查数据库中的IP API增加了DB和网络的负担。 因此我想将IP数据预加载到内存中以减少负载。

您能否建议一种存储和访问此类数据的有效方法? 我打算将所有数据加载到 Redis,但我认为它需要大量 RAM,我错了吗?

sql performance redis
2个回答
0
投票

您可以自己存储 IP 地址,但似乎更好的主意是仅在 Redis 中存储最近使用的 IP 地址,也许是您在最近 30 分钟内收到请求的 IP 地址,并定期清空这些 IP 地址有一段时间没有发送请求了。

您可以通过以下方式查询IP地址所属国家

https://api.country.is/<the IP address>

用你的IP亲自尝试一下。

所以流程是:

  1. 如果 IP 地址在 Redis 中,则从那里推断国家并将其时间戳更新为现在
  2. 如果 Redis 中没有该 IP 地址,则从 api.country.is 查询该 IP 地址并将结果所在的国家/地区以及时间戳存储在 Redis 中
  3. 有一个从 Redis 中删除旧引用的 cron 作业

0
投票

您需要更好地规范化该表,并使用更好的数据类型。这将减少数据的大小。

  • IP 地址可以是 v4 或 v6。 v4 适合 32 位,v6 适合 128 位并包含所有 v4 地址。因此,如果您只需要支持 v4,则将数据存储在
    binary(4)
    中,否则使用
    binary(16)
  • 去掉
    id
    列,没有必要,因为
    ip
    列应该是唯一的,而且很小。通常只有当自然键很宽或经常变化时才需要代理键。
  • ISO 规范中国家/地区代码固定为两个或三个字符。
CREATE TABLE country (
  country CHAR(2) PRIMARY KEY
);

CREATE TABLE region_1 (
  id bigint PRIMARY KEY,
  country CHAR(2) NOT NULL REFERENCES country (code2),
  region_1 VARCHAR(255) NOT NULL,
  UNIQUE (country, region_1)
);

CREATE TABLE region_2 (
  id bigint PRIMARY KEY,
  region_1 bigint REFERENCES region_1 (id),
  region_2 VARCHAR(255) NOT NULL,
  UNIQUE (region_1, region_2)
);

CREATE TABLE ip_location
(
  ip binary(4) NOT NULL PRIMARY KEY,
  region_2 bigint NOT NULL REFERENCES region_2 (id),
  latitude DECIMAL(10, 6) NOT NULL,
  longitude DECIMAL(10, 6) NOT NULL,
);

然后您可以使用联接来获取所需的数据。

例如:

SELECT
  r1.country,
  r1.region_1,
  r2.region_2,
  il.latitude,
  il.longitude
FROM ip_location il
JOIN region_2 r2 ON r2.id = il.region_2
JOIN region_1 r1 ON r1.id = r2.region_1
WHERE il.ip = @someIp;

各种主键索引应该使这些连接非常高效。

可能值得将较小的表预先加载到应用程序内存中,这样您就不需要不断地将其从数据库中加载。

© www.soinside.com 2019 - 2024. All rights reserved.