使用用于自动建议的庞大地址数据库的最佳方式是什么?
What is the best way to work with a huge address database for autosuggest?
我有一个数据库,其中包含一个国家/地区的大约 300,000,000 个地址。此数据应该用于表格中的自动建议,即:如果地址是 1900 Country Hill Road
,用户将键入 Count
(假设至少 3 个字符来触发自动建议是个好主意) ,并且表单可能会为用户提供前 10 个结果,即:
165 Countable Road
1890 Countable Road
44 Counting Circle
900 Country Drive
13 Country Hill Road
1900 Country Hill Road
344 Country Mountain Place
etc.
选择地址还要填写City和ZIP。
注意:这是在地址格式为 Street ##/##
的国家/地区,因此首先要输入实际姓名。
执行 SELECT * FROM addresses WHERE street LIKE 'Count%' LIMIT 10
查询平均需要大约 3 秒。仅选择 street, house_number, city, zip
会稍微快一些。每行有 19 列。我已经根据一些 MySQL 指南在其中一列上添加了索引,但这并没有显着帮助。我希望每个请求的时间少于 500 毫秒。
解决此问题的最佳方法是什么?我想到的是:
- 根据街道的首字母将数据库 table 分成多个 table,并根据用户键入的内容选择 table。这意味着我最终会得到大约 26 tables.
- 将所有内容转换成多个 JSON 文件
- 进一步研究优化数据库
让我逐步分析。
- 您正在搜索街道地址,以开头的给定字符串,正确吗?
WHERE street LIKE 'Count%'
和 INDEX(street)
是合理的最佳选择。没有单独的table,没有JSON,没有首字母等
LIMIT 10
将有效地找到前 10 个这样的项目。注意:添加 ORDER BY
可能会破坏性能。
SELECT street
在索引的 B+Tree
中找到结果。由于这就是您所需要的,因此请使用它。 SELECT <<some columns>>
或 SELECT *
必须执行额外操作才能获取其他列。
这样的索引会向下钻取 B+Tree 到第一次出现的“Count...”,然后从那里向前扫描。即使数据集不能缓存在 RAM 中,实际上也只有一次磁盘命中,这将花费几毫秒。如果您需要额外的列,那么额外的操作可能需要 10 次以上的磁盘命中(由于 LIMIT 10
)。
因此,重要的是 INDEX
包含 完全 您需要接收的 自动建议。
ORDER BY
可以 只有当 你说 ORDER BY street
(或 ORDER BY street DESC
)。否则,它必须找到以“count”开头的数千(百万?)条街道,对这个列表进行排序,然后交付 10。
根据该分析,“最少 3 个字符”并不重要。然而,我的 UI 直觉是 3 是合理的。
哦,另一个问题。由于数千个城镇中可能有“1 Main St”,因此“Main”的自动 select 可能会显示 10 个相同的“1 Main St”。
哦,我没有处理你从你的例子中暗示的东西。 street
包括街道号码。或者您可能有两列——street_name
(用于搜索)和 street_number
。所以...
有两列:
- 一个用于自动建议(“Main St”)
- 一个用于展示(“1 Main St, Acron OH”)
然后有INDEX(auto_suggest, display)
。这将使您以最少的努力获得自动建议值的效率,但允许同样有效地显示完整地址。
但这仍然存在重复地址的问题。 OK,我现在被迫额外推荐一个table。它将包含所有 唯一 街道名称,没有 street_number,等等
CREATE TABLE AutoSuggest (
street VARCHAR(99) NOT NULL,
PRIMARY KEY(street)
) ENGINE=InnoDB;
注意,在MySQL中,一个PRIMARY KEY
是一个B+Tree索引,与数据聚簇,是UNIQUE
。对于 200M 地址,这个 table 可能大约是 1GB,在补偿重复、开销等之后。在你的应用程序有 运行 一段时间后,大部分 table 将被缓存在 buffer_pool。这使得典型的自动建议查找大约需要 1 毫秒。
但是,这只会给你街道名称,而不是地址等。也许你想要一个名为 full_street
的第二列(并更改为 PRIMARY KEY(street, full_street)
):
INSERT INTO AutoSuggest (street, full_street)
VALUES
("Main St", "1 Main St");
这提供了前面提到的大部分优点和少数缺点。
考虑我对您的两个目标进行权衡的评论:
- 快速自动建议;
- UI 在自动提示期间呈现的内容。
我有一个数据库,其中包含一个国家/地区的大约 300,000,000 个地址。此数据应该用于表格中的自动建议,即:如果地址是 1900 Country Hill Road
,用户将键入 Count
(假设至少 3 个字符来触发自动建议是个好主意) ,并且表单可能会为用户提供前 10 个结果,即:
165 Countable Road
1890 Countable Road
44 Counting Circle
900 Country Drive
13 Country Hill Road
1900 Country Hill Road
344 Country Mountain Place
etc.
选择地址还要填写City和ZIP。
注意:这是在地址格式为 Street ##/##
的国家/地区,因此首先要输入实际姓名。
执行 SELECT * FROM addresses WHERE street LIKE 'Count%' LIMIT 10
查询平均需要大约 3 秒。仅选择 street, house_number, city, zip
会稍微快一些。每行有 19 列。我已经根据一些 MySQL 指南在其中一列上添加了索引,但这并没有显着帮助。我希望每个请求的时间少于 500 毫秒。
解决此问题的最佳方法是什么?我想到的是:
- 根据街道的首字母将数据库 table 分成多个 table,并根据用户键入的内容选择 table。这意味着我最终会得到大约 26 tables.
- 将所有内容转换成多个 JSON 文件
- 进一步研究优化数据库
让我逐步分析。
- 您正在搜索街道地址,以开头的给定字符串,正确吗?
WHERE street LIKE 'Count%'
和INDEX(street)
是合理的最佳选择。没有单独的table,没有JSON,没有首字母等LIMIT 10
将有效地找到前 10 个这样的项目。注意:添加ORDER BY
可能会破坏性能。SELECT street
在索引的B+Tree
中找到结果。由于这就是您所需要的,因此请使用它。SELECT <<some columns>>
或SELECT *
必须执行额外操作才能获取其他列。
这样的索引会向下钻取 B+Tree 到第一次出现的“Count...”,然后从那里向前扫描。即使数据集不能缓存在 RAM 中,实际上也只有一次磁盘命中,这将花费几毫秒。如果您需要额外的列,那么额外的操作可能需要 10 次以上的磁盘命中(由于 LIMIT 10
)。
因此,重要的是 INDEX
包含 完全 您需要接收的 自动建议。
ORDER BY
可以 只有当 你说 ORDER BY street
(或 ORDER BY street DESC
)。否则,它必须找到以“count”开头的数千(百万?)条街道,对这个列表进行排序,然后交付 10。
根据该分析,“最少 3 个字符”并不重要。然而,我的 UI 直觉是 3 是合理的。
哦,另一个问题。由于数千个城镇中可能有“1 Main St”,因此“Main”的自动 select 可能会显示 10 个相同的“1 Main St”。
哦,我没有处理你从你的例子中暗示的东西。 street
包括街道号码。或者您可能有两列——street_name
(用于搜索)和 street_number
。所以...
有两列:
- 一个用于自动建议(“Main St”)
- 一个用于展示(“1 Main St, Acron OH”)
然后有INDEX(auto_suggest, display)
。这将使您以最少的努力获得自动建议值的效率,但允许同样有效地显示完整地址。
但这仍然存在重复地址的问题。 OK,我现在被迫额外推荐一个table。它将包含所有 唯一 街道名称,没有 street_number,等等
CREATE TABLE AutoSuggest (
street VARCHAR(99) NOT NULL,
PRIMARY KEY(street)
) ENGINE=InnoDB;
注意,在MySQL中,一个PRIMARY KEY
是一个B+Tree索引,与数据聚簇,是UNIQUE
。对于 200M 地址,这个 table 可能大约是 1GB,在补偿重复、开销等之后。在你的应用程序有 运行 一段时间后,大部分 table 将被缓存在 buffer_pool。这使得典型的自动建议查找大约需要 1 毫秒。
但是,这只会给你街道名称,而不是地址等。也许你想要一个名为 full_street
的第二列(并更改为 PRIMARY KEY(street, full_street)
):
INSERT INTO AutoSuggest (street, full_street)
VALUES
("Main St", "1 Main St");
这提供了前面提到的大部分优点和少数缺点。
考虑我对您的两个目标进行权衡的评论:
- 快速自动建议;
- UI 在自动提示期间呈现的内容。