+ -
当前位置:首页 → 问答吧 → PHP+MongoDB组合来做访问统计

PHP+MongoDB组合来做访问统计

时间:2010-07-25

来源:互联网



今天好不容易休息一天,想到前几天看到的mongodb。。就来玩一玩啊。
MongoDB是一款现在非常流行的NoSql引擎。我看上的原因有两点。
1、完美支持php。非常丰富的接口。
2、高并发的插入和写入对比mysql有极大的优势。我测试过。搜索平均是2.5倍。插入和update是10倍左右。数据就不贴上来了。
但也有很多限制。32位系统,单文件大小不能超过2G。。。插入数据的时候比较依赖内存,内存太小得把虚拟内存搞大点或者完全删除掉虚拟内存的限制
安装、介绍以及手册:www.mongodb.com   资源非常丰富。
上代码
  1. class MDBBrowser{
  2.         //链接
  3.         private $connect;

  4.         private $db = null;
  5.         protected $table_name = "statistic";
  6.         protected $collection = "browser";
  7.         protected $memStorage = null;

  8.         //服务器信息
  9.         private $config = array(
  10.                                                 "host"     => "localhost",  //服务器
  11.                                                 "user"     => "sampeng",    //用户名
  12.                                                 "password" => "这里是你的密码",   //密码
  13.                                                 "unique"   => false         //唯一性
  14.                                                  );
  15.                                                 
  16.         public function __construct(){
  17.                 $this->connect();
  18.                 $this->selectDB();
  19.                 if($this->config['unique']){
  20.                         //TODO增加用户唯一性
  21.                         $this->intMemStorage();
  22.                 }
  23.         }
  24.         /**
  25.          * 选择数据库
  26.          * @param $name 数据库名
  27.          * @return 返回collection对象
  28.          */
  29.         public function table($name){
  30.                 $this->collection = $name;
  31.                 return $this->collection;
  32.         }
  33.         
  34.         /**
  35.          * 存储方法
  36.          * 将appid的需要累计加1的字段自动加1
  37.          * @param  $appid        appid。在命名空间下的应用id
  38.          * @param  $uid          uid.做该操作的用户。这个是用来做限制判断的
  39.          * @param  $field        需要计数的count字段。比如reader。dig。follow等等
  40.          * @param  $namespace    命名空间
  41.          */
  42.         public function storage($appid,$uid,$field = "reader",$namespace = "default"){
  43.                 $this->insertAndUpdateTime();
  44.                
  45.                 if(! ( $this->config['unique'] && $this->checkUnique($uid,$appid,$namespace)) ) {
  46.                         //检查该项是否存在
  47.                         $count = $this->db->count(array('ns'=>$namespace,'appid'=>$appid));
  48.                         if(0 == $count){
  49.                                 $data['ns'] = $namespace;
  50.                                 $data['appid'] = $appid;
  51.                                 $save[$field] = 0;
  52.                                 $result = $this->db->save($data,$save);
  53.                         }else{
  54.                                 $data['ns'] = $namespace;
  55.                                 $data['appid'] = $appid;
  56.                                 $result = $this->db->update($data,array('$inc'=>array($field=>1)));
  57.                                 unset($data);
  58.                         }
  59.                 }
  60.         }
  61.         
  62.         /**
  63.          * 获得命名空间下的某个资源的统计计数
  64.          * @param $appid
  65.          * @param $field        允许逗号分隔
  66.          * @param $namespace
  67.          */
  68.         public function getCount($appid,$field = 'reader',$namespace = "default"){
  69.                         $map['ns'] = $namespace;
  70.                         $map['appid'] = $appid;
  71.                         $fields = explode(',',$field);
  72.                         $data = $this->db->findOne($map,$fields);
  73.                         array_shift($data);
  74.                         if(1 == count($fields)){
  75.                                 $data = $data[$field];
  76.                         }
  77.                         return $data;
  78.         }
  79.         
  80.         /**
  81.          * 给定一个值,单位秒。检查是否超时。
  82.          * 这个是全局的,没有做每个命名空间下的超时检查
  83.          * @param $checkTime 单位秒
  84.          */
  85.         public function checkUpdateTime($checkTime = 300){
  86.                 $map['status'] = "extra";
  87.                 $data = $this->db->findOne($map,array('insertTime','updateTime'));
  88.                 return $data['updateTime'] - $data['insertTime'] >= $checkTime;
  89.         }

  90.         /**
  91.          * 将指定命名空间的所有数据弹出来。返回数组。
  92.          * 此操作将更新初始存储的insertTime和updateTime
  93.          *
  94.          * @param $namespace  stirng|false false时是所有的数据
  95.          * @param $remove     false|true  true时所有历史数据
  96.          */
  97.         public function popNsData($namespace,$remove = false){
  98.                
  99.                 if(!$namespace){
  100.                         //所有ns下的统计数据都取出来
  101.                         $map['status']['$ne'] ='extra';
  102.                 }else{
  103.                         //只取一部分
  104.                         $map['ns'] = $namespace;
  105.                 }
  106.                 $data = $this->db->find($map);
  107.                 $result = array();
  108.                 while($data->hasNext()){
  109.                         $data->next();
  110.                         $tempData = $data->current();
  111.                         $appId = $tempData['appid'];
  112.                         unset($tempData['_id']);
  113.                         $result[$tempData['ns']][$appId] = $tempData;
  114.                 }
  115.                 if(!empty($result)){
  116.                         if($remove){
  117.                                 $this->db->remove($map);
  118.                         }
  119.                         //修改更新时间
  120.                         $statusWhere['status'] = "extra";
  121.                         
  122.                         $time = time();
  123.                         $statusSave['insertTime'] = $time;
  124.                         $statusSave['updateTime'] = $time;
  125.                         $this->db->update($statusWhere,$statusSave);
  126.                 }
  127.                 return $result;
  128.         }

  129.         protected function connect(){
  130.                 $config = $this->config;
  131.                 $this->connect = new Mongo("mongodb://{$config['user']}:{$config['password']}@{$config['host']}");
  132.                 return $this;
  133.         }
  134.         
  135.         protected function selectDB(){
  136.                 $database = $this->connect->selectDB($this->table_name);        
  137.                 $this->db = $database->selectCollection($this->collection);
  138.                 return $this->db;
  139.         }
  140.         
  141.         protected function insertAndUpdateTime(){
  142.                 $time = time();
  143.                 $count = $this->db->count();
  144.                 //判断是否有数据
  145.                 if(0 == $count){
  146.                         $data['status'] = "extra";
  147.                         $data['insertTime'] = $time;
  148.                         $data['updateTime'] = $time;
  149.                         $this->db->insert($data);
  150.                 }else{
  151.                         $where['status'] = "extra";
  152.                         $save['$set']['updateTime'] = $time;
  153.                         $this->db->update($where,$save);
  154.                 }
  155.         }
  156.         
  157. }
复制代码

我的存储结构是这样的
{
  "_id": "4c4be8437f8b9a1b36000000",
  "appid": 1,
  "ns": "default",
  "reader": 15
}
{
  "_id": "4c4bd4197f8b9a9832000000",
  "insertTime": 1280042037,
  "updateTime": 1280042037
}


我想的是还可以这样

{

"_id":XXX

"ns":"default”,

"info1":

       {

         appid:XX,

         reader:XXX

      }

}

就是一个namespace就只用一个数据集完全存储。。查询倒是可以。。只是我觉得这样比较麻烦。扩展也不方便。。还是用平铺的方式存储就够了。。过多的设计就是过度设计- -
比如这个帖子的浏览数就可以这样增加
$a = new MDBBrowser();
$a->storage(1,2,'reader','bbs');

支持和反对的ajax接受方这样处理
$a = new MDBBrowser();
$a->storage(1,2,'dig','bbs');

等等等等。。。

作者: 某个人   发布时间: 2010-07-25

自己先把沙发拿了。。。代码格式化怎么变这鬼样了?

作者: 某个人   发布时间: 2010-07-25