учить без конец территория , а также июнь общий нежелание .
Связанные серии
Введение
HyperLogLog
даRedis
Расширенная структура данных в2^64data) для ведения кардинальной статистики (статистики дедупликации). Он характеризуется высокой скоростью и небольшими габаритами (12KB). Однако в расчетах будут ошибки, и стандартная ошибка равна0.81%.HyperLogLog
Он только вычисляет количество элементов на основе входного элемента и не сохраняет сам входной элемент, поэтому он не может определить, существует ли уже данный элемент.
основные инструкции
pfadd(ключ,значение...)
Добавить указанный элемент вHyperLogLog
, вы можете добавить несколько элементов
public void pfAdd(String key, String... value) {
stringRedisTemplate.opsForHyperLogLog().add(key, value);
}
pfcount(ключ...)
вернуть данныйHyperLogLog
базовая оценка . При подсчете более одного за разHyperLogLog
, необходимоHyperLogLog
структуры сравниваются и результат объединения помещается во временнуюHyperLogLog
, производительность невысокая, используйте с осторожностью
public Long pfCount(String... key) {
return stringRedisTemplate.opsForHyperLogLog().size(key);
}
pfmerge(ключ назначения, ключ источника...)
поставить несколькоHyperLogLog
Объединить и поместить результат объединения в указанный HyperLogLog
public void pfMerge(String destKey, String... sourceKey) {
stringRedisTemplate.opsForHyperLogLog().union(destKey, sourceKey);
}
Проверка ошибок
Тест ошибок на основе SpringBoot, инициализация 5HyperLogLog
, каждый случайным образом добавляя 10000 элементов, а затем вызываяpfcount
Проверьте конкретную ошибку:
@RestController
@RequestMapping("/redis/hll")
public class HyperController {
private final RedisService redisService;
public HyperController(RedisService redisService) {
this.redisService = redisService;
}
@GetMapping("/init")
public String init() {
for (int i = 0; i < 5; i++) {
Thread thread = new Thread(() -> {
String name = Thread.currentThread().getName();
Random r = new Random();
int begin = r.nextInt(100) * 10000;
int end = begin + 10000;
for (int j = begin; j < end; j++) {
redisService.pfAdd("hhl:" + name, j + "");
}
System.out.printf("线程【%s】完成数据初始化,区间[%d, %d)\n", name, begin, end);
},
i + "");
thread.start();
}
return "success";
}
@GetMapping("/count")
public String count() {
long a = redisService.pfCount("hhl:0");
long b = redisService.pfCount("hhl:1");
long c = redisService.pfCount("hhl:2");
long d = redisService.pfCount("hhl:3");
long e = redisService.pfCount("hhl:4");
System.out.printf("hhl:0 -> count: %d, rate: %f\n", a, (10000 - a) * 1.00 / 100);
System.out.printf("hhl:1 -> count: %d, rate: %f\n", b, (10000 - b) * 1.00 / 100);
System.out.printf("hhl:2 -> count: %d, rate: %f\n", c, (10000 - c) * 1.00 / 100);
System.out.printf("hhl:3 -> count: %d, rate: %f\n", d, (10000 - d) * 1.00 / 100);
System.out.printf("hhl:4 -> count: %d, rate: %f\n", e, (10000 - e) * 1.00 / 100);
return "success";
}
}
Инициализируем данные и вызываем интерфейс:http://localhost:8080/redis/hll/init
线程【4】完成数据初始化,区间[570000, 580000)
线程【2】完成数据初始化,区间[70000, 80000)
线程【0】完成数据初始化,区间[670000, 680000)
线程【1】完成数据初始化,区间[210000, 220000)
线程【3】完成数据初始化,区间[230000, 240000)
Проверьте конкретную статистику и рассчитайте ошибку:http://localhost:8080/redis/hll/count
hhl:0 -> count: 10079, rate: -0.790000
hhl:1 -> count: 9974, rate: 0.260000
hhl:2 -> count: 10018, rate: -0.180000
hhl:3 -> count: 10053, rate: -0.530000
hhl:4 -> count: 9985, rate: 0.150000
настоящий бой
Например, для подсчета популярности статьи и количества эффективных кликов пользователей. Вы можете считать тепло через счетчик Рейса и выполнять его каждый раз, когда вы проситеincr
инструкция. пройти черезHyperLogLog
для подсчета количества действительных пользователей.
Реализовать идеи
пройти черезАОП и пользовательские аннотацииДля подсчета статей, которые необходимо подсчитать:
- Добавьте в интерфейс статьи аннотации, которые необходимо учитывать
- Установить пользовательское значение аннотации
HyperLogLog
соответствующийkey
- Установите pointcut AOP в качестве пользовательской аннотации
- Получить значение аннотации в АОП
- Информация о пользователе оценивается токеном или файлом cookie в AOP.
- Совокупная популярность и количество пользователей
pom
представлятьredis
а такжеaop
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- redis Lettuce 模式 连接池 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
Определение пользовательских аннотаций
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Article {
/**
* 值为对应HyperLogLog的key
*/
String value() default "";
}
Определить АОП
@Aspect
@Component
public class ArticleAop {
private static final String PV_PREFIX = "PV:";
private static final String UV_PREFIX = "UV:";
@Autowired
private RedisService redisService;
/**
* 定义切入点
*/
@Pointcut("@annotation(org.ylc.note.redis.hyperloglog.annotation.Article)")
private void statistics() {
}
@Around("statistics()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
// 获取注解
Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
Article visitPermission = method.getAnnotation(Article.class);
String value = visitPermission.value();
// 获取请求信息
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 这里用来模拟,直接通过参数传入。实际项目中可以根据token或者cookie来实现
String userId = request.getParameter("userId");
// 热度
redisService.incr(PV_PREFIX + value);
// 用户量
redisService.pfAdd(UV_PREFIX + value, userId);
// 执行具体方法
return proceedingJoinPoint.proceed();
}
}
определить интерфейс
Добавить в интерфейс, которому нужна статистика@Article()
аннотация
@RestController
@RequestMapping("/redis/article")
public class ArticleController {
@Autowired
private RedisService redisService;
@Article("it")
@GetMapping("/it")
public String it(String userId) {
String pv = redisService.get("PV:it");
long uv = redisService.pfCount("UV:it");
return String.format("当前用户:【%s】,当前it类热度:【%s】,访问用户数:【%d】", userId, pv, uv);
}
@Article("news")
@GetMapping("/news")
public String news(String userId) {
String pv = redisService.get("PV:news");
long uv = redisService.pfCount("UV:news");
return String.format("当前用户:【%s】,当前news类热度:【%s】,访问用户数:【%d】", userId, pv, uv);
}
@GetMapping("/statistics")
public Object statistics() {
String pvIt = redisService.get("PV:it");
long uvIt = redisService.pfCount("UV:it");
String pvNews = redisService.get("PV:news");
long uvNews = redisService.pfCount("UV:news");
redisService.pfMerge("UV:merge", "UV:it", "UV:news");
long uvMerge = redisService.pfCount("UV:merge");
Map<String, String> result = new HashMap<>();
result.put("it", String.format("it类热度:【%s】,访问用户数:【%d】;", pvIt, uvIt));
result.put("news", String.format("news类热度:【%s】,访问用户数:【%d】", pvNews, uvNews));
result.put("merge", String.format("合并后访问用户数:【%d】", uvMerge));
return result;
}
}
Доступ к исходному коду
Весь код загружен на Github для быстрого доступа
>>>>>> Redis в действии — HyperLogLog
ежедневные комплименты
Это не легко создать, если вы найдете это полезным,попросить лайкслужба поддержки
Поиск внимания
Публичный аккаунт WeChat: Ю Дасянь