在MySQL中实现计数器字段的最佳方法是什么

时间:2009-02-08 21:17:09

标签: mysql

我想开始计算浏览网页的次数,因此需要某种简单的计数器。这样做的最佳可扩展方法是什么?

假设我有一个表Frobs,其中每一行对应一个页面 - 一些明显的选项是:

  1. 有一个unsigned int NumViews字段 得到的Frobs表 使用UPDATE Frobs SET NumViews = NumViews + 1更新每个视图。简单但不太擅长缩放,我理解它。

  2. 有一个单独的表FrobViews     其中为每个视图插入一个新行。要显示     视图数量,然后你需要做一个简单的SELECT COUNT(*) AS NumViews FROM FrobViews WHERE FrobId = '%d' GROUP BY FrobId。这不涉及任何更新,因此可以避免MyISAM表中的表锁定 - 但是,如果要显示每页上的视图数量,读取性能将受到影响。

  3. 你是怎么做到的?

    这里有一些很好的建议: http://www.mysqlperformanceblog.com/2007/07/01/implementing-efficient-counters-with-mysql/ 但我想听听SO社区的意见。

    我目前正在使用InnoDb,但我对InnoDb和MyISAM的答案感兴趣。

4 个答案:

答案 0 :(得分:3)

如果可扩展性比数字的绝对准确性对您更重要,那么您可以在应用程序中缓存视图计数一小段时间而不是在每个页面视图上访问数据库 - 例如,每100次视图只更新一次数据库

如果您的应用程序在数据库更新之间崩溃,那么显然您将丢失一些数据,但如果您可以容忍一定数量的不准确性,那么这可能是一种有用的方法。

答案 1 :(得分:3)

在页面视图中不想插入数据库。由于复制在MySQL上是单线程的,因此使用所有插入更新从属数据库时可能会遇到问题。

在我的公司,我们每天提供25M的页面浏览量,我们采取了分层方法。

视图计数器存储在一个单独的表中,其中2列(profileId,viewCounter)都是无符号整数。

对于不经常查看的项目,我们会在页面视图上更新表格。 对于经常查看的项目,我们大约1/10的时间更新MySQL。对于这两种类型,我们会在每次点击时更新Memcache int Memcache::increment ( string $key [, int $value = 1 ] )

if (pageViews < 10000) { UPDATE page_view SET viewCounter=viewCounter+1 WHERE profileId = :? }

else if ((int)rand(10) == 1) { //UPDATE page_view SET viewCounter= ?:cache_value WHERE profileId = :? }

在InnoDB中执行count(*)是非常低效的(MyISAM在索引中保持计数统计),但是MyISAM会在读取时锁定表,从而降低并发性。对50,000或100,000行进行count()计算需要很长时间。选择PK会非常快。

如果您需要更高的可扩展性,可能需要查看redis

答案 2 :(得分:2)

我会采用您的第二种方法,并将数据汇总到常规基础上的第一个解决方案表中。通过这种方式,您可以获得两种解决方案的优点。更清楚: 在每次点击时,您都会在表格中插入一行(让我们将其命名为hit_counters)。该表只有一个字段(pageid)。每隔x秒运行一个脚本(通过cronjob),它会聚合hit_counters表中的数据并将其放入第二个表(让它命名为'命中'。你有两个字段:pageid和总命中。

我不确定但imho如果你在同一页面上获得很多点击,那么innodb对解决方案1没有多大帮助:Innodb在更新时锁定行,因此该行的所有其他更新都将被延迟。

根据您编写的程序的原因,您还可以通过计算应用程序并仅每x秒更新数据库来一起批量处理更新。这只有在使用编程语言才能使用持久存储(如Java Servlets但不是PHP)

时才有效

答案 3 :(得分:0)

我做的,它可能不适用于您的场景,是在准备/返回页面上显示的数据的存储过程中,我在更新数据的同时更新表计数器 - 这样,只有一次调用服务器,它们都获取数据,并在同一次调用中更新计数器。

如果您没有使用SP,(或者如果您的页面上没有数据库数据),您可能无法使用此选项,但如果您使用此选项,则需要考虑这一点。