当前位置: 首页 > news >正文

Redis 实战篇 ——《黑马点评》(下)

《引言》

        (下)篇将记录 Redis 实战篇 最后的一些学习内容,希望大家能够点赞、收藏支持一下 Thanks♪ (・ω・)ノ,谢谢大家。

传送门(上):Redis 实战篇 ——《黑马点评》(上)

传送门(中):Redis 实战篇 ——《黑马点评》(中)

传送门(下):当-前-页


四、好友关注

1.关注和取关

        在业务中,用户之间存在着常见的关注功能,通过关注 up主来及时的获取其更新的内容。我们可以关注取关对应的用户。

        想要实现关注与取关功能,首先会在访问页面时查询当前用户与展示的用户之间的关系并展示出来,点击后根据现有关系发送请求进行关注或是取关。

public Result isFollow(Long followUserId) {Long userId = UserHolder.getUser().getId();Integer count = query().eq("user_id", userId).eq("follow_user_id", followUserId).count();return Result.ok(count > 0);
}

        查询用户之间的关系,先获取当前用户的 Id,通过其进行查询得到个数,如果查询得到的个数大于 0 则存在返回 true 表示状态为已关注,反之则为 false 表示未关注。

public Result follow(Long followUserId, Boolean isFollow) {Long userId = UserHolder.getUser().getId();if (isFollow){//关注Follow follow = new Follow();follow.setUserId(userId);follow.setFollowUserId(followUserId);boolean isSuccess = save(follow);}else {//取消关注remove(new QueryWrapper<Follow>().eq("user_id",userId).eq("follow_user_id",followUserId));}return Result.ok();
}

        该方法用于进行关注与取关操作,当点击按钮发送请求后,根据传来的 isFollow 值来判断当前进行的操作是关注或是取关。关注操作需要现将其存入数据库中,取关就是根据 Id 从数据库中删除相应的数据。

         点击关注后效果如下所示,上方提示操作成功,同时按钮状态变为取消关注,再次点击后取消关注


2.共同关注

        想要实现查看共同关注功能,需要先完善查看其他用户主页的功能,其主要通过查询用户及其发布的博客来展示在用户的主页上。

@GetMapping("/{id}")
public Result queryUserById(@PathVariable("id") Long userId){// 查询详情User user = userService.getById(userId);if (user == null) {return Result.ok();}UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);// 返回return Result.ok(userDTO);
}

        根据用户 Id 查询用户的信息并返回。

@GetMapping("/of/user")
public Result queryBlogByUserId(@RequestParam(value = "current", defaultValue = "1") Integer current,@RequestParam("id") Long id) {// 根据用户查询Page<Blog> page = blogService.query().eq("user_id", id).page(new Page<>(current, SystemConstants.MAX_PAGE_SIZE));// 获取当前页数据List<Blog> records = page.getRecords();return Result.ok(records);
}

        根据用户 Id 查询用户的博客 信息并返回。

点击其他用户头像可以查看其个人信息,效果如下所示

        接下来,就需要实现查看共同关注的功能。而我们可以借助 Redis 中 Set 数据类型的 SINTER 方法来查看两个 Set 集合中的交集部分,我们利用这一点,就可以简单地实现共同关注功能。

        首先,我们需要在关注用户时将信息同步的存入到 Redis 的对应用户的集合中,其中存入 Redis 中的 key 前缀为 follows 后接当前用户 Id 以作区分 。所以在原先的关注功能中关注后存入 Redis 中,同样的,在取消关注的操作成功后,将 Redis 中的数据也删除掉。

public Result follow(Long followUserId, Boolean isFollow) {Long userId = UserHolder.getUser().getId();String followKey = "follows:" + userId;if (isFollow){//关注Follow follow = new Follow();follow.setUserId(userId);follow.setFollowUserId(followUserId);boolean isSuccess = save(follow);if(isSuccess){//关注成功后,存入 Redis 中stringRedisTemplate.opsForSet().add(followKey,followUserId.toString());}}else {//取消关注boolean isSuccess = remove(new QueryWrapper<Follow>().eq("user_id",userId).eq("follow_user_id",followUserId));if (isSuccess) {stringRedisTemplate.opsForSet().remove(followKey, followUserId.toString());}}return Result.ok();
}

        接着,我们就要去实现查询共同关注的功能:

public Result followCommons(Long id) {//获取当前用户Long userId = UserHolder.getUser().getId();String key = "follows:" + userId;String key2 = "follows:" + id;//求交集Set<String> intersect = stringRedisTemplate.opsForSet().intersect(key, key2);if (intersect == null || intersect.isEmpty()){return Result.ok(Collections.emptyList());}//解析 IdList<Long> ids = intersect.stream().map(Long::valueOf).collect(Collectors.toList());//查询用户List<UserDTO> users = userService.listByIds(ids).stream().map(user -> BeanUtil.copyProperties(user, UserDTO.class)).collect(Collectors.toList());return Result.ok(users);
}

        获取当前用户 Id 后,拼接对应的字符串,分别对应自己与正在查看的用户,后用 intersect 方法求取交集,由于其中可能包含多个 Id,通过 stream 流的方式可以高效便捷的处理查询得到的 Id 并通过其进行查询得到对应的用户信息返回,用于展示在页面上。

最终效果如下所示


3.关注推送

        在我们关注某一用户后,当其再次发布新的博客后,系统应将其推送给所有关注他的用户,例如微信的朋友圈。这种方式也称作 Feed 流,可以将内容自动推送给用户。Feed 流种常见的模式:

① TimeLine:对内容进行简单的筛选,按照内容发布时间排序,常用于好友或关注。例如朋友圈

  • 优点:信息全面,不会有缺失。并且实现也相对简单
  • 缺点:信息噪音较多,用户不一定感兴趣,内容获取效率低

② 智能排序:利用智能算法屏蔽掉违规的、用户不感兴趣的内容,推送用户感兴趣信息来吸引用户。例如抖音

  • 优点:投喂用户感兴趣信息,用户粘度很高,容易沉迷
  • 缺点:如果算法不精准,可能起到反作用

        这里选择使用 TimeLine 模式来实现关注后推送最新博客的功能,而这种模式的实现方式又分为种:

  • 拉模式:也称做读扩散,被关注者会将消息先存入发件箱中,关注者获取消息时会从发件箱中拉取消息到收件箱中。但这种模式在用户关注大量用户的情况下拉取消息时的延迟较大。
  • 推模式:也称作写扩散,被关注者会直接将消息推送到关注者的收件箱中,关注者只需查看收件箱即可。但这种模式在用户被大量用户关注的情况下会发送大量消息,导致内存的大量消耗。适合用户量少,没有大量粉丝的用户的场景。
  • 推拉结合模式:根据具体的情况来分配给不同用户不同的模式。对于访问频繁、活跃的用户,对其使用推模式,保证其第一时间获取消息;对于不经常活跃的用户,对其使用拉模式,只有在其登录上线时主动的拉取发件箱的消息到收件箱中。适合用户量庞大,存在拥有大量粉丝的用户的场景。

        在了解了上述的三种实现方式的优劣后,选择通过推模式来实现关注推送的功能。而在选择 Redis 中的数据结构来保存消息时,尤其是消息会随着时间不断的更新的情况下,不会选择使用 List 而是使用 SortedSet,因为 List 不支持滚动分页查询,其只支持角标或是首尾查询;而 SortedSet 可以通过分数(score)来排序,支持范围查询和分页查询,更适合处理消息随时间更新的情况。

        ● 第一步:需要先改造原先的发布博客的部分代码,在发布后将对应博客的 Id 发送给所有粉丝来实现推送。

public Result saveBlog(Blog blog) {// 1.获取登录用户UserDTO user = UserHolder.getUser();blog.setUserId(user.getId());// 2.保存探店博文boolean isSuccess = save(blog);if (!isSuccess){return Result.fail("新增笔记失败");}// 3.查询笔记作者的所有粉丝List<Follow> follows = followService.query().eq("follow_user_id", user.getId()).list();// 4.推送笔记for (Follow follow : follows) {Long userId = follow.getId();String key = "feed:" + userId;stringRedisTemplate.opsForZSet().add(key, blog.getId().toString(), System.currentTimeMill}// 5.返回idreturn Result.ok(blog.getId());
}

        首先,获取当前登录用户的 Id 并保存博客信息。接着查询当前用户的所用粉丝信息,循环推送给每一个粉丝。其中 key 为 feed:+ 粉丝 Id 组成,对应每一个粉丝都有一个收件箱,value 为博客的 Id,分数为当前的时间戳,便于在展示时进行排序。 

        ● 第二步:在粉丝查询关注的用户更新的博客时,要实现对于博客内容的分页查询,而由于收件箱中的博客是按照时间来进行排序的,每当有新的内容时,按照角标查询得到的内容可能就会出现重复内容的错误,所以就需要通过分数来进行查询,记录每次查询的最后位置,从该位置继续查询,这样就避免了出现重复内容的错误。

Redis 中的对应指令

ZREVRANGEBYSCORE key max min [WITHSCORES] [LIMIT offset count]

max:分数的最大值

min:分数的最小值

[WITHSCORES]:可选,查询内容是否带上分数

LIMIT:

        offset:偏移量,从小于等于最大值的第 N + 1 个元素开始查。

        count:查询的数量

        其中,我们只需关注 max offset 这两个参数即可。min 默认为 0count 按照规定确定。max 是当前查询时的时间戳,之后的查询中为上一次查询的数据中最小的得分offset 在第一次查询时为 0之后的查询中为 1,表示跳过上次查询的最后一个数据,同时如果出现查询出来得分相同的数据这种状况时,重复出现 n 次,偏移量(offset)就为 n 次。

@Data
public class ScrollResult {private List<?> list;private Long minTime;private Integer offset;
}

        首先,创建一个类来存储用于返回滚动分页查询的数据。

@Override
public Result queryBlogOfFollow(Long max, Integer offset) {//1.获取当前用户Long userId = UserHolder.getUser().getId();//2.查询收件箱String key = FEED_KEY + userId;Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet().reverseRangeByScoreWithScores(key, 0, max, offset, 3);//3.解析数据if (typedTuples == null || typedTuples.isEmpty()){return Result.ok();}List<Long> ids = new ArrayList<>(typedTuples.size());long minTime = 0;int os = 1;for (ZSetOperations.TypedTuple<String> tuple : typedTuples) {ids.add(Long.valueOf(tuple.getValue()));long time = tuple.getScore().longValue();if (time == minTime) {os++;}else {minTime = time;os = 1;}}//4.根据 Id 查询 blogString idStr = StrUtil.join(",", ids);List<Blog> blogs = query().in("id", ids).last("order by field(id," + idStr + ")").list();for (Blog blog : blogs) {//查询笔记状态queryBlogUser(blog);isBlogLiked(blog);}//5.封装并返回ScrollResult r = new ScrollResult();r.setList(blogs);r.setOffset(os);r.setMinTime(minTime);return Result.ok(r);
}

        传入的两个参数分别对应 Redis 命令中的 max offset,获取到当前用户的 id 与前缀进行拼接后查询收件箱得到对应关注用户的更新博客的 id 集合。判断数据非空后对集合进行处理,主要是将其中的 id 存入新的集合中,并用 os 统计其中具有相同最小时间的数据的个数作为下一次查询的 offset

        接着就是根据博客的 id 从数据库中进行查询得到所有博客,但 MP 中普通的 listById 方法是根据 in 来进行查询的,并不能进行排序,所以需要通过 order by 来排序,其中的 idStr 通过 StrUtil 中的 join 方法拼接后得到。

private void queryBlogUser(Blog blog) {Long userId = blog.getUserId();User user = userService.getById(userId);blog.setName(user.getNickName());blog.setIcon(user.getIcon());
}
private void isBlogLiked(Blog blog) {if (UserHolder.getUser() == null) {//用户未登录时无需查询是否点赞return;}Long userId = UserHolder.getUser().getId();String key = BLOG_LIKED_KEY + blog.getId();Double score = stringRedisTemplate.opsForZSet().score(key, userId.toString());blog.setIsLike(score != null);
}

        最后,所有的博客的需要进行状态的判断,用于在页面中展示正确的信息,判断后将数据分装进先前定义好的实体类中返回。

最终效果如下所示


五、附近商户

1.Redis 中的 GEO 数据结构

        GEO 就是 Geolocation 的简写形式,代表地理坐标。Redis 在 3.2 版本中加入了对 GEO 的支持,允许存储地理坐标信息,帮助我们根据经纬度来检索数据。常见的命令有:

  • GEOADD:添加一个地理空间信息,包含:经度(longitude) 、纬度(latitude) 、值(member)
  • GEODIST:计算指定的两个点之间的距离并返回
  • GEOHASH:将指定 member 的坐标转为hash字符串形式并返回
  • GEOPOS:返回指定 member 的坐标
  • GEORADIUS:指定圆心、半径,找到该圆内包含的所有 member,并按照与圆心之间的距离排序后返回。
  • GEOSEARCH:在指定范围内搜索 member,并按照与指定点之间的距离排序后返回。范围可以是圆形矩形
  • GEOSEARCHSTORE:GEOSEARCH 功能一致,不过可以把结果存储到一个指定的 key。

        利用 Redis 中 GEO 这种数据结构,可以简单的实现查询附近商铺这种与地理位置信息有关的需求,并且可以根据用户当前位置获取附近商铺的信息。


2.附近商户搜索

        在原先的页面中查看商铺时选择根据距离进行查询时就需要根据地理位置来进行搜索,并在每个商铺右边显示出相距的距离大小,我们可以使用 Redis 中的 GEO 来十分简单的实现这个功能。 

@Test
void loadShopData() {//1.查询店铺信息List<Shop> list = shopService.list();//2.根据类型分组,得到类型id与店铺的映射关系(stream流)Map<Long, List<Shop>> map = list.stream().collect(Collectors.groupingBy(Shop::getTypeId));for (Map.Entry<Long, List<Shop>> entry : map.entrySet()) {Long typeId = entry.getKey();String key = "shop:geo:" + typeId;List<Shop> value = entry.getValue();List<RedisGeoCommands.GeoLocation<String>> locations = new ArrayList<>(value.size());//3.写入redisfor (Shop shop : value) {locations.add(new RedisGeoCommands.GeoLocation<>(shop.getId().toString(),new Point(shop.getX(), shop.getY())));}stringRedisTemplate.opsForGeo().add(key, locations);}
}

        而我们需要先将数据库中商铺的信息存入 Redis 中,并且根据商铺的类型(typeId)分别进行存储,这样在查询时也避免了需要先分类再进行查询,直接按照类型进行查询即可。

注意:因为版本的问题,Redis 中 GEO 类型的一些命令是 6.2 版本后提供的,所以需要保证 SpringBoot 中 SpringDataRedis 的版本支持这些命令

@Override
public Result queryShopByTypeId(Integer typeId, Integer current, Double x, Double y) {// 检查输入坐标是否为null,如果是,则执行类型分页查询if (x == null || y == null){// 根据类型分页查询Page<Shop> page = query().eq("type_id", typeId).page(new Page<>(current, SystemConstants.DEFAULT_PAGE_SIZE));// 返回数据return Result.ok(page.getRecords());}// 计算查询的起始和结束范围int from = (current - 1) * SystemConstants.DEFAULT_PAGE_SIZE;int end = current * SystemConstants.DEFAULT_PAGE_SIZE;...

        在 Impl 层中,首先对传入的坐标进行判断,不为空则接着计算分页的参数。

...
// 拼接Redis地理信息键名String key = SHOP_GEO_KEY + typeId;// 使用Redis地理信息命令,查询指定坐标附近的商店GeoResults<RedisGeoCommands.GeoLocation<String>> results = stringRedisTemplate.opsForGeo().search(key,GeoReference.fromCoordinate(new Point(x, y)),new Distance(5000),RedisGeoCommands.GeoSearchCommandArgs.newGeoSearchArgs().includeDistance().limit(end));// 如果查询结果为空,返回空列表if (results == null){return Result.ok(Collections.emptyList());}List<GeoResult<RedisGeoCommands.GeoLocation<String>>> list = results.getContent();// 检查结果列表是否足够覆盖查询的起始范围if (list.size() <= from){return Result.ok(Collections.emptyList());}
...

        接着,将商铺类型 id 与前缀进行拼接后进行查询得到地理位置信息,其中 Distance 表示查询的范围。对得到的结果进行非空判断,然后检查结果是否小于起始值,因为这个分页查询是先查询出总数据后按照范围取出数据展示,如果起始值大于总数,则会造成程序出错

    ...// 初始化商店ID列表和距离映射List<Long> ids = new ArrayList<>(list.size());Map<String, Distance> distanceMap = new HashMap<>(list.size());// 遍历结果列表,提取商店ID和距离信息list.stream().skip(from).forEach(result -> {String shopIdStr = result.getContent().getName();ids.add(Long.valueOf(shopIdStr));Distance distance = result.getDistance();distanceMap.put(shopIdStr, distance);});String idStr = StrUtil.join(",", ids);// 根据商店ID查询商店信息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());}// 返回return Result.ok(shops);
}

         最后将商店 Id 列表和距离初始化后遍历查询结果并将其存入先前初始化好的列表中。再利用 query 方法根据商铺 Id 查询商铺信息,最后将数据返回即可。

最终效果如下所示


六、用户签到

1.BitMap

        签到功能作为一个耳熟能详的功能常常出现在一些游戏和软件中,作为其中的常客,其主要是通过一些签到奖励来激励用户登录。使用数据库实现时其每个用户每天的签到情况会产生大量的数据,而在 Redis 中可以使用 String 类型来实现 BitMap(位图)这种思路,来简单的存储用户的签到信息。

         通过上面的结构,我们可以简单的用少量的内存存储大量的签到信息。、

BitMap的操作命令有:

  • SETBIT:向指定位置(offset)存入一个 0 或 1
  • GETBIT:获取指定位置(offset)的 bit 值
  • BITCOUNT:统计 BitMap 中值为 1 的 bit 位的数量
  • BITFIELD:操作(查询、修改、自增)  BitMap 中 bit 数组中的指定位置(offset)的值
  • BITFIELD_RO:获取 BitMap 中 bit 数组,并以十进制形式返回
  • BITOP:将多个 BitMap 的结果做位运算(与、或、异或)
  • BITPOS:查找 bit 数组中指定范围内第一个 0 或 1 出现的位置

        其中用到的主要是 SETBIT BITFIELD

SETBIT:

        操作如上图所示,需要注意需要选择 Binary 才能看到对应的格式。

BITFIELD:

         其中 type 表示获取的数量,offset 表示从第几个开始。


2.实现签到功能

        通过 Redis 来实现签到功能,不需要任何参数,只需通过用户 Id 当前时间进行拼接后作为 key 来存储即可。

public Result sign() {//1.获取当前登录用户Long userId = UserHolder.getUser().getId();//2.获取日期LocalDateTime now = LocalDateTime.now();//3.拼接 KeyString keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));String key = USER_SIGN_KEY + userId + keySuffix;//4.获取今天是本月的第几天int dayOfMonth = now.getDayOfMonth();//5.写入 RedisstringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);return Result.ok();
}

        首先获取当前登录用户的 Id 以及当前时间。拼接后再获取今天是本月第几天,用于确定在 BitMap 签到的位置,且注意 getDayOfMonth 方法获取的数是从 1 开始的,而 BitMap 的下标是从 0 开始的,所以方法结果需要 - 1 后再写入。

最终效果如下所示

        发送签到请求后成功的写入到了 Redis 中。注意需要在请求头中添加 authorization 参数否则请求会失败。


3.统计连续签到次数

·        想要统计从本月开始到今天为止的连续签到次数,就需要先先获取本月到今天为止的所有签到信息,然后遍历信息,统计连续签到的次数。其中,可以使用位运算来实现遍历功能,完成连续签到次数的统计。

public Result signCount() {//1.获取当前登录用户Long userId = UserHolder.getUser().getId();//2.获取日期LocalDateTime now = LocalDateTime.now();//3.拼接 KeyString keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));String key = USER_SIGN_KEY + userId + keySuffix;//4.获取今天是本月的第几天int dayOfMonth = now.getDayOfMonth();List<Long> result = stringRedisTemplate.opsForValue().bitField(key,BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));...

        首先还是获取到当前登录的用户 Id 和当前时间,拼接后查询从本月的第一天到今天的所有签到信息。其中 bitField 中包含两个参数,一个是 key,一个是子命令。子命令中设置范围

    ...if (result == null || result.isEmpty()){return Result.ok(0);}Long num = result.get(0);if (num == null){return Result.ok(0);}int count = 0;while(true){if((num & 1) == 0) {//为 0 未签到,结束break;}else {//不为 0 已签到,计数器++count++;}//将数字无符号右移一位抛弃最后一个 bit 位num >>>= 1;}return Result.ok(count);
}

        之后对查询得到的数据进行非空校验,通过后开始遍历。与 1 进行与运算获取最后一位并对其进行判断,为 0 则直接退出循环,不为 0 则计数后将数字右移移除最后一位数继续循环。结束统计后直接返回计数结果。

最终效果如下所示

        测试后可以看到成功统计出了本月的连续签到次数。


七、UV 统计

        首先,需要了解什么是 UV

  • UV:全称 Unique Visitor,也叫独立访客量,是指通过互联网访问、浏览这个网页的自然人。1天内同一个用户多次访问该网站,只记录1次
  • PV:全称Page View,也叫页面访问量或点击量,用户每访问网站的一个页面,记录1次PV,用户多次打开页面,则记录多次PV。往往用来衡量网站的流量。

        想要统计这种大量的数据,就需要使用到 Redis 中的 HyperLogLog 了。其基于 String 类型实现,其占用的内存极小,而代价就是其测量的结果是概率性的,但对于 UV 统计这种大数据量来说误差是可以忽略的。

        根据上图可知,HyperLogLog 不会将重复的内容统计。

后续的测试  o(´^`)o跳过


【下】完结

 传送门(上):Redis 实战篇 ——《黑马点评》(上)

 传送门(中):Redis 实战篇 ——《黑马点评》(中)

相关文章:

Redis 实战篇 ——《黑马点评》(下)

《引言》 &#xff08;下&#xff09;篇将记录 Redis 实战篇 最后的一些学习内容&#xff0c;希望大家能够点赞、收藏支持一下 Thanks♪ (&#xff65;ω&#xff65;)&#xff89;&#xff0c;谢谢大家。 传送门&#xff08;上&#xff09;&#xff1a;Redis 实战篇 ——《黑马…...

OpenCV计算摄影学(10)将一组不同曝光的图像合并成一张高动态范围(HDR)图像的实现类cv::MergeDebevec

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 resulting HDR 图像被计算为考虑了曝光值和相机响应的各次曝光的加权平均值。 cv::MergeDebevec 是 OpenCV 中用于将一组不同曝光的图像合并成一…...

Linux驱动开发之串口驱动移植

原理图 从上图可以看到RS232的串口接的是UART3&#xff0c;接下来我们需要使能UART3的收发功能。一般串口的驱动程序在内核中都有包含&#xff0c;我们配置使能适配即可。 设备树 复用功能配置 查看6ull如何进行uart3的串口复用配置&#xff1a; 设备树下添加uart3的串口复用…...

c语言中return 数字代表的含义

return 数字的含义&#xff1a;表示函数返回一个整数值&#xff0c;通常用于向调用者&#xff08;如操作系统或其他程序&#xff09;传递程序的执行状态或结果。 核心规则&#xff1a; return 0&#xff1a; 含义&#xff1a;表示程序或函数正常结束。 示例&#xff1a; int m…...

Android 端侧运行 LLM 框架 MNN 及其应用

MNN Chat Android App - 基于 MNN 引擎的智能聊天应用 一、MNN 框架简介与工作原理1.1 什么是 MNN&#xff1f;1.2 MNN 的工作原理 二、MNN Chat Android App2.1 MNN Chat 的功能2.2 MNN Chat 的优势2.3 MNN Chat Android App 的使用 三、总结 随着移动端人工智能需求的日益增长…...

jupyter汉化、修改默认路径详细讲解

1、配置镜像路径 修改第三方库的下载路径&#xff0c;比如&#xff1a;[清华镜像pypi](https://mirrors.tuna.tsinghua.edu.cn/help/pypi/)&#xff0c;配置镜像地址。 首先执行 pip config set global.index-url https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple 2、安…...

java面试笔记(二)

1.流程中如何数据回填 &#xff08;1&#xff09;方法1&#xff1a; 在工作流中有一个标识&#xff0c;每一次审批的时候去判断是否审批完成&#xff0c;然后调用反射 &#xff08;2&#xff09;方法2&#xff1a; 创建一个流程结束的监听器&#xff0c;监听流程是否结束&a…...

【大语言模型笔记进阶一步】提示语设计学习笔记,跳出框架思维,自己构建提示词

一、大语言模型应用场景 1. 文本生成 文本创作&#xff1a; 诗歌故事&#xff0c;剧本&#xff0c;推文帖子 摘要与改写&#xff1a; 长文本摘要与简化&#xff0c;多语言翻译与本地化 结构化生成&#xff1a; 表格&#xff0c;根据需求生成代码片段&#xff0c;API文档生成…...

sql调优:优化响应时间(优化sql) ; 优化吞吐量

Sql性能调优的目的 1.优化响应时间>>优化sql 经过调优后&#xff0c;执行查询、更新等操作的时候&#xff0c;数据库的反应速度更快&#xff0c;花费的时间更少。 2.优化吞吐量 即“并发”, 就是“同时处理请求”的能力。 优化sql 尽量将多条SQL语句压缩到一句>…...

debian/control中的包关系

软件包依赖就是软件包关系的一种&#xff0c;一般用 Depends 表示。 每个软件包都可以和其他软件包有各种不同的关系。除 Depends 外&#xff0c;还有 Recommends、Suggests、Pre-Depends、Breaks、Conflicts、Provides 和 Replaces&#xff0c;软件包管理工具&#xff08;如 …...

python学习第三天

条件判断 条件判断使用if、elif和else关键字。它们用于根据条件执行不同的代码块。 # 条件判断 age 18 if age < 18:print("你还是个孩子&#xff01;") elif age 18:print("永远十八岁&#xff01;") else:print("你还年轻&#xff01;")…...

k8s架构及服务详解

目录 1.1.容器是什么1.2.Namespace1.3.rootfs5.1.Service介绍5.1.1.Serice简介 5.1.1.1什么是Service5.1.1.2.Service的创建5.1.1.3.检测服务5.1.1.4.在运行的容器中远程执行命令 5.2.连接集群外部的服务 5.2.1.介绍服务endpoint5.2.2.手动配置服务的endpoint5.2.3.为外部服务…...

Unity中动态切换光照贴图LightProbe的方法

关键代码&#xff1a;LightmapSettings.lightmaps lightmapDatas; LightmapData中操作三张图&#xff1a;lightmapColor,lightmapDir,以及一张ShadowMap 这里只操作前两张&#xff1a; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.UI;public cl…...

基于Matlab的多目标粒子群优化

在复杂系统的设计、决策与优化问题中&#xff0c;常常需要同时兼顾多个相互冲突的目标&#xff0c;多目标粒子群优化&#xff08;MOPSO&#xff09;算法应运而生&#xff0c;作为群体智能优化算法家族中的重要成员&#xff0c;它为解决此类棘手难题提供了高效且富有创新性的解决…...

Android Studio 新版本Gradle发布本地Maven仓库示例

发布代码到JitPack示例&#xff1a;https://blog.csdn.net/loutengyuan/article/details/145938967 以下是基于 Android Studio 24.2.2&#xff08;Gradle 8.10.2 AGP 8.8.0 JDK17&#xff09; 的本地 Maven 仓库发布示例&#xff0c;包含aar和jar的不同配置&#xff1a; 1.…...

Langchain解锁LLM大语言模型的结构化输出能力(多种实现方案)

在 LangChain解锁LLM大语言模型的结构化输出能力&#xff1a;调用 with_structured_output() 方法 这篇博客中&#xff0c;我们了解了格式化LLM输出内容的必要性以及如何通过调用langchain框架中提供的 with_structured_output() 方法对LLM输出进行格式化&#xff08;三种可选方…...

深入理解Spring @Async:异步编程的利器与实战指南

一、为什么需要异步编程&#xff1f; 在现代高并发系统中&#xff0c;同步阻塞式编程会带来两大核心问题&#xff1a; // 同步处理示例 public void processOrder(Order order) {// 1. 保存订单&#xff08;耗时50ms&#xff09;orderRepository.save(order); // 2. 发送短信…...

让Word插上AI的翅膀:如何把DeepSeek装进Word

在日常办公中&#xff0c;微软的Word无疑是我们最常用的文字处理工具。无论是撰写报告、编辑文档&#xff0c;还是整理笔记&#xff0c;Word都能胜任。然而&#xff0c;随着AI技术的飞速发展&#xff0c;尤其是DeepSeek的出现&#xff0c;我们的文字编辑方式正在发生革命性的变…...

清华DeepSeek深度探索与进阶指南

「清华北大-Deepseek使用手册」 链接&#xff1a;https://pan.quark.cn/s/98782f7d61dc 「清华大学Deepseek整理&#xff09; 1&#xff0d;6版本链接&#xff1a;https://pan.quark.cn/s/72194e32428a AI学术工具公测链接:https://pan.baidu.com/s/104w_uBB2F42Da0qnk78_ew …...

迁移学习策略全景解析:从理论到产业落地的技术跃迁

&#xff08;2025年最新技术实践指南&#xff09; 一、迁移学习的范式革命与核心价值 在人工智能进入"大模型时代"的今天&#xff0c;迁移学习已成为突破数据瓶颈、降低训练成本的关键技术。本文基于2025年最新技术进展&#xff0c;系统梳理六大核心策略及其在产业实…...

WireGuard搭建网络,供整个公司使用

一、清理现有配置&#xff08;如已有失败尝试&#xff09; # 停止并删除现有 WireGuard 接口 sudo wg-quick down wg0 sudo rm -rf /etc/wireguard/wg0.conf# 验证接口已删除 (执行后应该看不到 wg0) ifconfig二、服务器端完整配置流程 1. 安装 WireGuard sudo apt update &…...

MyAgent:用AI开发AI,开启智能编程的产业革命

在人工智能技术爆发的2025年&#xff0c;‌MyAgent智能体平台‌凭借其独特的“AI开发AI”模式&#xff0c;正在重构全球软件开发行业的底层逻辑。这一创新范式不仅将自然语言处理、机器学习、RPA&#xff08;机器人流程自动化&#xff09;等技术深度融合&#xff0c;更通过‌“…...

Cherno C++ P60 为什么不用using namespace std

这篇文章我们讲一下之前写代码的时候的一个习惯&#xff0c;也就是不使用using namespace std。如果我们接触过最早的C教程&#xff0c;那么第一节课都会让我们写如下的代码&#xff1a; #include<iostream>using namespace std;int main() {cout << "Hello …...

el-select的下拉选择框插入el-checkbox

el-check注意这里要使用model-value绑定数据 <el-selectv-model"selectDevice"multiplecollapse-tags:multiple-limit"5"style"width: 200px"popper-class"select-popover-class" ><el-optionv-for"item in deviceList…...

M系列芯片 MacOS 在 Conda 环境中安装 TensorFlow 2 和 Keras 3 完整指南

目录 1. 引言2. 环境准备3. 安装 TensorFlow 和必要依赖4. 结语Reference 1. 引言 Keras 是搞深度学习很可爱的工具&#xff0c;其友好的接口让我总是将其作为搭建模型原型的首选。然而&#xff0c;当我希望在 M 系列芯片的MacBook Pro上使用 Keras时&#xff0c;使用Conda和P…...

GitHub教程

目录 1.是什么?2.安装3.创建库3.增删改查4.远程仓库5.分支6.标签7.使用流程8.总结 1.是什么? Git 是一个命令行工具&#xff0c;但也有许多图形用户界面可用。本地仓库&#xff0c;安装包下载到本地。Git 的一个流行 GUI 是 GitHub&#xff0c;它可以方便地管理存储库、推送…...

《JavaScript解题秘籍:力扣队列与栈的高效解题策略》

232.用栈实现队列 力扣题目链接(opens new window) 使用栈实现队列的下列操作&#xff1a; push(x) -- 将一个元素放入队列的尾部。 pop() -- 从队列首部移除元素。 peek() -- 返回队列首部的元素。 empty() -- 返回队列是否为空。 示例: MyQueue queue new MyQueue(); queue…...

Supra软件更新:AGRV2K CPLD支持无源晶体做时钟输入

Supra软件更新&#xff1a;AGRV2K CPLD支持无源晶体做时钟输入 AGRV2K CPLD支持无源晶体做时钟输入&#xff0c;和AG32一样接入OSC_IN和OSC_OUT管脚。 VE管脚文件设为PIN_HSE&#xff0c;如&#xff1a; clk PIN_HSE ledout[0] PIN_31 ledout[1] PIN_32 ...... 在下载烧录文…...

简易的微信聊天网页版【项目测试报告】

文章目录 一、项目背景二、项目简介登录功能好友列表页面好友会话页面 三、测试工具和环境四、测试计划测试用例部分人工手动测试截图web自动化测试测试用例代码框架配置内容代码文件&#xff08;Utils.py&#xff09;登录页面代码文件&#xff08;WeChatLogin.py&#xff09;好…...

nio使用

NIO &#xff1a; new Input/Output,&#xff0c;在java1.4中引入的一套新的IO操作API&#xff0c;&#xff0c;&#xff0c;旨在替代传统的IO&#xff08;即BIO&#xff1a;Blocking IO&#xff09;&#xff0c;&#xff0c;&#xff0c;nio提供了更高效的 文件和网络IO的 操作…...

【蓝桥杯单片机】第十二届省赛

一、真题 二、模块构建 1.编写初始化函数(init.c) void Cls_Peripheral(void); 关闭led led对应的锁存器由Y4C控制关闭蜂鸣器和继电器 由Y5C控制 2.编写LED函数&#xff08;led.c&#xff09; void Led_Disp(unsigned char ucLed); 将ucLed取反的值赋给P0 开启锁存器…...

Jenkins与Flutter项目持续集成实战指南

一、环境准备 1. 基础环境要求 Jenkins Server&#xff1a;已安装JDK 11&#xff0c;建议使用Linux服务器&#xff08;Ubuntu/CentOS&#xff09;Flutter SDK&#xff1a;全局安装或通过工具动态管理构建代理节点&#xff1a; Android构建&#xff1a;需Android SDK、Gradle、…...

linux常见操作命令

查看目录和文件 ls&#xff1a;列出目录内容。 常用选项&#xff1a; -l&#xff1a;以长格式显示&#xff0c;显示文件的权限、所有者、大小、修改时间等详细信息。-a&#xff1a;显示所有文件和目录&#xff0c;包括隐藏文件&#xff08;以 . 开头的文件&#xff09;。-h&…...

6.人工智能与机器学习

一、人工智能基本原理 1. 人工智能&#xff08;AI&#xff09;定义与范畴 核心目标&#xff1a;模拟人类智能行为&#xff08;如推理、学习、决策&#xff09;分类&#xff1a; 弱人工智能&#xff08;Narrow AI&#xff09;&#xff1a;专精单一任务&#xff08;如AlphaGo、…...

GPU架构分类

一、NVIDIA的GPU架构 NVIDIA是全球领先的GPU生产商&#xff0c;其GPU架构在图形渲染、高性能计算和人工智能等领域具有广泛应用。NVIDIA的GPU架构经历了多次迭代&#xff0c;以下是一些重要的架构&#xff1a; 1. Tesla&#xff08;特斯拉&#xff09;架构&#xff08;2006年…...

23种设计模式之单例模式(Singleton Pattern)【设计模式】

文章目录 一、简介二、关键点三、实现单例模式的步骤四、C#示例4.1 简单的单例模式4.2 线程安全的单例模式&#xff08;双重检查锁定&#xff09;4.3 静态初始化单例模式 五、单例模式优缺点5.1 优点5.2 缺点 六、适用场景七、示例的现实应用 一、简介 单例模式&#xff08;Si…...

MAX232数据手册:搭建电平转换桥梁,助力串口稳定通信

在现代电子设备的通信领域&#xff0c;串口通信因其简单可靠而被广泛应用。MAX232 芯片作为串口通信中的关键角色&#xff0c;发挥着不可或缺的作用。下面&#xff0c;我们将依据提供的资料&#xff0c;深入解读 MAX232 芯片的各项特性、参数以及应用要点。 一、引脚说明 MAX2…...

Day 55 卡玛笔记

这是基于代码随想录的每日打卡 所有可达路径 题目描述 ​ 给定一个有 n 个节点的有向无环图&#xff0c;节点编号从 1 到 n。请编写一个函数&#xff0c;找出并返回所有从节点 1 到节点 n 的路径。每条路径应以节点编号的列表形式表示。 输入描述 ​ 第一行包含两个整数…...

python量化交易——金融数据管理最佳实践——使用qteasy管理本地数据源

文章目录 统一定义的金融历史数据表最重要的数据表数据表的定义交易日历表的定义&#xff1a;交易日历表: trade_calendar qteasy是一个功能全面且易用的量化交易策略框架&#xff0c; Github地址在这里。使用它&#xff0c;能轻松地获取历史数据&#xff0c;创建交易策略并完…...

AVM 环视拼接 鱼眼相机

https://zhuanlan.zhihu.com/p/651306620 AVM 环视拼接方法介绍 从内外参推导IPM变换方程及代码实现&#xff08;生成AVM环视拼接图&#xff09;_avm拼接-CSDN博客 经典文献阅读之--Extrinsic Self-calibration of the Surround-view System: A Weakly... (环视系统的外参自…...

计算机基础面试(数据库)

1. 事务的ACID特性&#xff1f;如何通过日志保证原子性和持久性&#xff1f; 专业解答&#xff1a; ACID&#xff1a;原子性&#xff08;Atomicity&#xff09;、一致性&#xff08;Consistency&#xff09;、隔离性&#xff08;Isolation&#xff09;、持久性&#xff08;Dura…...

Self-Pro: A Self-Prompt and Tuning Framework for Graph Neural Networks

Self-Pro: A Self-Prompt and Tuning Framework for Graph Neural Networks ​#paper/GFM/GNN-BASED#​ #paper/⭐⭐⭐#​ 注意&#xff1a;这篇文章是每个图一个GCN模型&#xff0c;而不是所有图一个GCN 模型 算是最早的涉及异配图的prompt了 贡献和动机&#xff1a; 非对…...

Spring Boot 与 MyBatis 版本兼容性

初接触Spring Boot&#xff0c;本次使用Spring Boot版本为3.4.3&#xff0c;mybatis的起步依赖版本为3.0.0&#xff0c;在启动时报错&#xff0c;报错代码如下 org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition with name userMapper…...

WPF 如何使文本显示控件支持显示内容滚动显示

WPF中如何使文本显示控件支持显示内容滚动显示 在WPF中&#xff0c;TextBlock 控件本身并不直接支持滚动功能&#xff0c;因为它的设计初衷是用于静态文本展示。但是&#xff0c;你可以通过一些技巧和自定义控件来实现 TextBlock 的滚动效果。以下是几种常见的方法&#xff1a;…...

1208. 尽可能使字符串相等

目录 一、题目二、思路2.1 解题思路2.2 代码尝试2.3 疑难问题 三、解法四、收获4.1 心得4.2 举一反三 一、题目 二、思路 2.1 解题思路 2.2 代码尝试 class Solution { public:int equalSubstring(string s, string t, int maxCost) {int curcost0;//统计当前开销int left0;…...

Linux系统管理操作

一、关闭防火墙 默认端口号是22&#xff0c;其他端口用不了&#xff0c;这时候就引出关闭防火墙 1.1、systemctl 1.1.1、基本语法 systemctl start | stop | restart | status 服务名 //启动、关闭、重启、查看状态 1.1.2、查看服务的方法 查看/usr/lib/systemd/syst…...

【STM32H743IIT6】将外部SDRAM作为内部SRAM使用的方法及需要解决的问题

前言 STM32H743的片上随机存取存储器&#xff08;RAM&#xff09;容量最大约为1KB。对于简单项目而言&#xff0c;这一容量尚可满足需求。但在处理更为复杂的应用程序时&#xff0c;尤其是在随机存取存储器方面&#xff0c;“空间不足”的问题就会不可避免地出现。此时&#x…...

AMD RDNA3 GPU架构解析

本文会通过把AMD的RDNA3架构为例比喻为**“施工公司”**工作模式&#xff0c;深入理解GPU如何高效处理顶点着色、像素计算等任务。 一、施工公司的组织架构 1. 施工公司&#xff08;WGP&#xff09;与施工队&#xff08;CU&#xff09; WGP&#xff08;Work Group Processor&…...

博客系统--测试报告

博客系统--测试报告 项目背景项目功能功能测试①登录功能测试②发布博客功能测试③删除文章功能测试④功能测试总结&#xff1a; 自动化测试自动化脚本执行界面&#xff1a; 性能测试 本博文主要针对个人实现的项目《博客系统》去进行功能测试、自动化测试、性能测试&#xff0…...

打造个人知识库(Page Assist版)- 私人专属AI-本地化部署deepseek

上篇介绍了实现浏览器交互Ai Web Ui - chrome浏览器插件-Page Assist&#xff0c;安装即可使用&#xff0c;实现最简单的本地化部署AI使用。 实现浏览器交互Ai Web Ui-本地化部署的deepseek Ollama Page Assist 本编介绍使用 Page Assist 构建个人知识库&#xff0c;利用个…...