一、关于Redis中GEO地理位置的功能函数
redis的GEO地理位置功能在Redis3.2版本上发布,这个功能可以将用户给定的地理位置信息(经纬度)储存起来,并对这些信息进行操作,包括计算两个位置的距离。非常适合一些LBS(Location Based Service基于位置的服务)场景的使用.
笔者在多年前曾经抓取过大众点评某个城市的整站数据,当时找到了破解其经纬度加密的方法,然后在程序中需要计算两个位置之间的距离,那时是在php中用的一个function,虽然过了很久,但印象深刻,这块的计算相对复杂,其实写到php扩展中非常合适,因为不需要关注其实现并且可以提高计算速度,而现在redis直接就提供了,挺好。
Redis的 GEO 中主要有如下方法:
1.添加经纬度:
GEOADD key longitude latitude member [longitude latitude member …]
将给定的空间元素(纬度、经度、名字)添加到指定的键里面。 这些数据会以有序集合的形式被储存在键里面, 从而使得像 GEORADIUS 和 GEORADIUSBYMEMBER 这样的命令可以在之后通过位置查询取得这些元素。
2.根据key和number获取经纬度:
GEOPOS key member [member …]
从键里面返回所有给定位置元素的位置(经度和纬度)。因为 GEOPOS 命令接受可变数量的位置元素作为输入, 所以即使用户只给定了一个位置元素, 命令也会返回数组回复。
3.返回两个给定位置之间的距离:
GEODIST key member1 member2 [unit]
如果两个位置之间的其中一个不存在, 那么命令返回空值。指定单位的参数 unit 必须是以下单位的其中一个:m /km/mi/ft mi和ft分别表示英里和英尺。默认使用米作为单位。GEODIST 命令在计算距离时会假设地球为完美的球形, 在极限情况下, 这一假设最大会造成 0.5% 的误差。这和当时我们使用php计算时的算法是一样的。
4.以给定的经纬度为中心, 返回键包含的位置元素当中, 与中心的距离不超过给定最大距离的所有位置元素。
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count]
在给定以下可选项时, 命令会返回额外的信息,这几项信息应该比较有用:
WITHDIST : 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。
WITHCOORD : 将位置元素的经度和维度也一并返回。
ASC : 根据中心的位置, 按照从近到远的方式返回位置元素。
DESC : 根据中心的位置, 按照从远到近的方式返回位置元素。
5.和 GEORADIUS 命令有点一样, 都可以找出位于指定范围内的元素,但是 GEORADIUSBYMEMBER 的中心点是由给定的位置元素决定的
GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [ASC|DESC] [COUNT count]
GEORADIUSBYMEMBER 的中心点是由给定的位置元素决定的, 而不是像 GEORADIUS 那样, 使用输入的经度和纬度来决定中心点。
6.返回一个或多个位置元素的 Geohash 表示。
GEOHASH key member [member …]
返回一个数组, 数组的每个项都是一个 geohash 。 命令返回的 geohash 的位置与用户给定的位置元素的位置一一对应。
二、关于 Redis 分布式锁的应用
因业务需要,服务端有多个请求入口处理间需要进行并发控制,其中任何一个请求入口在接受到处理的时候,其它的请求入口接到的请求必须等待这个请求处理完毕之后再进行处理,因为这些请求开始处理之后,其事务开始之前的逻辑判断是某一个数据库字段的标识,而这个标识只有在整个请求执行完成后数据库字段才会变更,如果多个请求并发进入,会导致这些事务都会执行,最后出差错。
之前也有些地方使用过redis分布式锁,今天这里再记录一个这里的逻辑。
#分布式锁处理:
#1.先定义一个flag和redis key
$continue_flag = true;
$redis_key = $uid;
#为防止死锁,如果这个redis键没有设置过期时间就直接删除
$this->Redis()->ttl($redis_key) == -1 && $this->Redis()->del($redis_key);
#进入循环控制
while($continue_flag)
{
#循环获取分布式锁
if($this->Redis()->setnx($redis_key, time()))
{
#取得锁之后设置过期时间
$this->Redis()->expire($redis_key, 5);
#处理业务
$result = $this->service->doSomething($data);
#处理完毕删除锁并标记处理结束,让其它的请求可以获得锁
$this->Redis()->del($redis_key);
$continue_flag = false;
}else{
#如果没有获得锁就延迟一些时间再次尝试。
sleep(1);
}
}