【写在前面】
获取附近的人列表,首先要有用户的位置信息,做法是客户端调用一个接口传递用户的经纬度坐标、所在区域adcode等,把这些用户位置信息入库(当然前提是用户开启定位权限)。
有了用户位置信息库,可以通过一个用户的经纬度坐标,获取其附近的用户,实现附近的人功能,通常会按照距离正序排列,还会有男女性别的筛选。
起初做这个功能,由于对项目时间成本的考虑,以方便省事为原则,通过用户经纬度坐标计算出4个临界点坐标,使用mysql进行条件查询,一开始还行,随着用户量增多,大概不到一万用户mysql就扛不住了,查询速度巨慢,占用内存也很高,因此要换一种实现方案。所以第一次做这个功能的朋友,就直接放弃mysql吧,这是前车之鉴。
【实现方案】
Redis的GEO提供对地理位置的一些操作,这里只用到以下两个操作:
GEOADD:将指定的地理空间位置(纬度、经度、名称)添加到指定的key中
GEORADIUS: 以给定的经纬度为中心, 找出某一半径内的元素
composer require predis/predis
'127.0.0.1',
'port' => 6379,
// 'password' => 'xxxxxx'
]);
$client->geoadd($key,$longitude,$latitude,$user_id);
//省略其它逻辑......
}
}
/**
* 获取附近的人
* @param int $user_id 用户id
* @param string $latitude 纬度
* @param string $longitude 经度
* @return object
*/
public function nearbyList($user_id, $latitude, $longitude){
$key = 'geo::user';
$client = new Client([
'host' => '127.0.0.1',
'port' => 6379,
// 'password' => 'xxxxxx'
]);
$nearbyList = $client->georadius($key, $longitude, $latitude, 50000, 'm', ['withdist' => true, 'sort' => 'asc']);
//半径50000、单位m/km可选、距离显示、排序规则
//省略其它逻辑......
}
到此实现了使用Redis GEO做附近的人功能,但也存在一些问题需要思考:
一是针对条件查询,例如男、女的筛选,我的做法是在存入的时候,给成员标识拼上性别,但是这样会增加数据处理的复杂度,条件越多会越复杂。
//$gender 0保密 1女 2男
$client->geoadd($key,$longitude,$latitude, $user_id.'-'.$gender);
二是出于分页的考虑,现在是每次都要把全部数据得到,然后进行数组分页,即便加一些缓存,但还是会觉得欠妥当。
附数组分页封装函数:
/**
* 数组分页
* @param $arr
* @param int $page 页码
* @param int $size 每一页的条数
* @return array $array 分页数组
*/
function cutpage($arr, $page = 1, $size = 10)
{
$arr = array_slice($arr, ($page - 1) * $size, $size);
return $arr;
}