如何计算实时统计数据?

时间:2012-06-01 20:30:18

标签: performance memcached scalability

我有一个拥有数百万用户的网站(嗯,实际上它还没有,但让我们想象),我想计算一些统计数据,比如“过去一小时的登录”。

问题类似于此处描述的问题:http://highscalability.com/blog/2008/4/19/how-to-build-a-real-time-analytics-system.html

最简单的方法是做一个像这样的选择:

select count(distinct user_id) 
from logs
where date>='20120601 1200' and date <='20120601 1300' 

(当然其他条件可能适用于统计数据,例如每个国家/地区的登录) 当然这会非常慢,主要是因为它有数百万(甚至数千)行,并且我想在每次显示页面时查询它。

您如何汇总数据?什么应该去(mem)缓存?

编辑:我正在寻找一种方法来对数据进行去规范化,或者使缓存保持最新状态。例如,我可以在每次有人登录时增加内存中的变量,但这有助于了解登录的总量,而不是“过去一小时的登录”。希望现在更清楚了。

4 个答案:

答案 0 :(得分:2)

IMO这里更正确的方法是实现将相关计数器保存在内存中的连续计算。每次将用户添加到您的系统时,您都可以启动一个事件,该事件可以通过多种方式处理并更新最后一小时,最后一天甚至是总用户计数器。有一些很棒的框架可以做这种处理。 Twitter Storm是其中之一,另一个是GigaSpaces XAP(免责声明 - 我为GigaSpaces工作),特别是this tutorial,还有Apache S4GridGain

答案 1 :(得分:1)

如果您没有数据库,那就不要紧。我没有数百万用户,但我有一个具有多年登录值的表,其中包含一百万行和简单的统计数据,如亚秒级。对于数据库来说,一百万行并不是那么多。您不能将日期定为PK,因为您可以重复。为了最小化碎片和插入速度,使日期成为一个非集合的非唯一索引asc,这就是数据的来源。不确定你是否有数据库,但在MSSQL中你可以。索引user_id是要测试的东西。这样做会减慢插入速度,因为这是一个会碎片化的索引。如果您正在寻找一个相当紧凑的时间跨度,表扫描可能没问题。

为什么不同的user_id而不是登录是登录。

拥有一个仅每隔x秒运行一次查询的属性。即使每一秒都报告缓存的答案。如果或200页在一秒内命中该属性,肯定你不想要200个查询。如果统计数据是过去一小时仍然是有效统计信息的一秒钟陈旧信息。

答案 2 :(得分:0)

我最终使用Esper/NEsper。 Uri的建议也很有用。

Esper允许我在获取数据时计算实时统计数据。

答案 3 :(得分:0)

如果你刚刚运行日志,你可能想看看像Splunk这样的东西。

通常,如果您想要内存中和快速(实时),您可以创建登录数据的分布式缓存,并在例如驱逐后进行驱逐。 24小时,然后您可以查询该缓存,例如在过去一小时内登录。

假设登录记录类似于:

public class Login implements Serializable {
    public Login(String userId, long loginTime) {..}
    public String getUserId() {..}
    public long getLoginTime() {..}
    public long getLastSeenTime() {..}
    public void setLastSeenTime(long logoutTime) {..}
    public long getLogoutTime() {..}
    public void setLogoutTime(long logoutTime) {..}
    String userId;
    long loginTime;
    long lastSeenTime;
    long logoutTime;
}

要在24小时后支持驱逐,只需在缓存上配置到期(TTL)

<expiry-delay>24h</expiry-delay>

查询当前登录的所有用户:

long oneHourAgo = System.currentTimeMillis() - 60*60*1000;
Filter query = QueryHelper.createFilter("loginTime > " + oneHourAgo
                                        + " and logoutTime = 0");
Set idsLoggedIn = cache.keySet(query);

要查询过去一小时内登录和/或活动用户的数量:

long oneHourAgo = System.currentTimeMillis() - 60*60*1000;
Filter query = QueryHelper.createFilter("loginTime > " + oneHourAgo
                                        + " or lastSeenTime > " + oneHourAgo);
int numActive = cache.keySet(query).size();

(有关查询的更多信息,请参阅http://docs.oracle.com/cd/E15357_01/coh.360/e15723/api_cq.htm。所有这些示例均来自Oracle Coherence。)

为了充分披露,我在Oracle工作。本文中表达的观点和观点是我自己的,不一定反映我的雇主的意见或观点。