本帖最后由 default7 于 2010-09-28 13:30:35 编辑

解决方案 »

  1.   

    既然这样就不要用Mysql数据库统计啊,可以改成xml或者干脆文本文件啊。
      

  2.   


    xml,可以试试吗?
    但是我除了统计实时在线人数之外,还需要统计每小时平均在线人数,然后每分钟的在线人数。如果是用文本的话,那么如果同时数万人写入读取文件,那么。
    而且我要的不仅仅是那个在线人数的总数目,还需要在线人数的详细信息,IP地址,IP归属地。
      

  3.   

    貌似只有mysql才可以承受同时多个程序的写入与查询的,如果是文本的话,承受不了。。
      

  4.   

    你换一种思维吧。
    写入php 操作写入js里。
    usercode.jsvar user=new Array();
    user["admin"]="2009-01-02 16:30:32";
    user["test"]="........."然后服务器端写一个php 定时清理时间和当前时间超过多少分钟的。视为下线。同时把js里的数据更新进数据库。
    那么你所说的1万次,就变为一次了。
    至于服务端如何定时执行php 你可以考虑客户端触发(不是每个人都触发,是所有人定时总和只触发一次)。
      

  5.   

    建一个文件夹存放这些xml文件,既然要详细信息,那基本就要分开写了,同时操作会产生冲突。不妨试试吧。  我也没做过统计详细信息的。
    如果是存单独文件,二三十万的人同时在线应当每问题。
      

  6.   

    数据量不算大
    考虑维护一个实时状态的内存表,其他业务单独建表内存表中包含 
    id bigint(18),userid int(10),firstactive int(10),lastactive int(10)判定为离开的从该表中清除,保持该表数据为当前在线用户数据其他业务通过服务器端脚本定期统计获得,比如用户登录的log,平均在线统计信息等。实时在线为select count(*) from session;
      

  7.   

    我这边处理就是通过存入数据库才可以知道在线人数的。
    表session。但是如果不停的操作数据库的话,那么会导致数据库连接数太多,而导致 “数据库连接数太多”而无法让其他连接进入。现在我在优化下代码,我觉得可能真的没有其他的方法。
    因为PHP不能像C++那样读取写入内存.其实读写内存也会照成服务器的负荷,反正尽量减少负荷。
      

  8.   

    我说的意思是登录记录保存在js里,或者text里,服务器每一定时间,根据text或js里数据更新一次数据库,其实这时候更新数据库没多大意义了。基本可以脱离数据库了。
      

  9.   


    如果是这样的话,那么数据会非常不准确。
    而且服务器是必须定期清理session表离线用户的,根据用户最后发送的请求lastactive与当前时间的差值来判断。如果要达到精确的话,必须每隔1分钟或者30秒发送一次请求。
    ~~~~~~
    看了很多帖子,感觉绕到的方法还是我自己原先设计的那个,因为服务器端PHP不能主动向客户端发送信息。只能客户端请求。。所以为了证明这个用户在线,就必须不停的发送在线请求。
    为什么?
    因为有的用户不正常关机或者死机 ,那么网页里面的onunload 就不会触发。。~~~~~~~
    而现在的问题就是,如果同一时间发送在线请求的人数非常多,同时1万,另外限制每个用户最短只能1分钟发送一次,那么请求量也是会巨大的,甚至导致服务器宕机???(未经过测试)
      

  10.   

    如果你的网站够大,人数众多,并发率很高。那就专门弄一台服务器开memcached来记录当前登录状态吧。比你总去更新数据库要好的多。
      

  11.   

    不知道你的是什么需求,但是数W个同时访问,这个还是有点吓人。
    至于其他办法,什么memcached什么的,那根本就是瞎扯淡。
    共享内存,守护进程,操作锁,这样的东西可能对你有帮助,但是要自己开发的话,代价很高。
    数据库链接是个瓶颈,但是通过连接池,减小每个链接的使用时间,还是能完成你的需求的。不过每秒数万链接,这个还是不现实。
    内存表会比普通表快很多(数据更新及某些无法缓存的情况),缩短连接时间,并且没有普通表的未缓存时的慢现象,会让你的应用更稳定高效。
      

  12.   


    内存表,我现在用的就是直接的那种表。
    已经修改了。不知道下次高峰期会怎么样。
    反正统计还是有较大误差,zzzzz~~~~。
    目前的方案是网页中 每隔 50秒发送一次在线标识请求,这个请求会让数据库update 他的这个用户的最后响应时间,就这一句。然后每隔5分钟会发送一次清除session表中的离线请求。这些发送请求的代码是用Ajax写在SWF游戏所在页面的。
    如果同时有1000个用户同时这么发送,我担心的是那个 50秒发送一次的。因为5 分钟发送一次的,那个PHP文件会先检查一个txt文件的创建时间里面的分 date("i") 是否与当前时间一致,如果一致就什么都不做,如果不一致就重新写这个文件,并且执行该执行的,就是说保证了服务器只每1分钟只做一次那个处理。但是那个50秒一次的请求有点可怕。每50秒每个在线用户一个update 语句。
      

  13.   

    ajax触发的事件只完成一件事:
    如果session中没有这个用户数据则新增,有则更新lastactive。(也就是进更新session表并且仅作以上操作)。返回数据可以去某个统计表读取当前一个时间片段的在线人数值。
    用户端不再实现任何其他逻辑。
    所有其他逻辑全部通过后台定时执行程序完成。最简单的守护进程可以在定时任务中加入一个每个N分钟执行一次的php脚本。
    并且在这个php脚本的最开始做如下判断
    <?php$chknum = exec("ps aux|grep /full/path/for/your/script.php|grep -v grep|wc -l");
    if (intval($chknum) > 1)die();?>当然,这个“/full/path/for/your/script.php”要跟你定时任务中的一致
    至于你说的统计不准,这个是另外的问题,跟这个实现没关系。
    可能是你实现每个时间片统计在线人数时的逻辑出了问题
      

  14.   

    更新/新增可以用 一个sql实现
    另外,关键要检查你的程序,只在必要的时候做数据库链接。这个程序是特殊的,所以,不要在头文件中就进行数据库链接什么的。关键的问题是:
    1 尽量减少数据库链接
    2 尽量节省数据库分配链接的资源消耗
    3 尽量减少链接数据库的时间
    4 尽量减少单位时间内的数据库操作
    5 尽量减少更新操作时的锁表时间
      

  15.   

    仅对ajax访问的这个程序做特殊处理即可。
    应该考虑连接池。因为在这个平均1秒可能远超过1次的数据库链接请求,链接建立的时间就是一个不小的开销。不过,同样的,你可以仅对ajax访问的这个程序做优化。同时,你的这个应用最怕的是session被长时间锁定,1秒都不行。
      

  16.   

    不想用数据库 用 memecache 吧 把连接信息存储在内存里面 
      

  17.   


    第一次听说这个,PHP基本上不用这个啊。还是不用他了。
      

  18.   

    实时统计基本上是没有办法统计的,只能说是统计一段时间内的在线人数,如当用户一登录时,便自动把登录时间记入cookie,然后当该用户与服务器交互时,便去判断当前COOKIE时间是否在允许的时间段内,如果在,则不需要更新你的表,如果不在允许的时间段内,则更新当前用户的最后更新时间,这样,你在统计人数时,便可以直接去搜索一遍在线表(条件就是最后更新的时间小于允许时间段)就行了至于说,这些在线信息保存在哪,我觉得视情况而定,可以用MYSQL的内存表,或者上面所说的memcache.
      

  19.   


    是的,但是用户是一进入页面就直接停留的,是一个Flash网游里面,不会有开其他的网页的。
    mysql内存表,不太熟悉,看看。memcache就真的不太适合,还得另外安装。
      

  20.   

    mysql内存表,很简单,不过做之前你要非常清楚它的特性才行(也就几百字的说明,不过你要确认你理解它的优缺点了)
    memcache的,你就不用听了,如果有精力可以找个介绍看下,就会发现,用那个纯粹是扯淡。
    链接池的开发消耗会大。先尝试内存表及你本身业务优化吧。
      

  21.   


    memcache 还要另外安装,本身这样绝对会消耗更多的内存资源的。
    我没打算用它,我现在用的就是mysql的内存表来放session的,我的另外的一个帖子:http://topic.csdn.net/u/20101008/16/c5e0c6be-d3f3-48b9-8934-391e5c9a355e.html
      

  22.   

    系统:WIN NT
    mysql版本:mysql 5.1
    内存使用:826M / 18168M
    CPU使用:3%要顶住这个量,首先要搞好服务器
    系统建议改用Linux
    内存弄4~8G硬件配置先上去再说
      

  23.   

    不要使用数据库,1w用户记录ip、访问时间、用户名这几项20个byte应该足够了,20×10000 / 1000也就200k内存,生成一个二叉树就可以了。
      

  24.   

    所以,方案选择;20-30万人在线.百万人在线就不要想了,供计没几家做到那个规模的.
    一,共享内存主案:只适用于显示用户在线状态(每秒万次并发在线)
       单独文件法没试过,没思路,感觉不可行.
    二,独立服务器方案: 该服务器仅用于用户状态,30万人,每5分钟查询一次,每次一秒,可以要1000个并发.好一点的服务是能支持的.
       这里说一个内存表的问题,据说discuz当时就是用的内存表记录用户状态.
       没用过这个,但内存表是否还存在连接的问题?如果存在,也没有捷径.
       还有memcache,可以减速少查询次数据,但写数据入库可能也无法避免,所以了也没有捷径.
       
       这里有各种各样的优化法,但总体一条,就是要减少连接次数和查询时间,能从缓存中取的,就不要从数据库中取,能存客户端的,不要存服务器端.只在必要时连接.三,其他方式,只是猜一下:如果你有方法打开一个服务,这个服务一直是持续连接,而且监听一个端口,可以有多个实例,共用一个连接,可能会大幅提高查询效率.
        或者有一个通道连接,高速处理数据.等等.-------------实际操作中,有的程序有统一入口或写操作,比如你每点击一次就有feed,这个就可以 统计每分钟每小时多少人在线.这个实在.如果心跳法.可以每分钟一次,但这个只用为统计用,而不作为其他.看能否用日志的方式.向一个文件中添加,然后分析日志   
      

  25.   

    即使是discuz的产品,现在也开始关注分布式应用这一块
    这说明,即使康盛,这么成熟的产品,真正高并发时,他也顶不住
      

  26.   


    这个根本就不行的,还说他行。1.如果说的这个JS应该是写在服务器端的,但是PHP同时操作某一文件,立即出错,第一步就排除了
    2.如果说的这个JS是存在客户端的,PHP操作不了客户端的。
    3.及时这个JS存在服务器端,而且PHP同时写入这个文件并没有出错,那么如何读取呢?平台和这个是分开的,也就是管理员一打开平台就可以看到当前实时在线人数的详情,IP地址,用户名,用户ID,用户登录时间和退出时间,并且每隔一分钟汇总一次整个的在线人数,并且记录到分钟实时在线日志表中。另外一点如果这个JS是存放在客户端的,那么别人也可以通过HTTP来访问这个JS,这是一个很大安全漏洞啊。这个方法根本就不行,但是看到还有人说这个行,感觉很头晕啊~~~~~~~~
    上次测试撑到了 七八百同时在线的时候出现了 2003错误,远程数据库满了,然后发现了数据库的 max_connections=100,现在把他改为了 6000 了,但是不太肯定是否下次能够再次承受,目标是能够同时承受 2~3K人在线。这个的统计和Discuz网页的统计是不相同的。discuz只有在请求新的网页的时候才会更新,但是游戏这个是不行的,因为用户一进入网页flash游戏后就会一直停留在上面,他玩的是flash里面的,只是偶偶刷新一下网页(比如重登),然后如果不进行心跳发送,就无法判断他是否在线(有的用户关闭浏览器进程就无法向服务器端发送离线处理请求)。
      

  27.   


    出现那个mysql 2003错误,是偶偶出现过一两次。http://topic.csdn.net/u/20101008/16/c5e0c6be-d3f3-48b9-8934-391e5c9a355e.html如果PHP操作写入文件,文件会被锁定。一锁定再写入,就会延时,那么未写入成功的就不会被计入为在线。
    发送的时间间隔,如果过短,就会照成统计不准确,和需求对不上。50秒应该是很准确的了。反正这个需求还有点苛刻,要这么精确。我觉得还是读写数据库比较合适一些,因为除了统计实时在线的外,还有统计每分钟在线的(每分钟随即取样存入数据库中),另外还有其他的处理,每个登陆用户的每天总在线时长,所以肯定是必须要用到数据库的。-------------------------------------------------------------
    看了很多回帖,感觉都并没有考虑到读取(管理员查看实时在线人数)的问题,如果是在统计,但是管理员又无法查看,那么这个统计有什么意义呢?
      

  28.   

    一般来说,读都不是问题,因为读的话可缓存。写实际上也是可以缓存的。
    具体的没有操作过。只是讨论一下。等高手们解析。apache有那么多的访问题,不知它的日志是如何做的。他记下了所有的访问。另个,当你向一个文件或内存文件append方写文时,是否要加锁?memcache具说是专用的数据库缓存服务器。读缓存肯定是没有问题的。但写缓存具体用法不太详。如果可以,则可以解决写的问题。(比如,它缓存10000条语句时,一次性插入数据库)所以如果能解决,则可以用户是否在线的问题以及即时在线统计问题。
    其他的统计,你可以将数据定期从接收的心跳表里取出来,分析出用户的登录登录出时间另存即可最后,即时数据从缓存中取,其他数据根据登录登录计算
      

  29.   

    你的业务可能包括
    1 平均在线--每小时,每xx分钟平均在线人数
    2 平均在线时长,在线时长图标
    3 每次访问的在线时长记录
    4 在线人数变化曲线--每日分时段平均在线变化,每日总在线人数变化曲线
    5 当前实时在线人数差不多了,也不知道你是什么应用,其他的猜不出来。管理员查看的,其实重要的是前4点,它们是你要的业务,也就是管理员能够得到的统计数据。对于他们的查看,想什么时候看都没问题。
    第5点,管理员要看就直接跟用户的查看方式一样就行了,数据库全表 count(*)查询,最简单的查询类型。前面对你说的,在你的用户端程序中剔除1-4的业务逻辑,就是让你减轻由用户的大访问量带来的系统压力,而这些逻辑,你完全可以在后台单独开几个进程定时执行啊。管理员又没有几万个,也不会每多少秒刷一次。看来楼主思路不够清晰,呵呵。其实说白了,就是,首先你只考虑用户端,仅仅实现回传给用户当前在线人数即可。这一部分就是你的应用的核心部分。把它实现。
    然后在它的基础上看:
     哦,我现在要统计一些数据,给管理员看。哪些数据还不清楚,但所有数据都是通过session实时表以及它的变化及变化时刻而得到。
     再看看,管理员是个什么情况,他们访问是什么类型的访问。哦,人不多,少并发,非实时的统计数据查看。简单,后台跑程序定时按照各个业务逻辑统计结果并更新相关统计表。最后,仅仅给管理员展示统计表中的记录就ok了。减少他们之间的耦合。
    这样在你评估性能时,它们几乎是互不干扰的。影响最终性能的除了用户端的设计意外,关键点在于后台定时统计的那个统计程序。
      

  30.   

    另外,2小时,2万链接请求,在线数3k。
    那么首先2小时2w链接请求这个数值比较小。每秒3次,忽略
    问题在在线数3k上,如果是30秒心跳,那么,也就是每秒钟平均100次请求。峰值有可能是这个的几倍甚至更高,看实际的应用环境。首先,数据库链接数,不要设置太大,这要跟你的硬件相配合,如果导致系统内存或者cpu占用率太高,很可能导致数据库性能极度下降,会发生很可怕的事,一但发生,系统就危险了。所以,数据库的最大连接数要根据你硬件的承受能力来定。可以找找相关的文章(不要别人说多少多少的那种文章,是教你通过什么样的办法测试你当前环境能承载的最大连接数(没有明显性能下降的前提))。其次,我在之前说过了,你并没有太重视。根据上面的计算结果不难看出,如果用户端那个应用每一次访问仅需要0.1秒的连接数据库操作的时间,那么1秒钟同一个数据库链接就可以处理10个请求,那么100个数据库链接1秒就可以处理1000个请求。也就完全能够保证3k人在线毫无问题。
    再废话一句,如果0.01秒就能完成一次操作呢?
    最有价值的优化手段就在这里!
      

  31.   


    这样弄更复杂了啊,
    需求除了记录实时在线之外,还要记录每分钟记录一次在线人数的。
    如果是这样弄3个表的话,那么我如何统计当前取数据时候的在线人数呢?
    一定是在要程序里面进行一个复杂的计算了的啊而我如果只用一个表的话,那么就是 
    select count(userid) from session where unix_timstamp()-lastactive < 180 
    就可以了。而如果用这么多的地方来存的话,我觉得PHP还得连接上这几个地方,花费的时间会更长的呃。
    现在的做的就是优化,对于回帖里面的方法还没有测。
    但是我觉得要优化的话,绝对应该是要减少和数据库的查询次数的,当然也包括和其他的交互的次数。
    那个mysql内存管理的还没用过,对他不熟悉
    但是你说的具体的方案是什么呃。
    另外对于这个数据库不仅仅PHP在用他,游戏的服务器端程序也在连,而且是不会关闭。
    现在对于PHP处理请求的,如果在1秒钟的同时有10个人请求了PHP,那么PHP会同时处理这10个人的还是10个人不同的处理呢?我觉得应该是10个人不同的处理。而问服务器端的程序,貌似他们可以批量处理(C++的),好奇怪。也就是,“如果有 10个人同时进入的话,那么是同时处理这 10个人,而不是分开来处理”。
      

  32.   


    你的表的设计就非常消耗,id使用bigint而且是18位。占用的空间更大。我现在用的是int(4)。是独立的服务器的,八个CPU的。SQL语句确实还有一定的优化空间。包括执行逻辑,后来再优化了一次,才承受住了七八百人同时在线,原先一两百人同时进入就开始出问题了。
    如果心跳改为1分钟的话,那么精确度又减低了无语的需求。
    现在就是增大了max_connection,SQL语句和程序执行逻辑也在优化中。
      

  33.   

    仅提供一个思路,不解释了:考虑部署一台单独的 comet server (不知道的请 google)--------
    With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is
    hard to be sure where they are going to land, and it could be dangerous sitting under them as they
    fly overhead.
      

  34.   

    楼主的数据库知识还有待提高
    1 where中 unix_timstamp()-lastactive < 180 与lastactive >unix_timstamp()-180虽然结果一样,但效率完全不一样
    2 bigint(18) 是8字节显示18位, int(4)是4字节显示4位。其他的不想说了,楼主自己研究吧
      

  35.   


    哦,第一点应该的 ,谢谢啊。
    第二个,我原先存 unix_timestamp()的值一直用的是INT(10),但是C++说用int(4)就行了,用int(10)占得空间会更大。
    意思就是说PHP如果是用的长连接(即使用mysql_pconnect($dbhost, $dbuser, $dbpw)连接mysql而不是mysql_connect($dbhost, $dbuser, $dbpw, 1), )那么可以同时处理多个对吗?
    感觉到这里迷惑了。比如说PHP里面的代码:
    $_SGLOBALS['userid'] = intval($_SCOOKIE['userid']);如果同时处理多个的话,那么这个PHP取到的值是那一个呢,PHP应该是分开处理不同的用户的吧。已经很迷惑了,如果同时两个用户访问这个PHP程序,那么PHP是如何处理的呃。应该是同时处理的。但是处理完之后他就进程结束了吗?下次得到请求的时候会再次打开。
    C++是一直不会结束,是这样的吗,
      

  36.   


    PHP 应该是没有进程的啊,执行时一瞬间的事情,进程显示的应该是IIS/Apache的进程。
    C++ 他是有进程的。
    上一楼写的有点乱。现在对PHP同一时处理多个请求有点迷惑。
    不知道他是如何处理的。
    可否PHP连接mysql后处理完一个用户后接着处理另外一个用户?好像不行的吧。
    或者PHP批量处理同时过来的请求。那么对数据库的操作就一次了。但是PHP里面好像没有这个功能的啊。
      

  37.   

    ................
    C是有一个主进程,多个子线程/进程,主进程接受请求,然后派发给子线程/进程处理。。类似这样。处理完了变成空闲状态。再加上动态管理之类的php一次访问,apache为其创建一个子进程/线程,如果你开启了prefork就是一个新建的子进程或者空闲的子进程。这个子进程执行请求的php脚本。这个php脚本是执行一次后就被释放了。解释性脚本语言嘛所以,如果要用php完成,就是用一些已有工具如apache2.2中的连接池,或者memcached的,或者php自己共享内存自己写一个连接池。 通过(管理、)访问这个连接池,来获取当前空闲的数据库链接。以达到避免每次访问建立数据库链接的消耗。为什么c通讯程序高效,区别就在这些地方。题外话了。。不说了。
      

  38.   


    哦,那PHP应该是做不到C的那的了。
    在网上搜索了Memcache,可是没有WIN下的。貌似WIN下不适合用这个。目前的主机是WIN系统的。
    我想可否将目前的WIN主机换成Linux的,但是不清楚是否C++那边也要重新写程序(如果C++是要重新写程序,那绝对是不可能换成Linux的,因为就等于重做了)。我觉得是将我数据库和我的PHP放到Linux上面,C++远程连接mysql数据库,而不再localhost来连接,C++也放在原先的WIN系统上运行。现在我再次看了自己统计实时在线的代码,不能肯定能否承受两千同时在线数小时的能力,(估计是不能承受)。代码也没改什么地方,现在加了一个SQL执行时间的统计,如果执行时间超过了 32ms,那么记录此次SQL。从PHP记录的SQL日志上面来看,觉得实在是奇怪。
    因为简简单单的一句SQL语句,居然花了 3000 多ms的,也就是3秒多来执行,SQL语句:
    UPDATE session SET lastactive=UNIX_TIMESTAMP() WHERE userid='105569' LIMIT 1 
    我觉得太奇怪了,为什么这么简单的一句SQL语句从PHP查询他到查询结束,有的时候居然花了三四秒的时间。另外session已经由 MyISAM类型 改为 MEMORY类型 了,但是发现当session表里面完全为空的时候(被清空后),居然仍旧是占用了 92.多KB的空间。而MyISAM表清空后占得空间不超过2KB。
      

  39.   

        UPDATE session SET lastactive=UNIX_TIMESTAMP() WHERE userid='105569' LIMIT 1可能和锁有关,昨天看了一下资料。MyISAM类型有表级锁。如更要锁表。多时要等待。
    另外,更新表时如有索引可能会更新索引,这了需要时间。
      

  40.   

    玩家一请求主机A上面的PHP网页,PHP执行解析成为HTML,并且输出到客户端浏览器。
    客户端浏览器执行HTML,里面的JavaScript建立SWF对象 ,并且开始下载AS资源。
    AS资源下载完毕后,由Flash Player执行,执行SOCKET连接到主机A。主机A上面的C++检查主机A上面的数据库DB-ACC验证账号有无,如果有就传递线路IP地址给客户端AS,AS收到成功消息后断开与主机A上面的C++的连接。并且使用主机A上面的C++返回过来的线路的服务器的地址,通过SOCKET连接这个SOCKET。
    这个SOCKET这个时候是主机B上面的C++,然后主机B上面的C++验证主机B上面的数据的有没有这个角色,有就成功返回并且写入到主机B上面的内存里面。然后游戏开始进入了,主机B上面的C++程序也就将这个用户写入到主机B上面的内存去了。然后主机B上面的C++会统计这一条线路当前的在线人数。同样还有主机C(C线路),主机D(D线路)。。
      

  41.   

    不需要隨時更新mysql,只需要把數據存入內存,每間隔一段時間更新一下數據庫即可
    具體需要使用memchched 的set方法來解決,你可以詳細了解一下memcached