论坛首页 Java企业应用论坛

请教WEB站点统计相关设计思路

浏览 9869 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2006-10-10  
  先说系统用的框架是大家常见的SSH框架与AJAX,会的人多,招聘更容易些。目前开发的一个WEB视频分享项目,该项目中到处充斥着各种计数相关操作,如某个资源有对应的周点数、月点数、总点击数、评论数、每个分类或者标签下对应的总记录数,再就是网站流量的统计,PV值(这个计算量太大暂不实现)。老版采用的方式,就是点击一次就UPDATA一下数据库表中对应的字段值,这样站点在并发访问量大的情况下不可取,看到同行的网站大多也是采用类似(影响性能),另一种就是JSP+XML+DB方向,定时更新方法,想请问大家平时采用的是什么方式以及您的设计思路;
    另一个问题是例如评论数,目前网站的需求就是首页上有个评论数最多的视频(20条),设计中评论数一种方式就是在对应的资源表中增加一个字段用来保存对应的评论数,但这样增删评论都需要维护,这种方式相对于另一种靠程序来统计效率要高些,程序统计我的做法是因为是一对多的关系,我获得一个LIST取他的SIZE,或者写条COUNT SQL获得(影响性能).
   上述问题看似简单,但处理得不好对网站负载有一定的影响,所以请大家点评,并请教网站流量统计及PV值计算最优设计思路。谢谢,希望这个贴不会是垃圾贴,经过搜索论坛发现没有类似话题讨论,才斗胆发贴。
   发表时间:2006-10-10  
你可以内存当中保持一个点击计数器,每次更新内存的计数器,内存的计数器每达到一定的数值,例如100,就更新一次数据库。

0 请登录后投票
   发表时间:2006-10-10  
robbin 写道
你可以内存当中保持一个点击计数器,每次更新内存的计数器,内存的计数器每达到一定的数值,例如100,就更新一次数据库。



感谢Robbin,但按照上述操作,又有了疑惑了,不同类型不同资源需要在内存维护一个计数变量,如果多了就会有可能造成内存溢出,也有可能会出现丢失现象.目前我想采用的是点击一次保存到xml中,再定时更新到DB中
0 请登录后投票
   发表时间:2006-10-10  
ithero 写道
robbin 写道
你可以内存当中保持一个点击计数器,每次更新内存的计数器,内存的计数器每达到一定的数值,例如100,就更新一次数据库。



感谢Robbin,但按照上述操作,又有了疑惑了,不同类型不同资源需要在内存维护一个计数变量,如果多了就会有可能造成内存溢出,也有可能会出现丢失现象.目前我想采用的是点击一次保存到xml中,再定时更新到DB中


一个内存变量不过就是4bytes,你就是维护1000个计数器,不过才4KB内存,难道你要维护100百万个内存计数器?那也不过才4MB内存嘛。4MB内存你咋溢出?
0 请登录后投票
   发表时间:2006-10-11  
robbin 写道
ithero 写道
robbin 写道
你可以内存当中保持一个点击计数器,每次更新内存的计数器,内存的计数器每达到一定的数值,例如100,就更新一次数据库。



感谢Robbin,但按照上述操作,又有了疑惑了,不同类型不同资源需要在内存维护一个计数变量,如果多了就会有可能造成内存溢出,也有可能会出现丢失现象.目前我想采用的是点击一次保存到xml中,再定时更新到DB中


一个内存变量不过就是4bytes,你就是维护1000个计数器,不过才4KB内存,难道你要维护100百万个内存计数器?那也不过才4MB内存嘛。4MB内存你咋溢出?


这种设计是可行的,我当初没考虑一是考虑到同时对这么多变量的维护的(如各对应计数变量的标识)是不是复杂化了,再就是如果服务器或者容器因为什么原因要重启那就会造成计数丢失,还有担心这么多的变量存在于内存是否会引起越址冲突,从OO的角度上来考虑内存更多的应该是对象实例吧,这是我一些不成熟的想法,经验有限,如果采用这种设计,该如何着手呢?
0 请登录后投票
   发表时间:2006-10-11  
ithero 写道

这种设计是可行的,我当初没考虑一是考虑到同时对这么多变量的维护的(如各对应计数变量的标识)是不是复杂化了,再就是如果服务器或者容器因为什么原因要重启那就会造成计数丢失,还有担心这么多的变量存在于内存是否会引起越址冲突,从OO的角度上来考虑内存更多的应该是对象实例吧,这是我一些不成熟的想法,经验有限,如果采用这种设计,该如何着手呢?


不复杂,map结构就可以

key:  要统计点击数的资源标识
value: 递增值

至于何时更新到数据库,就有多种方案。

另一个问题是例如评论数,你已经给出了两种方案了,关键是自己权衡可靠性和速度之间的关系。

网站流量统计及PV值计算,应该采用专门的日志分析软件或者第三方服务,不是你的java应用程序应该处理的。


(晕,提交出错,重敲一遍,看来以后提交之前得按ctrl+a ,ctrl+c了)
0 请登录后投票
   发表时间:2006-10-11  
谢谢楼上热心指点,顺便问一下Robbin现在做IT咨询服务吗?有意向为企业做培训吗?我们公司打算请人来对项目组培训一下。
0 请登录后投票
   发表时间:2006-10-11  
teclogid 写道
为什么要保存到xml再保存到DB?
保存到XML比保存到DB快吗?



如果每点击一次就连接下数据库UP一下记录,这样成千上万个访问,这对系统性能的没有影响吗?保存XML中也不是什么好方式,但能通过定时更新点击数
0 请登录后投票
   发表时间:2006-10-11  
记得以前有篇关于为jiveforums 添加帖子点击数的文章,其实现方式就是robbin提及的,文章找不到了,但找到了一段代码,希望对你有帮助,其实结构很简单,用Map存储统计信息,系统定期执行批量jdbc操作,统计信息是实时更新的(从内存中取,往内存中写,效率很高),统计信息一样会持久化,并在系统启动之初被读取,惟一的缺点是,如果系统意外宕机,将丢失某个时间段的统计数据,但统计数据不像商业订单记录,因意外小部分的丢失不会影响到统计汇总和分析,所以相对百倍的性能提升,它是不足为虑的。

/**
 * <p>Title: </p>
 * <p>Description: </p>
 * <p>Copyright: Copyright (c) 2004</p>
 * <p>Company: </p>
 * @author not attributable
 * @version 1.0
 */

import com.jivesoftware.forum.database.ConnectionManager;
import com.jivesoftware.forum.*;
import com.jivesoftware.forum.util.*;
import com.jivesoftware.util.*;
import java.sql.*;
import java.util.*;

public class SingleThreadHits
{
    private static HashMap currentValue = new HashMap();
    private static HashMap updateValue = new HashMap();
    private static HashMap insertValue = new HashMap();
    private static SingleThreadHits instance = new SingleThreadHits();
    private SingleThreadHits()
    {
        init();
        TaskEngine.scheduleTask(new Runnable()
        {
            public void run()
            {
                batchInserts(); batchUpdates();
            }
        }

        , 60000, 60000);
    }

    public static SingleThreadHits getInstance()
    {
        return instance;
    }

    /**
     *
     */
    private void init()
    {
        Connection con = null;
        Statement stmt = null;
        ResultSet rs = null;
        try{
            con = ConnectionManager.getConnection();
            stmt = con.createStatement();
            rs = stmt.executeQuery("SELECT * FROM jivesinglethreadhits");
            long id = 0L;
            //int count = 0;
            while(rs.next()){
                long[] value = new long[2];
                value[0] = rs.getLong("threadID");
                value[1] = rs.getLong("visitedCount");
                currentValue.put(new Long(value[0]), value);
            }
        } catch(SQLException sqle){
        //Log.error(sqle);
        } finally{
            try{
                rs.close();
            } catch(Exception e){
            //Log.error(e);
            }
            try{
                stmt.close();
            } catch(Exception e){
            //Log.error(e);
            }
            try{
                con.close();
            } catch(Exception e){
            //Log.error(e);
            }
        }
    }

    public long getVisitedCount(ForumThread thread)
    {
        long visitedCount = 0L;
        Long id = new Long(thread.getID());
        long[] value;
        if(currentValue.containsKey(id)){
            value = (long[])(currentValue.get(id));
            visitedCount = value[1];
        } else{
            value = new long[2];
            value[0] = thread.getID();
            value[1] = 0;
            currentValue.put(id, value);
            insertValue.put(id, value);
        }
        return visitedCount;
    }

    public void addVisitedCount(ForumThread thread)
    {
        long visitedCount = 0L;
        Long id = new Long(thread.getID());
        long[] value;
        if(currentValue.containsKey(id)){
            value = (long[])(currentValue.get(id));
            value[1]++;
            if(!updateValue.containsKey(id)){
                updateValue.put(id, value);
            }
        } else{
            value = new long[2];
            value[0] = thread.getID();
            value[1] = 0L;
            currentValue.put(id, value);
            insertValue.put(id, value);
        }
    }

    private void batchInserts()
    {
        Iterator value;
        Connection con = null;
        PreparedStatement pstmt = null;
        try{
            con = ConnectionManager.getConnection();
            pstmt = con.prepareStatement("INSERT INTO jivesinglethreadhits VALUES(?,?)");
            for(value = insertValue.values().iterator(); value.hasNext(); ){
                synchronized(insertValue){
                    long result[] = (long[])value.next();
                    pstmt.setLong(1, result[0]);
                    pstmt.setLong(2, result[1]);
                    value.remove();
                    pstmt.addBatch();
                }
            }
            pstmt.executeBatch();
        } catch(SQLException sqle){
            //Log.error(sqle);
            sqle.printStackTrace();
        } finally{
            try{
                pstmt.close();
            } catch(Exception e){
                //Log.error(e);
                e.printStackTrace();
            }
            try{
                con.close();
            } catch(Exception e){
                // Log.error(e);
                e.printStackTrace();
            }
        }
    }

    private void batchUpdates()
    {
        Iterator value;
        Connection con = null;
        PreparedStatement pstmt = null;
        try{
            con = ConnectionManager.getConnection();
            pstmt = con.prepareStatement(
                "UPDATE jivesinglethreadhits SET visitedCount = ? WHERE threadID = ?");
            for(
                value = updateValue.values().iterator();
                        value.hasNext(); ){
                synchronized(updateValue){
                    long result[] = (long[])value.next();
                    pstmt.setLong(1, result[1]);
                    pstmt.setLong(2, result[0]);
                    value.remove();
                    pstmt.addBatch();
                }
            }
            pstmt.executeBatch();
        } catch(SQLException sqle){
            //Log.error(sqle);
            sqle.printStackTrace();
        } finally{
            try{
                pstmt.close();
            } catch(Exception e
                    ){
                //Log.error(e);
                e.printStackTrace();
            }
            try{
                con.close();
            } catch(Exception e){
                //Log.error(e);
                e.printStackTrace();
            }
        }
    }
}
0 请登录后投票
   发表时间:2006-10-11  
ithero 写道
谢谢楼上热心指点,顺便问一下Robbin现在做IT咨询服务吗?有意向为企业做培训吗?我们公司打算请人来对项目组培训一下。


是的,提供软件技术咨询服务,请收站内短信
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics