目录
- 附近商户
- 一:Geo数据结构
- 二:附近商户搜索
- 用户签到
- 一:BitMap功能演示
- 二:实现签到功能
- 三:统计签到功能
- uv统计
- 一:hyperloglog的用法
- 二:测试百万数据的t'ji
- 二:测试百万数据的t'ji
附近商户
一:Geo数据结构
二:附近商户搜索
编写一个测试方法,将店铺的信息:经纬度传入geo中,且按照typeid分组查询,因为我们查询距离都是在一个模块中查看的,所以需要根据类型分组;member就是店铺的id;
@Testvoid loadShopData() {//获取店铺的所有信息List<Shop> list = shopService.list();//使用stream流对list进行分组,按照typeidMap<Long, List<Shop>> collect = list.stream().collect(Collectors.groupingBy(Shop::getTypeId));//遍历map,获取每个类型的id和每个类型的所有商家,每一次查询都是一个typeidfor (Map.Entry<Long, List<Shop>> longListEntry : collect.entrySet()) {Long typeId = longListEntry.getKey();String key =RedisConstants.SHOP_GEO_KEY + typeId;List<Shop> value = longListEntry.getValue();//创建一个GeoLocation的集合原来存储geo相关信息List<RedisGeoCommands.GeoLocation<String>> locations=new ArrayList<>(value.size());for (Shop shop : value) {
// stringRedisTemplate.opsForGeo().add(key,new Point(shop.getX(),shop.getY())
// ,shop.getId().toString());//写入信息:x,y,memberlocations.add( new RedisGeoCommands.GeoLocation<String>(shop.getId().toString(),new Point(shop.getX(),shop.getY())));}//将每个类型的店铺geo信息批量插入redisstringRedisTemplate.opsForGeo().add(key, locations);}
然后就要将商品连带着距离返回给前端,并且返回的数据是以距离进行排序:
@Override
@Transactional
public Result queryShopByType(Integer typeId, Integer current, Double x, Double y) {//判断有没有传入x,和y,没有就直接去数据库查询if (x == null || y == null) {Page<Shop> shopPage = query().eq("type_id", typeId).page(new Page<Shop>(current, SystemConstants.DEFAULT_PAGE_SIZE));return Result.ok(shopPage.getRecords());}String key = RedisConstants.SHOP_GEO_KEY + typeId;//设置redis-geo中分页查询的参数Integer from = (current - 1) * SystemConstants.DEFAULT_PAGE_SIZE;//开始的索引Integer end = current * SystemConstants.DEFAULT_PAGE_SIZE;//结束的索引//从redis中的geo中搜索附近的店铺id并且按照距离排序GeoResults<RedisGeoCommands.GeoLocation<String>> search = stringRedisTemplate.opsForGeo().search(key,GeoReference.fromCoordinate(x, y),new Distance(5000),RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance().limit(end));if (search == null) {return Result.ok(Collections.emptyList());}//获取geo中的内容,每个内容都包含value的值,和距离List<GeoResult<RedisGeoCommands.GeoLocation<String>>> content = search.getContent();List<Long> ids = new ArrayList<>(content.size());Map<String, Distance> map = new HashMap<>(content.size());//遍历每一条内容,并且将获取到的value(店铺id)和距离存储起来,店铺id用list存储,距离因为是和店铺在一起的,所以选择用map来存储,能够保留对应关系//因为是分页查询,前面获取的是从0到end的所以内容,我们这里要从from开始而不是0开始,所以使用stream流的skip,直接跳过0-from,从from开始content.stream().skip(from).forEach(geoResult -> {//将店铺id存入集合,将距离存入mapString name = geoResult.getContent().getName();ids.add(Long.valueOf(name));map.put(name, geoResult.getDistance());});//判断集合是否为空,因为刚才我们从跳过了前面的从from开始,所以获取到的id集合可能为空,所以要判断一下,否则在sql的in中会报错if (ids==null||ids.isEmpty()){return Result.ok();}//一样的将集合转成字符串,用‘,’隔开String join = StrUtil.join(",", ids);//从数据库中查询id集合中的对应的店铺,并且顺序要和ids的顺序一致List<Shop> shops = query().in("id", ids).last("order by field(id," + join + ")").list();//遍历每一个shop,为他们的distance属性赋值for (Shop shop : shops) {String id = shop.getId().toString();Distance distance = map.get(id);shop.setDistance(distance.getValue());}return Result.ok(shops);
}
用户签到
一:BitMap功能演示
用法:setbit:向指定位置存入一个0或1,getbit,从指定位置取出值0/1;bitcount,统计bitmap中值为1的个数;
bitops,查询数组中第一个是0或者1的位置;
二:实现签到功能
实现签到功能我们就使用bitmap,因为签到对我们来说就是签上和没签上,签上就是1,没签上就是0,我们可以以一个月为一个key,然后以bitmap来存储就行,在java中使用bitmap是和字符串一起的:
@Override
public Result sign() {Long id = UserHolder.getUser().getId();LocalDate now = LocalDate.now();String format = now.format(DateTimeFormatter.ofPattern("yyyy:MM"));String key=RedisConstants.USER_SIGN_KEY+id+format;//true代表我们设置的值是1;stringRedisTemplate.opsForValue().setBit(key,now.getDayOfMonth()-1,true);return Result.ok();
}
三:统计签到功能
统计签到就是从今天开始向前,有多少个连续的1;
首先我们要得到到今天为止这个月所有签到的数据,可以使用bitfield获取
如果从后向前遍历每个bit位:可以先与1做与运算,然后再向右移一位;
@Override
public Result signCount() {Long userId = UserHolder.getUser().getId();LocalDate now = LocalDate.now();String format = now.format(DateTimeFormatter.ofPattern("yyyy:MM"));String key=RedisConstants.USER_SIGN_KEY+userId+format;int dayOfMonth = now.getDayOfMonth();//bitfield key get u14 0表示获取从0开始14个字节,放到这就是从0开始到当前日的所有签到数据List<Long> longs = stringRedisTemplate.opsForValue().bitField(key,BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));//健壮性判断if (longs==null||longs.isEmpty()){return Result.ok();}//我们虽然得到的是一个集合,但是我们的值只有一个直接获取就行Long res = longs.get(0);if (res==0||res==null){return Result.ok();}//计数器Integer count=0;//循环统计while (true){//与1做与运算,得到当前最后一位是0还是1long i = (res&1);//是1就计数器加1if (i==1){count++;}else {//不是1直接结束循环break;}//最后要将数字向右移一位,不然会死循环res>>>=1;}return Result.ok(count);
}
uv统计
一:hyperloglog的用法
uv:用户量统计
pv:点击量统计
hhl在redis中如何使用:
pfadd:将要统计的值添加进去,比如用户id
pfcount:统计
pfmerge:合并统计量
二:测试百万数据的t’ji
存中…(img-80mHMYws-1730682836468)]
[外链图片转存中…(img-Jn0VjHSl-1730682836468)]
uv:用户量统计
pv:点击量统计
hhl在redis中如何使用:
pfadd:将要统计的值添加进去,比如用户id
pfcount:统计
pfmerge:合并统计量