我正在处理一个需要缓存分页“搜索”结果的问题:Paginating very large datasets
搜索的工作方式如下:给定item_id,我找到匹配的item_ids及其排名。
我愿意承认没有向我的用户显示过去的任何结果,比如500.在500之后,我会假设他们不会找到他们想要的东西...结果已经排序反正顺序。所以我想缓存这500个结果,所以我只需要对查询进行一次繁重的处理,用户仍然可以对结果进行分页(最多500个)。
现在,假设我使用中间MySQL表作为我的缓存...也就是说,我将每个项目的前500个结果存储在“匹配”表中,如下所示:“item_id(INTEGER),matched_item_id(INTEGER) ,match_rank(REAL)“。搜索现在变得非常快:
SELECT item.* FROM item, matches
WHERE matches.item_id=<item in question>
AND item.id=matches.matched_item_id
ORDER BY match_rank DESC
LIMIT x,y
如果结果超过24小时,客户会要求将项目及其匹配重新索引到此表中,我没有问题。问题是,为N个项目存储500个结果(其中N是~100,000到1,000,000),这个表变得相当大...... 50,000,000 - 500,000,000行。
MySQL可以处理吗?我应该注意什么?
答案 0 :(得分:4)
MySQL可以处理这么多行,当你开始碰壁时,有几种技术可以扩展。 Partioning和replication是此方案的主要解决方案。
您还可以在stackoverflow上的a question I previously asked中查看MySQL的其他扩展技术。
答案 1 :(得分:1)
同意上述内容。要非常小心,以避免在这里通过非规范化进行过早优化。
请勿使用“SELECT *”。更多字段意味着更多磁盘读取。
确保使用覆盖索引 - 即您可以从索引中获取所有请求的字段值,而无需转到数据表。仔细检查您是否没有阅读记录数据。
测试,测试。
如果可能,请使用只写表(即没有更新,也不删除),因此mysql不会重用已删除的空格并重新填充索引。
确保索引字段尽可能短(但不能缩短。)
编辑:我想到了更多的事情......
标准(和最快)MyISAM表类型没有任何方法来维护除插入顺序以外的任何顺序的记录(通过填充已删除的行进行修改) - 即没有聚簇索引。但是,如果您根据对同一页面中的关联记录进行分组有用的索引定期复制/重建表,则可以伪造它。当然新的记录不符合,但98%的表效率优于默认值。
熟悉配置设置,尤其是缓存大小。事实上,为了方便起见,不要担心除缓存大小之外的任何其他设置(并了解它们各自的大小)。
熟悉统计日志中的信息,因为它适用于配置缓存设置的有效性。
始终运行“慢查询日志”。它的开销很低,而且是所有补救措施的第一站。
这不用说,但除了同一台服务器上的数据库之外,不要运行任何东西。一个重要原因是能够仅为数据库优化资源。
在事情即将崩溃之前,不要反规范化。
非negotiables。
这一行以上的一切都是值得怀疑的建议。如果不理解并测试它,就不要接受任何建议。每个设计决策都有两个方面;并且MySQL的在线建议比没有资格的一般化更糟糕,并且没有扩大利益和惩罚。质疑我在这里注意到的一切。了解您正在做什么,为什么要这样做,以及您期望获得什么好处。测量变化,看看发生了什么是预期的。
永远不要“尝试一些事情看看会发生什么”。这样做就像用多个化油器调整汽车,除了更糟糕。如果您没想到会发生什么事情,请退出更改并将其弄清楚,或者处理您理解的其他事情。睡觉是你的朋友;经过艰苦的测试后,大部分会在一夜之间到来。
你永远不会理解这一切;你总是需要学习比你知道的更多。总是问“为什么”和“你的证据是什么”。 (通常这是某人阅读的东西,不适用于您的情况。)
答案 2 :(得分:0)
MySQL可以处理它。真正的问题是:它能在合理的时间内处理吗?这取决于您的查询。就像Eran Galperin在他的回答中所说,考虑分区和复制以进行优化。
答案 3 :(得分:0)
正如其他人所说,MySQL可以轻松扩展以容纳非常大的数据集,并且通常它将处理大型集合(几百万行),而不需要开发人员/ dba的干预,除了一些明智的索引和查询优化。 @doofledorer在避免过早优化方面是正确的。正如37个Signals人员所说,如果你的应用程序取得了成功,你就会遇到数据库问题 - 那么这是一个很棒的地方。
但是,我会用自己的一个来反驳这个问题 - 你真的需要使用MySQL作为你的缓存系统吗?有很多地方可以容纳500个整数的列表,我的第一个选择是会话中的服务器端。即使将会话数据写入光盘,加载500个整数的数组也不会那么慢 - 并且有很多策略可以使用内存缓存(例如MemCache)来进一步加快速度。循环你的会话存储数组并执行10,20(或多页),单个查询沿着“select item。* where id = X”可能听起来很可怕 - 当然它会提高物理数量查询,但它会快速闪电,尤其是抛出一些MySQL查询缓存。
编辑:Sam的评论突出显示了我忘记的内容: 如果你使用基于会话的方法,你可以从会话是基于状态的事实中获益。您不必担心清除过期数据 - 当会话结束时,poof,它已经消失了。而且,如果你坚持使用基于光盘的会话(我在这里假设PHP作为服务器端语言),那么请记住磁盘空间非常便宜。
在一天结束时,它将成为易用性(开发/维护术语),可扩展性和性能之间的权衡。我只想说你只是因为你正在处理数据库查询的结果,并不意味着数据库是在所有情况下存储这些结果的最佳方法 - 保持开放的心态! / p>