zoukankan      html  css  js  c++  java
  • 使用Redis来实现LBS的应用

    原文地址

    微信、陌陌 架构方案分析

    近两年、手机应用,莫过于微信、陌陌之类最受欢迎;但实现原理,分享文章甚少。

    故,提出两种方案,供分享;不对之处,敬请留言学习。

    目标

    查找附近的某某某,由近到远返回结果,且结果中有与目标点的距离。

    针对查找附近的某某某,提出两个方案,如下:

    方案A:

    本方案前,请先阅读:基于LBS功能应用的Geohash方案,看过该文章便可简单知道;
    1、仅需每分钟将用户的经纬度,上报到数据库
    2、然后每次用户查找附近好友时,通过 LIKE 'wm3yr3%',即可获取
    缺点:稍有一定数据量,对数据库的鸭梨可想而知

    方案B:使用Redis

    策略

    假象把中国分成,若干个一平方公里的单元格

    1)、用户位置的变更,理解为一个单元格移动到另外一个单元格(或者不移动)

    2)、用户查找附近,理解为查找,自己所在方块的的所有人

    数据结构

    1)、用户基本信息 纬度、经度、GeoHash值(经纬度,仅用于后期距离计算)

    2)、单元格 集合(用户1,用户2,…)

    存储工具

    1)、redis string(key->value) 结构,存储用户基本信息

    2)、redis set(集合) 结构,以GeoHash值,前6位作为key(约表示一平方千米),存储单元格的用户群

    算法流程

    1)、更新用户信息,先删除用户原所在集合,再更新当前用户信息,最后更新当前用户所在集合

    2)、查找附近,直接查找,所在单元格集合所有用户ID

    具体实现

      * @site http://www.wubiao.info
      */
    include_once('geohash.class.php');
      
    class LBS {
        //索引长度 6位
        protected $index_len = 6;
        protected $redis;
        protected $geohash;
      
        public function __construct() {
            //redis
            $this->redis = new Redis();
            $this->redis->pconnect('127.0.0.1','6379');
            //geohash
            $this->geohash = new Geohash();
        }
        /**
        * 更新用户信息
        * @param mixed $latitude 纬度
        * @param mixed $longitude 经度
        */
        public function upinfo($user_id,$latitude,$longitude) {
            //原数据处理
            //获取原Geohash
            $o_hashdata = $this->redis->hGet($user_id,'geo');
            if (!empty($o_hashdata)) {
                //原索引
                $o_index_key = substr($o_hashdata, 0, $this->index_len);
                //删除
                $this->redis->sRem($o_index_key,$user_id);
            }
            //新数据处理
            //纬度
            $this->redis->hSet($user_id,'la',$latitude);
            //经度
            $this->redis->hSet($user_id,'lo',$longitude);
            //Geohash
            $hashdata = $this->geohash->encode($latitude,$longitude);
            $this->redis->hSet($user_id,'geo',$hashdata);
            //索引
            $index_key = substr($hashdata, 0, $this->index_len);
            //存入
            $this->redis->sAdd($index_key,$user_id);
            return true;
        }
        /**
        * 获取附近用户
        * @param mixed $latitude 纬度
        * @param mixed $longitude 经度
        */
        public function serach($latitude,$longitude) {
            //Geohash
            $hashdata = $this->geohash->encode($latitude,$longitude);
            //索引
            $index_key = substr($hashdata, 0, $this->index_len);
            //取得
            $user_id_array = $this->redis->sMembers($index_key);
            return $user_id_array;
        }
    }
    ?>
      

    性能测试

    1)模拟数据上报

      * @site http://www.wubiao.info
      */
    include_once('lbs.class.php');
      
    $b_time = microtime(true);
    $n = 0;
      
    while(1) {
        //user_id 1~1000000
        $user_id = rand(1, 1000000);
     
        //latitude 30.59773~30.726786
        $rand_latitude = rand(30597730, 30726786);
        $latitude = $rand_latitude / 1000000;
      
        //longitude 103.983192 ~104.16069
        $rand_longitude = rand(103983192, 104160690);
        $longitude = $rand_longitude / 1000000;
      
        $lbs = new lbs();
        $lbs->upinfo($user_id, $latitude, $longitude);
        $n++;
        mylog($n);
        $e_time = microtime(true);
        if(($e_time - $b_time) >= 60) {
            exit;
        }
    }
      
    function mylog($content) {
        file_put_contents('upinfo.log', $content . " ", FILE_APPEND);
    }
    ?>

      

    2)模拟附近查找

    * @site http://www.wubiao.info
      */
    include_once('lbs.class.php');
      
    $b_time = microtime(true);
    $n = 0;
      
    while(1) {
        //latitude 30.59773~30.726786
        $rand_latitude = rand(30597730, 30726786);
        $latitude = $rand_latitude / 1000000;
      
        //longitude 103.983192 ~104.16069
        $rand_longitude = rand(103983192, 104160690);
        $longitude = $rand_longitude / 1000000;
      
        $lbs = new lbs();
        $re = $lbs->serach($latitude,$longitude);
     
        $n++;
        mylog($n);
     
        $e_time = microtime(true);
        if(($e_time-$b_time) >= 60) {
            exit;
        }
    }
      
    function mylog($content) {
        file_put_contents('search.log', $content . " ", FILE_APPEND);
    }
    ?>
      

    测试环境

    vmWare虚拟机,内存256M,主频2.93GHz

    性能结果

    模拟了100W活跃用户行为,不断更新,不断查找附近好友

    1
    2
    3
    4
    5
    6
    7
    8
    //60 seconds insert
    88544
     
    //60 seconds search
    117660
     
    //成都 100W人,数据占用内存
    11.97M

    总结

    从测试结果来看,完全能满足,微信、陌陌之类的性能要求;

    尚可改进之处:

    1、Geohash,可写成PHP C扩展;或者其他Geohash实现方式

    2、Redis,内存消耗较大,可考虑redis集群方案

    3、本文仅查出本单元格用户,提高精度,可查出周围八个单元个,求交集

    4、求出结果,如需按照由远到近排序;读出Redis经纬度,利用距离公式排序方可。(可参照上一篇文章)

    问题

    1)假设我现在设定的hash长度为7 ,那一个个hash值对应一个块,如何得到这个块的坐标区间呢?

    例如,成都永丰立交的Geohash值为:wm3yr31d2524;如取7位,则为,wm3yr31;

    根据Geohash的算法,那么区间就会是 wm3yr3100000 ~ wm3yr31zzzzz;

    根据如上两值,通过“Geohash->经纬度”算出经纬度,可大致确定区间。

    2)如果用户上报的位置信息有时效性(比如:15秒内有效)如何处理?

    可以在redis存储的时候,设置有效时间

    http://www.2cto.com/weixin/201504/393787.html

  • 相关阅读:
    升级windows 11小工具
    windows 10更新升级方法
    您需要了解的有关 Oracle 数据库修补的所有信息
    Step by Step Apply Rolling PSU Patch In Oracle Database 12c RAC Environment
    Upgrade Oracle Database Manually from 12.2.0.1 to 19c
    如何应用版本更新 12.2.0.1.210420(补丁 32507738 – 2021 年 4 月 RU)
    xtrabackup 安装、备份和恢复
    Centos_Lvm expand capacity without restarting CentOS
    Centos_Lvm_Create pv vg lv and mount
    通过全备+relaylog同步恢复被drop的库或表
  • 原文地址:https://www.cnblogs.com/softidea/p/6195165.html
Copyright © 2011-2022 走看看