说一说Redis的Bitmaps和HyperLoLog?

本篇内容对应 “Redis高级数据类型”小节 和 “7.5 网站数据统计”小节
对应视频:
Redis高级数据结构
网站数据统计

1 什么是UV和DAU?

DAUUV
英文全称Daily Active UserUnique Visotr
中文全称日活跃用户量独立访客
如何统计数据通过用户ID排重统计数据通过用户IP排重统计数据
记录的是什么记录的是用户记录的是设备
使用的Redis数据结构BitmapsHyperLogLog

2 什么是Redis的Bitmap和HyperLoLog?

2.1 Bitmaps

计算机是用二进制作为信息的基础单位,1个字节是8个bit位。例如"big"字符由3个字节组成,实际在计算机存储时将其用二进制表示。"big"中每个字符分别对应的ASCII码是98,105和103,这些十进制数对应的8位二进制数如下:
在这里插入图片描述

许多开发语言都提供了此操作位的功能,例如Java语言中:

i >> 1   // 右移1位  等价于 i / 2
i << 1   // 左移1位  等价于 i * 2
i & j    // 按符号与
i | j    // 按符号或
i ^ j    // 按符号异或
~i       // 按位取非

合理地使用位操作能够有效地提高内存使用率和开发效率。Redis也提供了操作位的功能,即利用Bitmaps这个“数据结构”实现对位的操作,这里使用Bitmaps时要注意两点:

  1. Bitmaps本身不是一种数据结构,实际上它就是字符串,只不过它可以对字符串的位进行操作。
  2. Bitmaps单独提供了一套命令,所以在Redis中使用Bitmaps和使用字符串的方法不太相同。可以把Bitmaps想象成一个以位为单位的数组,数组的每个单元只能存储0和1,数组的下标在Bitmaps中叫偏移量。

在这里插入图片描述

将每个独立用户是否访问过网站存放在Bitmaps中,将访问的用户记作1,没有访问的用户记作0,用偏移量作为用户的id。

1.设置值

设置键的第offset个位的值(从0开始算起),假设现在有20个用户,userid=0, 5, 11, 15, 19 的用户对网站进行了访问,我们使用以下Redis命令向Bitmaps中添加这几个访问用户的id,2024-04-03表示这天的独立访问的用户的Bitmaps

setbit unique:users:2024-04-03 0 1
setbit unique:users:2024-04-03 5 1
setbit unique:users:2024-04-03 11 1
setbit unique:users:2024-04-03 15 1
setbit unique:users:2024-04-03 19 1

我使用lua脚本,在redis中可以直接将这5条命令一次执行:

EVAL "
redis.call('SETBIT', 'unique:users:2024-04-03', 0, 1)
redis.call('SETBIT', 'unique:users:2024-04-03', 5, 1)
redis.call('SETBIT', 'unique:users:2024-04-03', 11, 1)
redis.call('SETBIT', 'unique:users:2024-04-03', 15, 1)
redis.call('SETBIT', 'unique:users:2024-04-03', 19, 1)
" 0

在这里插入图片描述

当前Bitmaps初始化结果是:

在这里插入图片描述
如果此时有一个userid=50的用户访问了网站,会将这个用户添加到Btimaps中:

setbit unique:users:2024-04-03 50 1

在这里插入图片描述

对应的Bitmaps应该长这样:

在这里插入图片描述
在使用Bitmaps统计独立访问的用户时,需要注意以下几点:

  • 一些应用的用户id可能以一个指定的数字开头(例如1000),直接使用这样的用户id作为偏移量会造成一定的浪费,可以考虑减去一个特定的数字。
  • 在第一次初始化Bitmaps,如果偏移量较大,整个初始化过程会比较慢,可能造成Redis的阻塞。

2.获取值

获取userid=8的用户在2024-04-03这天是否访问过网站:

getbit unique:users:2024-04-03 8

在这里插入图片描述

结果返回0,表示该用户在2024-04-03这天没有访问过网站,由于前面我们没有添加过userid=8的这个用户,所以这个结果时符合逻辑的。

获取一个不存在的userid,比如偏移量是10000,结果也是返回0:

getbit unique:users:2024-04-03 10000

在这里插入图片描述

3.获取Bitmaps指定范围值为1的个数

下面这个命令会计算2024-04-03这天网站的独立访问用户数量:

bitcount unique:users:2024-04-03

在这里插入图片描述
通过bitcout命令就可以计算出2024-04-03这天这天网站的DAU,值是6

4.Bitmaps间的运算

假设
2024-04-04这天访问网站的用户是userid=1, 2, 5, 9;
2024-04-05这天访问网站的用户是userid=0, 1, 4, 9
现在要计算2024-04-04和2024-04-05这两天都访问过网站的用户数量,首先初始化这连天的Bitmaps,可以使用bitio and命令将这两个Bitmaps取交集。
图示过程如下:

在这里插入图片描述

再用前面介绍的bitcout命令即可获取与操作之后的Btimaps数量。

Bitmaps性能分析

假设网站由一亿用户,每天独立访问的用户有5千万。分别用集合类型和Bitmaps类型计算DAU,性能比较如下:

数据类型每个用户id占用的空间需要存储的用户量全部内存量
集合64 位5千万64位 X 5千万=400MB
Bitmaps1 位1亿1位X1亿=12.5MB

首先,我们知道:
1 Byte = 8 Bit
1 KB = 1024 Byte
1 MB = 1024 KB

那么,64乘以5千万个bit就是:
64 * 50,000,000 Bit

将Bit转换为Byte:
64 * 50,000,000 Bit / 8 = 400,000,000 Byte

再将Byte转换为KB:
400,000,000 Byte / 1024 = 390,625 KB

最后将KB转换为MB:
390,625 KB / 1024 = 381.47 MB

最后结果大约在400M

但是这并不说明Bitmaps是万金油,如果用户量还是1亿,但是每天访问的用户只有10万,也就是说注册的用户大量是僵尸用户。这时他们之间的性能对比如下:

数据类型每个用户id占用的空间需要存储的用户量全部内存量
集合64 位10万64位 X 10万=800KB
Bitmaps1 位1亿1位X1亿=12.5MB

因为Bitmaps不管你每天具体独立访问的用户数量,而是关系用户id,如果有一个id很大的用户访问一次,那么Bitmaps也会变得非常大。

2.2 HyperLogLog

HyperLogLog是一种基数算法,实际数据类型还是字符串类型,通过HyperLogLog可以利用很小的内存空间完成独立总数的统计,数据集可以是IP、Email、ID等。

Philippe Flajolet教授发表的论文首次提出的HyperLogLog基数算法
论文:The analysis of a near-optimal cardinality estimation algorithm

1.添加
pfadd用于向HyperLogLog添加元素,如果添加成功返回1:

 pfadd 2024_04_03:unique:ids uuid-1 uuid-2 uuid-3 uuid-4

2.计算独立用户数
pfcount用于计算一个或多个HyperLogLog的独立总数:

pfcount 2024_04_03:unique:ids

3.HyperLogLog内存占用实验

一两条数据看不出HyperLogLog内存占用小的优势,我们使用linux的bash脚本,向redis插入100万条用户uuid。
首先我们查询插入前的内存大小,我们执行关注前两行数据即可:

info memory

在这里插入图片描述
向redis HyperLogLog中的2024_04_04:unique:ids键插入100万个用户id的bash脚本如下

#!/bin/bash# 初始化变量
elements=""
key="2024_04_04:unique:ids"# 循环生成并插入用户ID
for i in $(seq 1 1000000); doelements="${elements} user-"${i}if [[ $((i%1000))  == 0 ]]; then# 使用Redis的pfadd命令插入用户redis-cli pfadd ${key} ${elements}# 清空elements变量,准备下一次插入elements=""fi
done# 如果最后一次循环的用户数量不足1000,将剩余的用户插入
if [[ -n "$elements" ]]; thenredis-cli pfadd ${key} ${elements}
fi

我们将该bash脚本命名为insert_users.sh,然后在Linux运行该脚本
在这里插入图片描述

./insert_users.sh

如果报bash: ./insert_users.sh: Permission denied 错误,说明这个文件没有执行权限
使用chmod +x insert_users.sh命令给该文件添加执行权限,然后再次运行即可

执行完毕后到redis中,再次执行查询内存的命令

info memory

在这里插入图片描述
1108384b - 1091920b = 16464b = 16.46KB
所以存入100万个用户的uuid之后,内存只增加了16.46KB。可以看到内存占用非常小。我们再来看一下统计结果是否正确,用pfcount命令统计以下:

pfcount 2024_04_04:unique:ids

在这里插入图片描述
可以看到结果是1003860,并不是精确的100万,但是也很接近了。考虑到占用的内存有很大的优势,对精确值要求敏感的业务完全可以用HyperLogLog。

再来看一下如果用集合来存储着100万个用户uuid,然后统计总数,bash脚本如下:

#!/bin/bash# 初始化变量
elements=""
key="2024_04_04:unique:ids:set"# 循环生成并插入用户ID
for i in $(seq 1 1000000); doelements="${elements} user-"${i}if [[ $((i%1000))  == 0 ]]; then# 使用Redis的sadd命令插入用户redis-cli sadd ${key} ${elements}# 清空elements变量,准备下一次插入elements=""fi
done# 如果最后一次循环的用户数量不足1000,将剩余的用户插入
if [[ -n "$elements" ]]; thenredis-cli pfadd ${key} ${elements}
fi

存入insert_users_set.sh脚本文件中,然后运行。

./insert_users_set.sh

执行完后,到redis执行info memory命令,与上一次的内存大中比较:

info memory

在这里插入图片描述
89496112b - 1108384b = 88387728b = 84.28M
可以看到与前一次相比,内存增加了84M左右。但是用集合统计的结果是精确的:

scard 2024_04_04:unique:ids:set

在这里插入图片描述
根据上面的实验结果,这里列出了使用集合类型和HyperLogLog类型统计百万级用户的占用空间对比:

数据类型1天1个月1年
集合80M2.4G28G
HyperLogLog15K450K5M

可以看到HyperLogLog内存占用量小得惊人,用如此小的空间来估算如此巨大的数据,必然不是100%的精确,存在一定的误差率。Redis官方给出的数字是0.81%的失误率。

4.合并
pfmerge可以求出多个HyperLogLog的并集并赋值给destkey,例如要计算2016年3月5日和3月6日的访问独立用户数,可以按照如下方式来执行:

pfadd 2016_03_06:unique:ids "uuid-1" "uuid-2" "uuid-3" "uuid-4"
pfadd 2016_03_05:unique:ids "uuid-4" "uuid-5" "uuid-6" "uuid-7"
pfmerge 2016_03_05_06:unique:ids 2016_03_05:unique:ids 2016_03_06:unique:ids
pfcount 2016_03_05_06:unique:ids

3 网站数据统计

为什么UV要用ip作为统计访客的依据?

因为我们希望将一些匿名的用户,或者一些没有登录的用户或一些没有账号的游客也统计进来。 UV这个统计值关注的是访问量。而不是关注到底注没注册、登没登录。不管是谁,只要你访问就算。所以,我们要对每次访问都要进行统计,每次访问都把这个用户的ip存到HyperLogLog里,HyperLogLog会统计不同ip的个数,得到最终去重以后的结果,虽然有误差率,但是其极致的内存利用率,这点误差率可以忍受的。

为什么HyperLogLog适合用统计UV的数据结构?

因为HyperLogLog最大的特点是性能好,存储空间非常小。而我们网站的访问量,加上游客的话,可能会非常大,每天可能有几十万甚至上百万的访问量。因为即使是同一个ip访问,由于服务器事先不知道,也会取把这个ip记录到数据结构中。所以访问量才这么大。而我们对UV这个统计值要求不是那么精确,HyperLogLog的误差率足以忍受,所以我们使用HyperLogLog来统计UV。

你的网站如何定义用户是否活跃?

我的网站定义只要今天访问一次就算是一个活跃用户。

DAU与UV的区别?

我们统计DAU是根据用户id来进行统计的,也就是说一些没有登录的游客,DAU是不统计的。

为什么DAU要用Bitmaps?

因为我们在统计DAU时更关注用户的有效性,通过Bitmaps不仅可以统计某天的活跃用户数量,还可以通过命令判断某天,某个用户是不是活跃的等功能,也就是Bitmaps虽然空间利用率没有HyperLogLog高,但是一个网站真正注册登录的用户其实相较于访问量会小很多,所以对空间的占用不会特别高。而且Bitmaps统计是精确的结构。我们关注用户的核心程度,所以对于这样的数据,要求它是一个精确的结果。Bitmaps本身统计的内存占用也不大,性能也比较好,能统计出精确的结果。

你如何实现用Bitmaps存储用户id?

在数据库表设计时,我将用户表的userid设计成为int类型,从1开始自增的主键,所以可以直接将用户id作为偏移量,是否访问了网站分别设置为1和0来存入bitmaps中。
由于userid是整数,而我们要记录的是这个userid访没访问过,访问过就是1,没访问过就是0。1就是活跃,0就是不活跃。
例如:bitmaps的key = dau:2024-04-04,然后userid=101的用户在2024-04-04这条访问过我们的网站,我就将Bitmaps的101位设置成1
这样我们每天可能有几万个用户访问了网站,我只需要用一个Bitmaps,这个Bitmaps的长度=max(userid)。所以,Bitmaps也是比较节约空间的,这么多用户我只需用这么些位来统计即可。

下面我们开始编码实现,因为要用到redis来存储数据,所以首先需要定义redisk key。在RedisKeyUtil类里面添加UV和DAU的key:

    private static final String PREFIX_UV = "uv";private static final String PREFIX_DAU = "dau";

如何查看一周的UV?

由于HyperLogLog支持合并,我们只需使用pfmerge命令求出多个HyperLogLog的并集。例如要计算2024-04-04到2024-04-11这一周的UV,我们可以将这一周7天的7个HyperLogLog进行合并。

我们按照单日UV的方式来记录:

	// 单日UVpublic static String getUVKey(String date) {return PREFIX_UV + SPLIT + date;}

单日UV的key类似于:“uv:2024-04-04”。

如果想统计一周,一个区间的UV,可以对多个HyperLogLog合并,合并的适合会产生一个新的数据,这个数据要指定一个新的key,所以我们要构造一周UV的key,即区间UV。

	// 区间UVpublic static String getUVKey(String startDate, String endDate) {return PREFIX_UV + SPLIT + startDate + SPLIT + endDate;}

区间UV的key类似于 “uv:2024-04-04:2024-04-11”。

同理,统计活跃用户也需要但是DAU和区间DAU

    // 单日活跃用户public static String getDAUKey(String date) {return PREFIX_DAU + SPLIT + date;}// 区间活跃用户public static String getDAUKey(String startDate, String endDate) {return PREFIX_DAU + SPLIT + startDate + SPLIT + endDate;}

定义完以后,我们开发首先开发数据访问层,因为我们用的是Redis,所以我们数据访问层不用单独一个组件,直接上来些Service即可。取调用这个RedisTemplate就行了,创建一个新的Server:DataService类。

首先将RedisTemplate 对象注入进来,由于创建DAU和UV的key时要不断的和日期打交道,所以也将SimpleDateFormat 注入进来,日期格式是"yyyyMMdd"

	@Autowiredprivate RedisTemplate redisTemplate;private SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");

要统计这个数据包括两个方面:

  1. 我要把这个数据记录下来,在每一次请求当中,需要截获这次请求,把相关数据记录到redis。(记)
  2. 当我想看的时候,能够提供一个查询的方法,能够访问到。(查)

首先做UV记的方法:

    // 将指定的IP计入UVpublic void recordUV(String ip) {String redisKey = RedisKeyUtil.getUVKey(df.format(new Date()));redisTemplate.opsForHyperLogLog().add(redisKey, ip);}

UV区间查的方法:

// 统计指定日期范围内的UVpublic long calculateUV(Date start, Date end) {if (start == null || end == null) {throw new IllegalArgumentException("参数不能为空!");}// 整理该日期范围内的keyList<String> keyList = new ArrayList<>();Calendar calendar = Calendar.getInstance();calendar.setTime(start);while (!calendar.getTime().after(end)) {String key = RedisKeyUtil.getUVKey(df.format(calendar.getTime()));keyList.add(key);calendar.add(Calendar.DATE, 1);}/** 如果是同一天,2024-04-03 00:00:00~ 2024-04-03 23:59:59* 合并生成的redisKey是vu:2024-04-03:2024-04-04* keyList的值是[vu:2024-04-03],union操作只union了一个* 实现了一个方法统计单日UV和区间UV* 下面DAU查的方式也是一样的* */// 合并这些数据String redisKey = RedisKeyUtil.getUVKey(df.format(start), df.format(end));redisTemplate.opsForHyperLogLog().union(redisKey, keyList.toArray());// 返回统计的结果return redisTemplate.opsForHyperLogLog().size(redisKey);}

DAU的记和查的逻辑类似:

// 将指定用户计入DAUpublic void recordDAU(int userId) {String redisKey = RedisKeyUtil.getDAUKey(df.format(new Date()));redisTemplate.opsForValue().setBit(redisKey, userId, true);}// 统计指定日期范围内的DAUpublic long calculateDAU(Date start, Date end) {if (start == null || end == null) {throw new IllegalArgumentException("参数不能为空!");}// 整理该日期范围内的keyList<byte[]> keyList = new ArrayList<>();Calendar calendar = Calendar.getInstance();calendar.setTime(start);while (!calendar.getTime().after(end)) {String key = RedisKeyUtil.getDAUKey(df.format(calendar.getTime()));keyList.add(key.getBytes());calendar.add(Calendar.DATE, 1);}/** redisTemplate.opsForValue().setBit 可以设置位* 但是要使用or运算必须使用底层连接* */// 进行OR运算return (long) redisTemplate.execute(new RedisCallback() {@Overridepublic Object doInRedis(RedisConnection connection) throws DataAccessException {String redisKey = RedisKeyUtil.getDAUKey(df.format(start), df.format(end));connection.bitOp(RedisStringCommands.BitOperation.OR,redisKey.getBytes(), keyList.toArray(new byte[0][0]));return connection.bitCount(redisKey.getBytes());}});}

业务层逻辑写好以后,接下来就可以写表现层的逻辑。表现从逻辑一分为二:

  1. 什么时候去记录这个值
  2. 什么时候去查看这个值

记录这个值需要每次请求都进行记录,因为每次请求都有可能是一个新的访问,所以我们在拦截器里面写这个代码。请求到底Handler之前将请求拦截
在这里插入图片描述

创建一个DataInterceptor类,只需要重写preHandle方法即可,在该方法里面进行DAU和UV的统计。

    @Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 统计UVString ip = request.getRemoteHost();dataService.recordUV(ip);// 统计DAUUser user = hostHolder.getUser();if (user != null) {dataService.recordDAU(user.getId());}return true;}

然后将该拦截器注册。

之后就是统计方法,比较简单。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/790605.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

上位机图像处理和嵌入式模块部署(qmacvisual图像清晰度)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 做过isp的同学都知道&#xff0c;图像处理里面有一个3A&#xff0c;即自动曝光、自动白平衡和自动对焦。其中自动对焦这个&#xff0c;就需要用输入…

绩效考核存在合理性、公平性、客观性吗?

目录 一、绩效考核流于形式&#xff1a;没有实际考核过 二、考核结果的确定: 主管一人说了算 三、考核结果&#xff1a; 与绩效奖金挂钩吗&#xff1f; 四、考核的滥用&#xff1a;成为公司排挤迫使员工离职的手段 五、公司说&#xff1a; 让你滚蛋&#xff0c;谁还会发你奖…

SpringBoot(48)-使用 SkyWalking 进行分布式链路追踪

Spring Boot&#xff08;48&#xff09;- 使用 SkyWalking 进行分布式链路追踪 介绍 在分布式系统中&#xff0c;了解各个服务之间的调用关系和性能表现是非常重要的。SkyWalking 是一款开源的分布式系统监控与分析平台&#xff0c;能够帮助我们实现分布式系统的链路追踪、性…

使用minikube安装使用单机版K8S(docker)

前置&#xff1a;作为一个开发&#xff0c;工作之余想玩一下k8s&#xff0c;但是搭建成本太高&#xff0c;所以就找到了minikube这个工具&#xff0c;快速搭建单机版k8s&#xff0c;下面是个人搭建流程&#xff0c;基于centos7&#xff0c;仅供参考。 1.下载kubectl&#xff0…

ES学习日记(十)-------Java操作ES之连接客户端

Elasticsearch有两种连接方式: transport、rest。transport 通过TCP方式访问ES(只支持iava)&#xff0c;rest 方式通过http API 访问ES(没有语言限制)。 ES官方建议使用Iest 方式&#xff0c;transport 在7.8 版本中不建议使用&#xff0c;在8.x的版本中废弃。你可以用Java客户…

Java23种设计模式

本文主要是对Java中一些常用的设计模式进行讲解 后期会进行不断的更新&#xff0c;欢迎浏览 23种设计模式 创建型模式&#xff0c;共五种&#xff1a;工厂方法模式、抽象工厂模式、建造者模式、原型模式、单例模式。结构型模式&#xff0c;共七种&#xff1a;适配器模式、桥接…

使用 Flume 将 CSV 数据导入 Kafka:实现实时数据流

使用 Flume 将 CSV 数据导入 Kafka&#xff1a;实现实时数据流 文介绍了如何使用 Apache Flume 将 CSV 格式的数据从本地文件系统导入到 Apache Kafka 中&#xff0c;以实现实时数据流处理。通过 Flume 的配置和操作步骤&#xff0c;我们可以轻松地将数据从 CSV 文件中读取并发…

RT-Thread下使用NTP服务器获取时间并同步到硬件RTC

单片机:STM32F407VET6 实现功能:通过ntp服务器获取时间并同步到硬件RTC上 1.配置NTP相关参数 1.1打开netutils相关软件包 1.2 关闭软件RTC相关配置 参考资料:RT-Thread中使用NTP自动更新时间_rtthread ntp-CSDN博客 2.配置硬件RTC 2.1 在ENV里面使能硬件RTC 2.2使用STM32C…

日志服务 HarmonyOS NEXT 日志采集最佳实践

作者&#xff1a;高玉龙&#xff08;元泊&#xff09; 背景信息 随着数字化新时代的全面展开以及 5G 与物联网&#xff08;IoT&#xff09;技术的迅速普及&#xff0c;操作系统正面临前所未有的变革需求。在这个背景下&#xff0c;华为公司自主研发的鸿蒙操作系统&#xff08…

idea maven 打包 内存溢出 报 GC overhead limit exceeded -> [Help 1]

idea 使用maven打包 报GC overhead limit exceeded -> [Help 1] 解决方法&#xff1a; 打开settings -> 点开如同所示 将 vm Options 参数 设为 -Xmx8g

双链表算法库构建

v1.0 : 模仿贺利坚老师, 进行基本构建 贺老师链接:数据结构之自建算法库——双链表_双链表画法-CSDN博客 我的解析博客:双链表的存储结构_p (*q)->next;-CSDN博客 库函数: //(1)初始化双链表 void InitDoubleLinkList(DoubleLinkList *&L);//(2)输出双链表 void Dispal…

Pots(DFS BFS)

//新生训练 #include <iostream> #include <algorithm> #include <cstring> #include <queue> using namespace std; typedef pair<int, int> PII; const int N 205; int n, m; int l; int A, B, C; int dis[N][N];struct node {int px, py, op…

解决虚拟机centos8无法连接外网,ping: www.baidu.com: 未知的名称或服务

设置的虚拟机刚开还是好好的&#xff0c;改完hostname重启后就连不上网了 ping百度时显示未知的名称或服务。 1.找到虚拟机的IP(NAT模式的) 编辑-->虚拟网络编辑器 可以看到我的子网IP为192.168.47.0 2.编辑网络配置文件 vim /etc/sysconfig/network-scripts/ifcfg-XXXXX…

SpringCloud学习(1)-consul

consul下载安装及使用 1.consul简介 Consul是一种开源的、分布式的服务发现和配置管理工具&#xff0c;能够帮助开发人员构建和管理现代化的分布式系统。它提供了一套完整的功能&#xff0c;包括服务注册与发现、健康检查、KV存储、多数据中心支持等&#xff0c;可以帮助开发人…

Redis的高可用(主从复制、哨兵模式、集群)的概述及部署

目录 一、Redis主从复制 1、Redis的主从复制的概念 2、Redis主从复制的作用 ①数据冗余&#xff1a; ②故障恢复&#xff1a; ③负载均衡&#xff1a; ④高可用基石&#xff1a; 3、Redis主从复制的流程 4、Redis主从复制的搭建 4.1、配置环境以及安装包 4.2所有主机…

设计模式-行为型-中介者模式-Mediator

同事抽象类 public abstract class Colleague {private Mediator mediator;public abstract void play(String data); } 视频同事 public class AudioColleague extends Colleague {public void play(String data) {System.out.println("画外音是&#xff1a;" d…

最新408试卷分析+备考经验分享

408出题再糟糕&#xff0c;你是不是还是要考&#xff1f; 别管出题人出多刁钻的题&#xff0c;大家拿到的卷子都是一样的&#xff0c;要难就都难&#xff0c;要刁钻就一起g... 所以再潜心钻研出题规律或出题套路&#xff0c;不如多花些时间去多复习巩固几遍知识点&#xff01…

3D雷达和相机联合标定:一种灵活且精确的基于目标的外参标定方法

3D雷达和相机联合标定&#xff1a;一种灵活且精确的基于目标的外参标定方法 论文链接&#xff1a;https://arxiv.org/pdf/2307.15264.pdf 附赠自动驾驶学习资料和量产经验&#xff1a;链接 摘要 本文介绍了3D雷达和相机联合标定&#xff1a;一种灵活且精确的基于目标的外参…

WPF中动画教程(DoubleAnimation的基本使用)

实现效果 今天以一个交互式小球的例子跟大家分享一下wpf动画中DoubleAnimation的基本使用。该小球会移动到我们鼠标左键或右键点击的地方。 该示例的实现效果如下所示&#xff1a; 页面设计 xaml如下所示&#xff1a; <Window x:Class"AnimationDemo.MainWindow&qu…

vue使用iview导航栏Menu activeName不生效

activeName不生效 一、问题一、解决方案&#xff0c; 一、问题 根据ivew官网的提示&#xff0c;设置了active-name和open-names以后&#xff0c;发现不管是设置静态是数据还是设置动态的数据&#xff0c;都不生效 一、解决方案&#xff0c; 在设置动态名称的时候&#xff0c…