前言附近的人,这四个字的需求大有可为。最牛逼的方式就是存储每个人的经纬度,然后遍历数据库中的所有数据,foreach循环,两点距离的坐标公式。少量时,这没问题。量大的话,全表扫描+经纬度距离计算,分分钟拖垮数据库。那么这个痛点有解决方案吗?今年,我们来谈谈Geohash的实现。想要不拖累数据,就必须会用索引。不要扫描与您无关的点。减少扫描的行数以减轻数据库的压力。那么减少扫描行数一定要想到索引。但是,有经纬度两个字段,查询条件怎么写都没有办法去索引。那么唯一能想到的就是二维到一维。geohash的基本原理是将地球理解为一个二维平面,递归地将平面分解成更小的子块。每个子块在一定的经纬度范围内具有相同的代码。这种方法简单粗暴,可以满足小规模数据的经纬度搜索。两点之间的距离越近,它们的编码前缀相同,前缀越相同则距离越近。那么我们在扫描数据库的时候,可以使用WHEREgeohashLike'code%'来进行索引,优化执行效率。代码思路(PHP)-90,'max'=>90)){$count=($data['max']-$data['min'])/2;$limit_0=array('min'=>$data['min'],'max'=>$data['min']+$count);$limit_1=array('min'=>$data['min']+$count,'max'=>$data['max']);$str.=$num>$limit_1分钟']?1:0;如果($i>=$max_separate_num){返回$str;}else{returnself::separate($num,$str,$i+1,$max_separate_num,$num>$limit_1['min']?$limit_1:$limit_0);}}/***@param$latitude_str纬度*@param$longitude_str经度*/publicstaticfunctioncombination($latitude_str,$longitude_str){$str='';for($i=0;$i=precision$str.=$longitude_str{$i};如果(isset($latitude_str{$i})){$str.=$latitude_str{$i};}}返回$str;}publicstaticfunctiongeohash_encode($str){$str_arr=str_split($str,5);//按5位拆分字符串$encode_str='';foreach($str_arras$va){$decimal=bindec($va);$encode_str.=self::BASE32[$decimal];}返回$encode_str;}/***编码*//***解码*/publicstaticfunctiongeohash_decode($str){//按照一位字符串拆分$str_arr=str_split($str,1);$decode_str='';$base32=array_flip(self::BASE32);foreach($str_arras$va){$decode_str.=str_pad(decbin($base32[$va]),5,'0',STR_PAD_LEFT);}返回(字符串)$decode_str;}/***解码二进制组合*@param$str*@returnarray*/publicstaticfunctionde_combination($str){$latitude_str='';$longitude_str='';//按两位数字符串切$str_arr=str_split($str,2);foreach($str_arras$va){$longitude_str.=$va[0];if(isset($va[1])){//根据精度表可以发现维度>=precision$latitude_str.=$va[1];}}返回数组(self::LATITUDE=>$latitude_str,self::LONGITUDE=>$longitude_str,);}/***解码二进制区间*@param$str*@paramstring$i//执行次数*@paramarray$data,interval*/publicstaticfunctionde_separate($str,$i=1,$data=array('min'=>-90,'max'=>90)){$count=($data['max']-$data['min'])/2;$limit_0=array('min'=>$data['min'],'max'=>$data['min']+$count);$limit_1=array('min'=>$data['min']+$count,'max'=>$data['max']);如果($str[$i-1]==0){$data=$limit_0;}else{$data=$limit_1;}if($i>=strlen($str)){返回$data;}else{returnself::de_separate($str,$i+1,$data);}}/***解码*//***根据精度获取二分法个数*@param$level*@param$type*/publicstaticfunctionget_precision_level_num($level,$type=self::LATITUDE){$precision=array(1=>array(self::纬度=>2,self::LONGITUDE=>3,),2=>array(self::LATITUDE=>5,self::LONGITUDE=>5,),3=>array(self::LATITUDE=>7,self::LONGITUDE=>8,),4=>array(self::LATITUDE=>10,self::LONGITUDE=>10,),5=>array(self::LATITUDE=>12,self::LONGITUDE=>13,),6=>array(self::LATITUDE=>15,self::LONGITUDE=>15,),7=>array(self::LATITUDE=>17,self::LONGITUDE=>18,),8=>数组(self::LATITUDE=>20,self::LONGITUDE=>20,),9=>数组(self::LATITUDE=>22,self::LONGITUDE=>23,),10=>array(self::LATITUDE=>25,self::LONGITUDE=>25,),11=>array(self::LATITUDE=>27,self::LONGITUDE=>28,),12=>array(self::LATITUDE=>30,self::LONGITUDE=>30,),);返回$precision[$level][$type];}/***获取区域*@param$type*@returnmixed*/publicstaticfunctionget_interval_value($type=self::LATITUDE){$interval=array(self::LATITUDE=>array('min'=>-90,'max'=>90),self::LONGITUDE=>array('min'=>-180,'max'=>180),);返回$interval[$type];}}accuracy值如图所示。字首码相同时,字首码为7时相差约76米,字首码为8时相差19米。如果为9,则大致可以理解为人在你身边