设为首页 收藏本站
查看: 1039|回复: 0

[经验分享] PHP+MongoDB组合来做访问统计

[复制链接]

尚未签到

发表于 2017-4-4 10:47:10 | 显示全部楼层 |阅读模式
MongoDB是一款现在非常流行的NoSql引擎。我看上的原因有两点。
1、完美支持php。非常丰富的接口。
2、高并发的插入和写入对比mysql有极大的优势。我测试过。搜索平均是2.5倍。插入和update是10倍左右。数据就不贴上来了。
但也有很多限制。32位系统,单文件大小不能超过2G。。。插入数据的时候比较依赖内存,内存太小得把虚拟内存搞大点或者完全删除掉虚拟内存的限制
安装、介绍以及手册:www.mongodb.com   资源非常丰富。
上代码

class MDBBrowser{
//链接
private $connect;
private $db = null;
protected $table_name = "statistic";
protected $collection = "browser";
protected $memStorage = null;
//服务器信息
private $config = array(
"host"     => "localhost",  //服务器
"user"     => "sampeng",    //用户名
"password" => "这里是你的密码",   //密码
"unique"   => false         //唯一性
);
public function __construct(){
$this->connect();
$this->selectDB();
if($this->config['unique']){
//TODO增加用户唯一性
$this->intMemStorage();
}
}
/**
* 选择数据库
* @param $name 数据库名
* @return 返回collection对象
*/
public function table($name){
$this->collection = $name;
return $this->collection;
}
/**
* 存储方法
* 将appid的需要累计加1的字段自动加1
* @param  $appid        appid。在命名空间下的应用id
* @param  $uid          uid.做该操作的用户。这个是用来做限制判断的
* @param  $field        需要计数的count字段。比如reader。dig。follow等等
* @param  $namespace    命名空间
*/
public function storage($appid,$uid,$field = "reader",$namespace = "default"){
$this->insertAndUpdateTime();
if(! ( $this->config['unique'] && $this->checkUnique($uid,$appid,$namespace)) ) {
//检查该项是否存在
$count = $this->db->count(array('ns'=>$namespace,'appid'=>$appid));
if(0 == $count){
$data['ns'] = $namespace;
$data['appid'] = $appid;
$save[$field] = 0;
$result = $this->db->save($data,$save);
}else{
$data['ns'] = $namespace;
$data['appid'] = $appid;
$result = $this->db->update($data,array('$inc'=>array($field=>1)));
unset($data);
}
}
}
/**
* 获得命名空间下的某个资源的统计计数
* @param $appid
* @param $field        允许逗号分隔
* @param $namespace
*/
public function getCount($appid,$field = 'reader',$namespace = "default"){
$map['ns'] = $namespace;
$map['appid'] = $appid;
$fields = explode(',',$field);
$data = $this->db->findOne($map,$fields);
array_shift($data);
if(1 == count($fields)){
$data = $data[$field];
}
return $data;
}
/**
* 给定一个值,单位秒。检查是否超时。
* 这个是全局的,没有做每个命名空间下的超时检查
* @param $checkTime 单位秒
*/
public function checkUpdateTime($checkTime = 300){
$map['status'] = "extra";
$data = $this->db->findOne($map,array('insertTime','updateTime'));
return $data['updateTime'] - $data['insertTime'] >= $checkTime;
}
/**
* 将指定命名空间的所有数据弹出来。返回数组。
* 此操作将更新初始存储的insertTime和updateTime
*
* @param $namespace  stirng|false false时是所有的数据
* @param $remove     false|true  true时所有历史数据
*/
public function popNsData($namespace,$remove = false){
if(!$namespace){
//所有ns下的统计数据都取出来
$map['status']['$ne'] ='extra';
}else{
//只取一部分
$map['ns'] = $namespace;
}
$data = $this->db->find($map);
$result = array();
while($data->hasNext()){
$data->next();
$tempData = $data->current();
$appId = $tempData['appid'];
unset($tempData['_id']);
$result[$tempData['ns']][$appId] = $tempData;
}
if(!empty($result)){
if($remove){
$this->db->remove($map);
}
//修改更新时间
$statusWhere['status'] = "extra";
$time = time();
$statusSave['insertTime'] = $time;
$statusSave['updateTime'] = $time;
$this->db->update($statusWhere,$statusSave);
}
return $result;
}
protected function connect(){
$config = $this->config;
$this->connect = new Mongo("mongodb://{$config['user']}:{$config['password']}@{$config['host']}");
return $this;
}
protected function selectDB(){
$database = $this->connect->selectDB($this->table_name);        
$this->db = $database->selectCollection($this->collection);
return $this->db;
}
protected function insertAndUpdateTime(){
$time = time();
$count = $this->db->count();
//判断是否有数据
if(0 == $count){
$data['status'] = "extra";
$data['insertTime'] = $time;
$data['updateTime'] = $time;
$this->db->insert($data);
}else{
$where['status'] = "extra";
$save['$set']['updateTime'] = $time;
$this->db->update($where,$save);
}
}
}

我的存储结构是这样的
{
  "_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');
等等等等。。。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-359976-1-1.html 上篇帖子: 一个php购物车的例子 下篇帖子: PHP字符串处理函数(二)
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表