场景解决方案-附近的人(GeoHash的应用)

栏目: 数据库 · 发布时间: 5年前

内容简介:想要不拖垮数据,要做到能走索引。就是跟你无关的点,不要扫描。减少扫描行数来实现减轻数据库的压力。那么减少扫描行数肯定要想到索引。可是经纬度有两个字段,且查询条件无论怎么写都没办法走索引。那么唯一能想到的就是二维变一维。 geohash基本原理是将地球理解为一个二维平面,将平面递归分解成更小的子块,每个子块在一定经纬度范围内拥有相同的编码,这种方式简单粗暴,可以满足对小规模的数据进行经纬度的检索。如图,当前缀码相同为7相差76米左右,为8相差19米,为9的话可以近似理解为那个人就在你身边了。

前言

附近的人 ,这四个字的需求就大有文章可做了。很二逼的做法是,存每个人的经度纬度,然后遍历数据库所有数据,foreach循环,两点距离坐标公式。量少的时候,这个没啥问题。量大了,扫描全表 + 经纬度距离运算分分钟拖垮数据库。那么是否有方案可以解决这个痛点呢,今年就来说下 Geohash

实现思路

想要不拖垮数据,要做到能走索引。就是跟你无关的点,不要扫描。减少扫描行数来实现减轻数据库的压力。那么减少扫描行数肯定要想到索引。可是经纬度有两个字段,且查询条件无论怎么写都没办法走索引。那么唯一能想到的就是二维变一维。 geohash基本原理是将地球理解为一个二维平面,将平面递归分解成更小的子块,每个子块在一定经纬度范围内拥有相同的编码,这种方式简单粗暴,可以满足对小规模的数据进行经纬度的检索。 两个点的距离越近,他们的编码前缀部分就相同,前缀部分相同越多,代表距离越近 。然后我们做数据库扫描的时候 可以 WHERE geohash Like 'code%' 这样就起到了走索引从而优化了执行效率。

代码思路(PHP)

class Geohash {
    private $coding = "0123456789bcdefghjkmnpqrstuvwxyz";
    private $codingMap = array();
 
    public function Geohash() {
        for($i = 0; $i < 32; $i++) {
            $this->codingMap[substr($this->coding, $i, 1)] = str_pad(decbin($i), 5, "0", STR_PAD_LEFT);
        }
    }
 
    public function decode($hash) {
        $binary = "";
        $hl = strlen($hash);
        for($i = 0; $i < $hl; $i++) {
            $binary .= $this->codingMap[substr($hash, $i, 1)];
        }
 
        $bl = strlen($binary);
        $blat = "";
        $blong = "";
        for ($i = 0; $i < $bl; $i++) {
            if ($i%2) {
                $blat = $blat.substr($binary, $i, 1);
        } else {
                $blong = $blong.substr($binary, $i, 1);
        }
        }
 
        $lat = $this->binDecode($blat, -90, 90);
        $long = $this->binDecode($blong, -180, 180);
 
        $latErr = $this->calcError(strlen($blat), -90, 90);
        $longErr = $this->calcError(strlen($blong), -180, 180);
 
        $latPlaces = max(1, -round(log10($latErr))) - 1;
        $longPlaces = max(1, -round(log10($longErr))) - 1;
 
        $lat = round($lat, $latPlaces);
        $long = round($long, $longPlaces);
 
        return array($lat,$long);
    }
 
    public function encode($lat,$long) {
        $plat = $this->precision($lat);
        $latbits = 1;
        $err = 45;
        while($err > $plat) {
            $latbits++;
            $err/ = 2;
        }
 
        $plong = $this->precision($long);
        $longbits = 1;
        $err = 90;
        while($err > $plong) {
            $longbits++;
            $err /= 2;
        }
        $bits = max($latbits,$longbits);
        $longbits = $bits;
        $latbits = $bits;
        $addlong = 1;
        while (($longbits+$latbits) % 5 != 0) {
            $longbits += $addlong;
            $latbits += !$addlong;
            $addlong = !$addlong;
        }
        $blat = $this->binEncode($lat, -90, 90, $latbits);
        $blong = $this->binEncode($long, -180, 180, $longbits);
        $binary = "";
        $uselong = 1;
        while (strlen($blat)+strlen($blong)) {
            if ($uselong) {
                $binary = $binary.substr($blong, 0, 1);
                $blong = substr($blong, 1);
            } else {
                $binary = $binary.substr($blat, 0, 1);
                $blat = substr($blat, 1);
            }
            $uselong = !$uselong;
        }
        $hash = "";
        for ($i = 0; $i < strlen($binary); $i += 5) {
            $n = bindec(substr($binary, $i, 5));
            $hash = $hash . $this->coding[$n];
        }
        return $hash;
    }
 
    private function calcError($bits, $min, $max) {
        $err = ($max - $min) / 2;
        while ($bits--) {
            $err /= 2;
    }
        return $err;
    }
 
    private function precision($number) {
        $precision = 0;
        $pt = strpos($number,'.');
        if ($pt! == false) {
            $precision = -(strlen($number) - $pt - 1);
        }
        return pow(10, $precision) / 2;
    }
 
    private function binEncode($number, $min, $max, $bitcount) {
        if ($bitcount == 0) {
            return "";
    }
        $mid = ($min + $max) / 2;
        if ($number > $mid) {
            return "1" . $this->binEncode($number, $mid, $max, $bitcount - 1);
        } else {
            return "0" . $this->binEncode($number, $min, $mid, $bitcount - 1);
    }
    }
 
    private function binDecode($binary, $min, $max) {
        $mid = ($min + $max) / 2;
 
        if (strlen($binary) == 0) {
            return $mid;
    } 
        $bit = substr($binary, 0, 1);
        $binary = substr($binary, 1);
 
        if ($bit == 1) {
            return $this->binDecode($binary, $mid, $max);
        } else {
            return $this->binDecode($binary, $min, $mid);
        }
    }
}

精度值

如图,当前缀码相同为7相差76米左右,为8相差19米,为9的话可以近似理解为那个人就在你身边了。

场景解决方案-附近的人(GeoHash的应用)


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

科学的极致:漫谈人工智能

科学的极致:漫谈人工智能

集智俱乐部 / 人民邮电出版社 / 2015-7 / 49.00元

集智俱乐部是一个从事学术研究、享受科学乐趣的探索者组成的团体,倡导以平等开放的态度、科学实证的精神进行跨学科的研究与交流,力图搭建一个中国的“没有围墙的研究所”。这些令人崇敬的、充满激情与梦想的集智俱乐部成员将带你了解图灵机模型、冯•诺依曼计算机体系结构、怪圈与哥德尔定理、通用人工智能、深度学习、人类计算与自然语言处理,与你一起展开一场令人热血沸腾的科学之旅。一起来看看 《科学的极致:漫谈人工智能》 这本书的介绍吧!

MD5 加密
MD5 加密

MD5 加密工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器