假设您有一个搜索表单,有多个选择字段,假设用户从下拉列表中选择一个选项,但在提交数据之前,我需要显示数据库中行的计数。
因此,假设该网站每天至少有300,000(300.000)个访问者,并且用户从访问表中选择至少40次的选项,这意味着12M ajax请求+ 12M计数查询数据库,这似乎有点太多了。
问题是如何实现快速计数(使用php(Zend Framework)和MySQL),以便数据库上额外的12M查询不会影响站点的负载。
一种解决方案是拥有一个存储所有选择字段组合及其各自计数的表(当从产品表中添加或删除产品时,存储计数的表将被更新)。虽然对于43个中的8个过滤器(选择选项)来说这不是一个好主意,但是会插入需要管理的+ 8M行。
关于如何实现这一目标的任何其他想法?
P.S。我不需要代码示例,但想法本身可以在这种情况下工作。
答案 0 :(得分:7)
我可能会有一个预先计算好的表 - 正如你自己建议的那样。导入是指您有两种智能机制:
如果你有实体键,8M条目就不会很重要,因为你只需要直接查找。
我会在所有需要的地方为此表编写特定更新。即使有大量的变化,这仍然是有效的。如果操作正确,您将知道在插入/更新/删除产品时需要更新或无效的行。
<强>旁注强>: 根据您的评论。如果你需要在八个地方添加代码以覆盖所有可以删除的地点 - 这可能是重构和集中某些代码的好时机。
答案 1 :(得分:4)
情景很少
mysql有查询缓存,你必须打扰缓存IF the update of table is not that frequently
99%
用户不会打扰匹配的结果数量,he/she just need the top few records
使用explain
- 如果您发现explain
将返回查询中要匹配的行数is not 100% precise
,但应该足以作为粗略行计数
答案 2 :(得分:3)
不是你要求的,但由于你有很多选择,并且想要根据选项计算可用的项目,你应该看看Lucene及其分面搜索。这是为了解决这样的问题。
如果您不需要从搜索中获取最新信息,您可以使用队列系统不时地将更新和插入推送到Lucene(因此您不必为数千的Lucene打扰每天更新和插入)。
答案 3 :(得分:3)
你真的只有三个选项,没有多少搜索可能会显示第四个:
正是由于这个原因,必须扩展到无关紧要的系统 - 也就是大多数系统 - 要么限制他们将要计算的结果数量(例如,您的GMail收件箱中的项目或Google阅读器中未读取的项目),估计基于统计数据(例如,Google搜索结果计数)或两者的计数。
我认为您可能实际上需要为您的用户准确计数,没有任何限制,但很难设想实际上可能需要的情况。
答案 4 :(得分:2)
您可以轻松优化的一些事项:
缓存您可以自己缓存的所有内容。例如,您的下拉菜单的选项是否需要由ajax调用获取?当我实现memcache时,This page回答了我的许多问题,当然memcached.org也提供了很好的文档。
提供可以静态服务的任何内容。即,不经常更改的选项可以每小时通过cron存储在一个平面文件中作为数组,并在运行时包含在脚本中。
对于任何严重的应用程序负载,具有默认配置设置的MySQL通常都是次优的,应该进行调整以适应当前任务的需要。也许请查看memory engine以获得高性能的读取权限。
你可以看一下these 3 great-but-very-technical posts on materialized views,事实上整个博客真的是mysql性能提示的金矿。
GOOD-luck
答案 5 :(得分:2)
我建议使用一个单独的表来缓存计数,并结合触发器。
为了使它快速,你可以使它成为一个内存表,并使用插入,删除和更新的触发器来更新它。
伪代码:
CREATE TABLE counts (
id unsigned integer auto_increment primary key
option integer indexed using hash key
user_id integer indexed using hash key
rowcount unsigned integer
unique key user_option (user, option)
) engine = memory
DELIMITER $$
CREATE TRIGGER ai_tablex_each AFTER UPDATE ON tablex FOR EACH ROW
BEGIN
IF (old.option <> new.option) OR (old.user_id <> new.user_id) THEN BEGIN
UPDATE counts c SET c.rowcount = c.rowcount - 1
WHERE c.user_id = old.user_id and c.option = old.option;
INSERT INTO counts rowcount, user_id, option
VALUES (1, new.user_id, new.option)
ON DUPLICATE KEY SET c.rowcount = c.rowcount + 1;
END; END IF;
END $$
DELIMITER ;
计数的选择将是即时的,并且触发器中的更新不会花费很长时间,因为您正在使用具有O(1)查找时间的哈希索引的内存表。
<强>链接:强>
记忆引擎:http://dev.mysql.com/doc/refman/5.5/en/memory-storage-engine.html
触发器:http://dev.mysql.com/doc/refman/5.5/en/triggers.html
答案 6 :(得分:0)
据推测,你正在使用ajax来调用你正在谈论的后端。使用某种chached flat文件作为数据的中间件。设置5秒的过期时间或任何适当的时间。将数据文件命名为query key = value string。在ajax请求中,如果数据文件的时间早于冷却时间,则刷新(如果不是),使用存储在数据文件中的值。
另外,您可能低估了mysql查询缓存机制的强度。如果您正在使用mysql查询缓存,我怀疑与我刚刚描述的方式相比,会有任何显着的性能下降。如果查询被mysql查询缓存,那么实际上唯一的减速效果将来自应用程序和mysql之间的网络层。
答案 7 :(得分:0)
考虑复制可以在您的架构中扮演什么角色。如果需要向外扩展,可以考虑将表从InnoDB复制到MyISAM。如果您正在进行count(*)
查询,MyISAM引擎会自动维护一个表计数。如果您正在进行count(col) where
次查询,那么您需要严重依赖设计良好的指标。在这种情况下,您的计数查询可能会像这样形成:
alter table A add index ixA (a, b);
select count(a) using from A use index(ixA) where a=1 and b=2;
答案 8 :(得分:0)
我觉得因为似乎没有其他人这样做而感到疯狂,但你考虑过客户端缓存吗? JavaScript在处理大型列表时并不可怕,特别是如果它们是相对简单的列表。
我知道您的理想是您希望数字完全准确,但启发式是您的朋友,特别是因为同步永远不会是100% - 由于服务器端流量导致连接速度慢或延迟高将使AJAX请求过时,特别是如果该数据不是常量。 如果数据可以被其他用户编辑,则使用AJAX是不可能的同步性。如果任何人都无法编辑,那么客户端缓存将会起作用,并且可能是您最好的选择 。哦,如果你正在使用某种端口连接,那么推送到服务器的任何东西都可以简单地更新所有其他客户端,直到可以完成同步。
如果您愿意采用这种形式的缓存,您也可以将结果缓存在服务器上,只需定期刷新查询。
答案 9 :(得分:0)
正如其他人所说,你真的需要在服务器端使用某种缓存机制。无论是MySQL表还是memcache,都可以。但是为了减少对服务器的调用次数,请在一个请求中检索缓存计数的完整列表,并在javascript中本地缓存。这是消除差不多12M服务器命中率的一种非常简单的方法。
您甚至可以将计数信息存储在一小时内过期的cookie中,因此后续页面加载不需要再次查询。如果您不需要实时数字,那就是这样。
许多最新的浏览器也支持本地存储,它不会像cookie一样传递给服务器。
您可以将大量数据放入1-2K json数据结构中。因此,即使您有数千种可能的计数选项,它仍然比您的典型图像小。如果您使用cookie缓存,请记住最大cookie大小。