目录
一、大数据统计之GEO
1.1、面试题
移动互联网时代LBS应用越来越多,交友软件中附近的妹子,外卖软件中附近的美食店铺,打车软件中附近的车辆,这些软件中是如何实现的?
出现的问题:
1、查询性能问题,当在并发高,数据量访问大的情况下查询会搞垮MySQL数据库。
2、一般MySQL查询的是一个平面矩形访问,而叫车服务要以自己为中心N公里半径的圆形覆盖。
3、矩形计算在长距离计算时会有很大误差,MySQL不合适。
1.2、获得地址的经纬度
1.3、GEO相关指令
GEOADD添加经纬度坐标
注意:
有中文乱码需要在进时redis-cli -a xxxxxx -raw
GEOPOS返回经纬度
GEOHASH返回坐标的geohash表示
GEODIST两个位置之间的距离
GEORADIUS
GEORADIUSBYMEMBER
1.4、需求分析
某app上搜索附近的酒店距离自己多远
相亲app上小姐姐的距离
某地图软件上附近的人,景区、酒店等
1.5、架构设计
redis的GEO类型实现
1.6、代码实现
@Service
@Slf4j
public class GEOService {
public static final String CITY = "city";
@Autowired
private RedisTemplate redisTemplate;
public String geoAdd() {
HashMap<String,Point> map = new HashMap<>();
//将地标名与经纬度存入map中
map.put("交子公园",new Point(104.08044 , 30.59152 ));
map.put("金融麦田",new Point(104.07260 , 30.58909 ));
map.put("华商金融中心",new Point(104.07146 , 30.58722 ));
//存入redis
redisTemplate.opsForGeo().add(CITY,map);
return map.toString();
}
public Point position(String member) {
//获取经纬度坐标
List<Point> list = redisTemplate.opsForGeo().position(CITY, member);
//返回查询的值即第一个
return list.get(0);
}
public String hash(String member) {
//geohash算法生成的base32编码值
List<String> list = redisTemplate.opsForGeo().hash(CITY, member);
return list.get(0);
}
public Distance distance(String member1, String member2) {
//获取两个给定位置之间的距离
Distance distance = redisTemplate.opsForGeo().distance(CITY, member1, member2,
RedisGeoCommands.DistanceUnit.KILOMETERS);
return distance;
}
public GeoResults radiusByxy() {
//通过经度纬度查找xxx附近
Circle circle = new Circle(104.07260, 30.58909, Metrics.KILOMETERS.getMultiplier());
//返回多少条记录
RedisGeoCommands.GeoRadiusCommandArgs limit = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortDescending().limit(50);
GeoResults<RedisGeoCommands.GeoLocation<String>> radius = redisTemplate.opsForGeo().radius(CITY, circle, limit);
return radius;
}
public GeoResults radiusByMember() {
//通过地名查找
Distance distance = new Distance(50, Metrics.KILOMETERS);
//返回多少条记录
RedisGeoCommands.GeoRadiusCommandArgs limit = RedisGeoCommands.GeoRadiusCommandArgs.newGeoRadiusArgs().includeDistance().includeCoordinates().sortDescending().limit(50);
GeoResults<RedisGeoCommands.GeoLocation<String>> radius = redisTemplate.opsForGeo().radius(CITY,"交子公园",distance,limit);
return radius;
}
}
@RestController
@Slf4j
@Api(tags = "酒店推送")
public class GEOController {
@Autowired
private GEOService geoService;
@ApiOperation("添加坐标geoAdd")
@RequestMapping(value = "/geoAdd",method = RequestMethod.GET)
public String geoAdd(){
return geoService.geoAdd();
}
@ApiOperation("获取经纬度坐标position")
@RequestMapping(value = "/position",method = RequestMethod.GET)
public Point position(String member){
return geoService.position(member);
}
@ApiOperation("获取经纬度生成的base32编码值hash")
@RequestMapping(value = "/hash",method = RequestMethod.GET)
public String hash(String member){
return geoService.hash(member);
}
@ApiOperation("获取两个给定位置间的距离")
@RequestMapping(value = "/distance",method = RequestMethod.GET)
public Distance distance(String member1,String member2){
return geoService.distance(member1,member2);
}
@ApiOperation("通过经度纬度查找xxx附近")
@RequestMapping(value = "/radiusByxy",method = RequestMethod.GET)
public GeoResults radiusByxy(){
return geoService.radiusByxy();
}
@ApiOperation("通过地方查找附近")
@RequestMapping(value = "/radiusByMember",method = RequestMethod.GET)
public GeoResults radiusByMember(){
return geoService.radiusByMember();
}
}