Mysql使用高流量数据库上的过滤器计算行数

时间:2011-06-10 06:57:25

标签: php mysql algorithm count

假设您有一个搜索表单,有多个选择字段,假设用户从下拉列表中选择一个选项,但在提交数据之前,我需要显示数据库中行的计数。

因此,假设该网站每天至少有300,000(300.000)个访问者,并且用户从访问表中选择至少40次的选项,这意味着12M ajax请求+ 12M计数查询数据库,这似乎有点太多了。

问题是如何实现快速计数(使用php(Zend Framework)和MySQL),以便数据库上额外的12M查询不会影响站点的负载。

一种解决方案是拥有一个存储所有选择字段组合及其各自计数的表(当从产品表中添加或删除产品时,存储计数的表将被更新)。虽然对于43个中的8个过滤器(选择选项)来说这不是一个好主意,但是会插入需要管理的+ 8M行。

关于如何实现这一目标的任何其他想法?

P.S。我不需要代码示例,但想法本身可以在这种情况下工作。

10 个答案:

答案 0 :(得分:7)

我可能会有一个预先计算好的表 - 正如你自己建议的那样。导入是指您有两种智能机制:

  1. 轻松查询哪些条目受哪些更改影响。
  2. 为整个表单请求提供唯一的查找字段。
  3. 如果你有实体键,8M条目就不会很重要,因为你只需要直接查找。

    我会在所有需要的地方为此表编写特定更新。即使有大量的变化,这仍然是有效的。如果操作正确,您将知道在插入/更新/删除产品时需要更新或无效的行。

    <强>旁注: 根据您的评论。如果你需要在八个地方添加代码以覆盖所有可以删除的地点 - 这可能是重构和集中某些代码的好时机。

答案 1 :(得分:4)

情景很少

  1. mysql有查询缓存,你必须打扰缓存IF the update of table is not that frequently

  2. 99%用户不会打扰匹配的结果数量,he/she just need the top few records

  3. 使用explain - 如果您发现explain将返回查询中要匹配的行数is not 100% precise,但应该足以作为粗略行计数

答案 2 :(得分:3)

不是你要求的,但由于你有很多选择,并且想要根据选项计算可用的项目,你应该看看Lucene及其分面搜索。这是为了解决这样的问题。

如果您不需要从搜索中获取最新信息,您可以使用队列系统不时地将更新和插入推送到Lucene(因此您不必为数千的Lucene打扰每天更新和插入)。

答案 3 :(得分:3)

你真的只有三个选项,没有多少搜索可能会显示第四个:

  1. 手动计算结果。 O(n)与查询时的结果总数。
  2. 存储和维护每个过滤器组合的计数。 O(1)检索计数,但需要O(2 ^ n)存储和O(2 ^ n)时间来更新记录更改时的所有计数。
  3. 缓存计数,仅在缓存中找不到它们时计算它们(按#1)。 O(1)当数据在缓存中时,否则为O(n)。
  4. 正是由于这个原因,必须扩展到无关紧要的系统 - 也就是大多数系统 - 要么限制他们将要计算的结果数量(例如,您的GMail收件箱中的项目或Google阅读器中未读取的项目),估计基于统计数据(例如,Google搜索结果计数)或两者的计数。

    我认为您可能实际上需要为您的用户准确计数,没有任何限制,但很难设想实际上可能需要的情况。

答案 4 :(得分:2)

您可以轻松优化的一些事项:

  1. 缓存您可以自己缓存的所有内容。例如,您的下拉菜单的选项是否需要由ajax调用获取?当我实现memcache时,This page回答了我的许多问题,当然memcached.org也提供了很好的文档。

  2. 提供可以静态服务的任何内容。即,不经常更改的选项可以每小时通过cron存储在一个平面文件中作为数组,并在运行时包含在脚本中。

  3. 对于任何严重的应用程序负载,具有默认配置设置的MySQL通常都是次优的,应该进行调整以适应当前任务的需要。也许请查看memory engine以获得高性能的读取权限。

  4. 你可以看一下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大小。