使用redis做pv、uv、click统计

 

redis实时统计
设计思路:
1、 前端smarty插件(smarty_function_murl),将网站所有的连接生成一个urlid,后端根据获取的参数将需要的数据存入redis。
2、后端插件(smarty_function_aurl),将urlid传入redis获取数据。
3、定时将数据跑出来存入关系数据库,清除redis记录

缩减开支事业部没了,编入其它组织了,这个东西我就停工了,分享下思路和代码。

<?php
 
/**
 * 使用redis统计pv、uv、链接的点击数。每个链接都需要一个唯一的id,将这个id记录做点击数的key。类不能直接单独使用,只是提供了一个思路。为pv 500W做的测试。项目停了没有进下去,分享下思路。
 * @author [email]pigletshake@gmail.com[/email]
 * @version 
 * 策略部署两台redis主从结构,复制信息。每天程序抛出数据,5天清除一次。
 */
 
namespace mokbuy\helper;
 
class Analytics {
 
    //page view 的key;
    private $_pv_key = 'aitily::pv:::';
    //用户唯一id 的可以;
    private $_uv_key = 'aitily::uv::';
    //用户访问轨迹的key,hs=history
    private $_hs_key = 'aitily::hs::';
    //点击记录
    private $_ck_key = 'aitily::ck::';
    //url key
    private $_url_key = 'aitily::urlid::';
    //首页key
    public $_homepage_key = null;
    private $_redisr=null;
    private $_redisw=null;
    private $_key=null;
    private $_time=null;
 
    //构造函数
    public function __construct() {
       
         //连接redis
        $this->_redisr = $this->redisr(3);
        $this->_homepage_key = md5("homepage");
        $this->_time = $this->_time();
        $this->_key = date('Ymd'$this->_time);
         
    }
 
    /**
     * 保存消息
     * @param string $urid urlid
     * @param string $sid 用户id
     * [url=home.php?mod=space&uid=987628]@Return[/url] 参数不全-1,redis连接失败-2
     */
    public function s($urid=null,$sid=null) {
        if(isset($_GET['anys']))
            return false;
        //todo,还需要监测urid和sid参数的准确性
        $urlid = empty($urid)?(isset($_GET['urid'])?$_GET['urid']:md5('homepage')):$urid;
        $sid = empty($sid)?(isset($_GET['sid'])?$_GET['sid']:session_id()):$urid;
        //连接redis
        $_redisw = $this->redisw(3);
        if (!$_redisw)
            return '-2'//redis连接失败
        //获取变量信息;
        $url_request = $urlid == $this->_homepage_key ? 'homepage' : $urlid;
        //echo $url_request;
        $referer = isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : 'direct'//请求地址,第一次访问
        $agen = $_SERVER['HTTP_USER_AGENT'];
        $time = $this->_time;
        $key = $this->_key;
        $ip = self::_ip();
        //获取上一页的urlid
        $history_urlid = '0';
        if ($url_request == 'homepage') {
            $history_urlid = md5('homepage');
        } else {
            $history_url_arr = explode('&', $referer);
            if (count($history_url_arr) > 1)
                $history_urlid = str_replace('urid=''', $history_url_arr[count($history_url_arr) - 1]);
        }
        //==开始统计==
        //统计按url id做key开始第一次统计,value:上一级页面为主;for:为了按urlid统计pv
        $_redisw->zadd($this->_url_key . $urlid, $time, $referer . '::' . $ip . '::' . $time . '::' . $sid);
        //点击数统计
       // var_dump($history_urlid);
        if ($url_request != 'homepage')//如果第一次访问不会产生点击数
            $_redisw->zadd($this->_ck_key . $history_urlid, $time, $referer . "::" . $url_request . '::' . $time . '::' . $sid);
        //每天的pv统计
        $_redisw->zadd($this->_pv_key . $key, $time, $url_request . '::' . $agen . "::" . $time . '::' . $sid);
        //用户浏览历史
        $_redisw->zadd($this->_hs_key . $key . '::' . $sid, $time, $url_request . '::' . $referer . '::' . $ip . '::' . $time);
        return true;
    }
 
    /**
     * 返回 redis 实例
     * @staticvar \Redis $_redis
     * @param int $database redis数据库
     * @return \Redis
     */
    public function redisw($database = 2) {
        static $_redis_w;
        if (is_null($_redis_w) || !($_redis_w instanceof \Redis)) {
            $_redis_w = new \Redis;
            $_redis_w->pconnect("xx.xx.xx.xxx:8301");
            $_redis_w->auth('abc');
        }
        $_redis_w->select($database);
        return $_redis_w;
    }
 
    /**
     * 读 返回 redis 实例
     * @staticvar \Redis $_redis
     * @param int $database redis数据库
     * @return \Redis
     */
    public function redisr($database = 2) {
        static $_redis_r;
        if (is_null($_redis_r) || !($_redis_r instanceof \Redis)) {
            $_redis_r = new \Redis;
            $_redis_w->pconnect("xx.xx.xx.xxx:8301");
            $_redis_r->auth('abc');
        }
        $_redis_r->select($database);
        return $_redis_r;
    }
 
    // 析构函数
    public function __destruct() {
         
    }
    /**
     *按天查询pv总数
     * @param string $day 
     * 可以为空,默认是查询当天的(20121220)
     * @return int 
     */
    public function pv($day=null)
    {
       if(empty($day))
           $day = $this->_key;
        $key=$this->_pv_key.$this->_key;
        return $this->_redisr->zcard($key);
         
    }
    /*
     * 按天查询uv
     * @param string $day
     * 可以为空,默认是查询当天的(格式=20121220)
     * @return int
     */
    public function uv($day=null)
    {
       if(empty($day))
           $day = $this->_key;
        $key=$this->_hs_key.$this->_key;
        return count($this->_redisr->keys($key."*"));
    }
    /*
     * 按条件查询pv,uv数据
     * @param string $type,按类型查询,支持{pv、uv、ck}
     * @param string $key,查询的key目前支持{$urlid\},默认查询首页的key
     * @param int    $start 开始时间,时间戳格式,默认当天凌晨
     * @param init  $end  结束时间戳,默认当前时间戳
     * $return int 
     */
    public function pvslot($type='pv',$key=null,$start=0,$end=0)
    {
        $pv=$uv=0;
        $pv_arr=array();
        if(empty($key))
            $key =  $this->_homepage_key;
        if($start==0)
            $start = strtotime (date('Ymd' . '00:00:01'));
        if($end==0)
            $end = $this->_time;
        //统计pv的数据
        $pv_arr = $this->_redisr->zrangebyscore($this->_url_key.$key, $start, $end);
        $pv=count($pv_arr);
        if($type=='pv')
            return $pv;
        //统计uv的数据
        $uv_arr = array();        
        if ($pv > 0) {
            //direct::10.2.5.186::1356073254::ju82lv4girj508udt0dv6bts72
            foreach ($pv_arr as $po):
                $po_ar = explode('::', $po);
                if (!in_array($po_ar[3], $uv_arr)) {
                    $uv_arr[] = $po_ar[3];
                }
            endforeach;
         $uv = count($uv_arr);
        }
        unset($pv_arr);
        unset($uv_arr);
        if($type=='uv') 
            return $uv;
        //统计ck点击数
        if($type=='ck')
            return count($this->_redisr->zrangebyscore($this->_ck_key.$key,$start,$end)); //url pv
    }
 
 
 
 
    /*
     * ip地址
     */
    static function _ip() {
        foreach (array('HTTP_X_FORWARDED_FOR''HTTP_CLIENT_IP''REMOTE_ADDR'as $p) {
            if (!empty($_SERVER[$p])) {
                return $_SERVER[$p];
            }
        }
    }
    /**
     * 获取当前时间
     * @return int
     */
    public function _time()
    {
        !$this->_time && $this->_time = time();
        return $this->_time;
    }
 }
  
    /*
         * pv思路
         * ==按天统计==
         * r->zdd(天,时间戳,url:sid) ;
         * 统计今天的pv数:r->zcard(今天) return int;
         * 统计某天某个时间段的pv个数 r->zcount(天,时间戳开始,时间戳结束) return int;
         * 统计某个时间段的pv详细数据 r->zrangebyscore(天,开始时间戳,结束时间戳) return array;如果是月的需要另算>10天>mysql,统计
         * ==按url统计pv==
         * $_redis->zadd(url,时间戳,$ip.'::'.$sid));//value值暂时没有想法
         * 统计某个url的pv数:r->zcard(url) return int;
         * 统计某个url某个时间段的pv个数 r->zcount(url,时间戳开始,时间戳结束) return int;
         * 统计某个时间段的pv详细数据 r->zrangebyscore(url,开始时间戳,结束时间戳) return array;如果是月的需要另算>10天>mysql,统计
         * ==策略==
         * 主库负责插入操作
         * 从库1负责备份和查询。
         * 从库1负责数据到mysql的工作。
         * 从库2负责备
         * 
     * 
     */
 
?>
0 Comments
Leave a Reply