做視頻網(wǎng)站需要什么空間視頻號(hào)直播推廣二維碼
??本博客為個(gè)人學(xué)習(xí)筆記,學(xué)習(xí)網(wǎng)站與詳細(xì)見:黑馬程序員Redis入門到實(shí)戰(zhàn)?P88?- P95
目錄
附近商鋪
數(shù)據(jù)導(dǎo)入?
功能實(shí)現(xiàn)
用戶簽到
簽到功能
連續(xù)簽到統(tǒng)計(jì)?
UV統(tǒng)計(jì)
附近商鋪
利用Redis中的GEO數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)附近商鋪功能,常見命令如下圖所示。?
key值由特定前綴與商戶類型id組成,每個(gè)GEO存儲(chǔ)一個(gè)店鋪id與該店鋪的經(jīng)緯度信息,如下圖所示。
數(shù)據(jù)導(dǎo)入?
編寫單元測(cè)試,將MySql數(shù)據(jù)庫(kù)中的所有商鋪位置信息導(dǎo)入Redis中,代碼如下。
@Test
void loadShopData() {// 1.查詢所有店鋪信息List<Shop> shops = shopService.list();// 2.將店鋪按照typeId分組,typeId一致的放到一個(gè)集合中Map<Long, List<Shop>> map = shops.stream().collect(Collectors.groupingBy(Shop::getTypeId));// 3.分批完成寫入Redisfor (Map.Entry<Long, List<Shop>> entry : map.entrySet()) {// 3.1 獲取類型idLong typeId = entry.getKey();String key = "shop:geo:" + typeId;// 3.2 獲取同類型的店鋪集合List<Shop> list = entry.getValue();// 3.3 寫入redis( GEOADD key 經(jīng)度 緯度 member)List<RedisGeoCommands.GeoLocation<String>> locations = new ArrayList<>(list.size());for (Shop shop : list) {locations.add(new RedisGeoCommands.GeoLocation<>(shop.getId().toString(),new Point(shop.getX(), shop.getY())));}stringRedisTemplate.opsForGeo().add(key, locations);}
}
功能實(shí)現(xiàn)
由于SpringDataRedis的2.3.9版本并不支持Redis 6.2提供的GEOSEARCH命令,因此我們需要修改版本,代碼如下。
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><exclusions><exclusion><artifactId>spring-data-redis</artifactId><groupId>org.springframework.data</groupId></exclusion><exclusion><artifactId>lettuce-core</artifactId><groupId>io.lettuce</groupId></exclusion></exclusions>
</dependency>
<dependency><groupId>org.springframework.data</groupId><artifactId>spring-data-redis</artifactId><version>2.6.2</version>
</dependency>
<dependency><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId><version>6.1.6.RELEASE</version>
</dependency>
Controller層代碼如下。
@GetMapping("/of/type")
public Result queryShopByType(@RequestParam("typeId") Integer typeId,@RequestParam(value = "current", defaultValue = "1") Integer current,@RequestParam(value = "x", required = false) Double x,@RequestParam(value = "y", required = false) Double y
) {return shopService.queryShopByType(typeId, current, x, y);
}
接口方法的具體實(shí)現(xiàn)代碼如下。
@Override
public Result queryShopByType(Integer typeId, Integer current, Double x, Double y) {// 1.判斷是否需要根據(jù)坐標(biāo)查詢if (x == null || y == null) {// 不需要坐標(biāo)查詢,按數(shù)據(jù)庫(kù)查詢Page<Shop> page = query().eq("type_id", typeId).page(new Page<>(current, SystemConstants.DEFAULT_PAGE_SIZE));// 返回?cái)?shù)據(jù)return Result.ok(page.getRecords());}// 2.計(jì)算分頁(yè)參數(shù)int from = (current - 1) * SystemConstants.DEFAULT_PAGE_SIZE;int end = current * SystemConstants.DEFAULT_PAGE_SIZE;// 3.查詢r(jià)edis、按照距離排序、分頁(yè)。結(jié)果:shopId,distanceString key = "shop:geo:" + typeId;GeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo().search(key,GeoReference.fromCoordinate(x, y),new Distance(5000),RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance().limit(end));// 4.解析出idif (results == null)return Result.ok(Collections.emptyList());List<GeoResult<RedisGeoCommands.GeoLocation<String>>> list = results.getContent();// 如果沒有下一頁(yè),則結(jié)束if (list.size() < from)return Result.ok(Collections.emptyList());// 4.1 截取從from~end的部分ArrayList<Object> ids = new ArrayList<>(list.size());Map<String, Distance> distanceMap = new HashMap<>(list.size());list.stream().skip(from).forEach(result -> {// 4.2 獲取店鋪idString shopIdStr = result.getContent().getName();ids.add(Long.valueOf(shopIdStr));// 4.2 獲取距離Distance distance = result.getDistance();distanceMap.put(shopIdStr, distance);});// 5.根據(jù)id查詢ShopString idStr = StrUtil.join(",", ids);List<Shop> shops = query().in("id", ids).last("ORDER BY FIELD(id," + idStr + ")").list();for (Shop shop : shops) {shop.setDistance(distanceMap.get(shop.getId().toString()).getValue());}// 6.返回return Result.ok(shops);
}
用戶簽到
簽到功能
Controller層代碼如下。?
@PostMapping("/sign")
public Result sign() {return userService.sign();
}
接口方法的具體實(shí)現(xiàn)代碼如下。
@Override
public Result sign() {// 1.獲取當(dāng)前登錄用戶信息Long userId = UserHolder.getUser().getId();// 2.獲取當(dāng)前日期LocalDateTime now = LocalDateTime.now();// 3.拼接keyString keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));String key = "sign:" + userId + keySuffix;// 4.計(jì)算今天是本月的第幾天int dayOfMonth = now.getDayOfMonth();// 5.寫入Redis SETBIT key offset// true:寫入1// false:寫入0stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);return Result.ok();
}
連續(xù)簽到統(tǒng)計(jì)?
Controller層代碼如下。
@GetMapping("/sign/count")
public Result signCount() {return userService.signCount();
}
接口方法的具體實(shí)現(xiàn)代碼如下。
@Override
public Result signCount() {// 1.獲取當(dāng)前登錄用戶信息Long userId = UserHolder.getUser().getId();// 2.獲取當(dāng)前日期LocalDateTime now = LocalDateTime.now();// 3.拼接keyString keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));String key = "sign:" + userId + keySuffix;// 4.計(jì)算今天是本月的第幾天int dayOfMonth = now.getDayOfMonth();// 5.獲取本月截止今天為止的所有簽到記錄,返回結(jié)果是一個(gè)十進(jìn)制數(shù)字 BITFIELD sign:5:202203 GET u14 0List<Long> result = stringRedisTemplate.opsForValue().bitField(key,BitFieldSubCommands.create() //創(chuàng)建子命令.get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)//選擇子命令);if (result == null || result.isEmpty())return Result.ok(0);// 獲取本月簽到位圖Long num = result.get(0);if (num == null || num == 0)return Result.ok(0);// 6.循環(huán)遍歷int cnt = 0;//記錄連續(xù)簽到天數(shù)while (true) {if ((num & 1) == 0)break;num >>= 1;cnt++;}return Result.ok(cnt);
}
UV統(tǒng)計(jì)
測(cè)試
我們直接利用單元測(cè)試,向HyperLogLog中添加100萬(wàn)條數(shù)據(jù),看看統(tǒng)計(jì)效果如何,測(cè)試代碼如下。
@Test
void testHyperLogLog() {// 準(zhǔn)備數(shù)組,裝用戶數(shù)據(jù)String[] users = new String[1000];// 數(shù)組角標(biāo)int index = 0;for (int i = 1; i <= 1000000; i++) {// 賦值users[index++] = "user_" + i;// 每1000條發(fā)送一次if (i % 1000 == 0) {index = 0;stringRedisTemplate.opsForHyperLogLog().add("hll1", users);}}// 統(tǒng)計(jì)數(shù)量Long size = stringRedisTemplate.opsForHyperLogLog().size("hll1");System.out.println("size =" + size);
}
測(cè)試結(jié)果如下圖所示。
誤差 = 1 - (997593 / 1000000)≈ 0.002 可忽略不計(jì)