微信扫码登录过程

微信扫码登录过程

用户请求登陆,后端生成随机code,保存code和socket映射,拿code和微信平台申请二维码,用户扫描这个二维码,微信平台就会返回给后端openid+code,最后只需要保存openid和socket的关系

游标翻页

深翻页问题

普通翻页前端一般会有个分页条。能够指定一页的条数,以及任意选择查看第几页

1
select * from table limit 10000,10

其中10000代表需要跳过的条数,10代表跳过指定条数后,往后需要再取的条数。
需要先查询10000条进行丢弃,再取那么个10条选用。这个效率太低了

游标翻页

为了优化,我们可以加一个where条件

1
select * from table where id>100 order by id limit 0,10

只要id这个字段有索引,就能直接定位到101这个字段,然后去10条记录。以后无论翻页到多大,通过索引直接定位到读取的位置,效率基本是一样的。这个id>100就是我们的游标,这就是游标翻页。

因为游标翻页每次都记住上次查询的最后位置,所以游标翻页不适合跳页,只能不断的往下翻。更适合C端的列表场景

总结

游标翻页的优点

  1. 解决深翻页问题

  2. 解决频繁变动的列表翻页问题。

缺点

  1. 无法跳页,只能不断往下翻

游标翻页更适合c端场景,用户只能不断下滑翻页。

普通翻页更适合B端场景。用户能看见总页数,能随意跳页

游标翻页技术细节

redis游标翻页

redis的zset天然适合游标翻页

1
2
3
ZADD [KEY]  [SOCRE] [VALUE]
#添加一个成员
ZADD 成员列表key 上线时间 UID

获取指定游标的成员

1
Set<TypedTuple<V>> reverseRangeByScoreWithScores(K key, double min, double max, long offset, long count);

min-max代表我们要筛选的score范围。根据正序还是倒序,我们只需要用到其中一个字段

offset代表要跳过几个,我们只需要跳过游标那一条记录,固定传1

count代表需要取几行记录。根据前端传的size

mysql游标翻页

CleanShot 2024-12-29 at 16.05.04@2x

这段代码实现了基于游标分页的逻辑,常用于高效的分页查询。下面对代码的逻辑逐步解析:

1. 输入参数

1
public CursorPageBaseResp<Message> getCursorPage(Long roomId, CursorPageBaseReq request)
  • roomId:表示目标房间 ID,用于查询消息时的筛选条件。

  • request:表示游标分页的请求参数,通常包括:

  • 游标值(cursor):指示分页的起始位置。

  • 每页大小(pageSize)。

2. 构建查询条件(wrapper)

1
LambdaQueryWrapper<Message> wrapper = new LambdaQueryWrapper<>();
  • wrapper 是 MyBatis-Plus 提供的查询构造器,允许以更优雅的方式构建查询条件。

3. 游标字段

1
2
3
4
5
if (StrUtil.isNotBlank(request.getCursor())) {

    wrapper.lt(Message::getId, request.getCursor());

}
  • 如果请求中携带了游标值(cursor),则只查询消息 ID 小于游标值的记录。

  • lt:表示小于(less than)的条件,确保返回的记录在游标之前。

4. 游标翻页方向

1
wrapper.orderByDesc(Message::getId);
  • 按照消息 ID 降序排列,确保新消息(ID 更大)排在前面。

  • 在游标分页中,常用降序排列来实现倒序翻页。

5. 额外查询条件

1
2
3
wrapper.eq(Message::getRoomId, roomId);

wrapper.eq(Message::getStatus, MessageStatusEnum.NORMAL.getStatus());
  • 通过 eq 方法添加等值过滤条件:

  • 过滤出指定房间(roomId)的消息。

  • 过滤出状态为正常(NORMAL)的消息。

6. 分页查询

1
Page<Message> page = page(request.plusPage(), wrapper);
  • request.plusPage():通过分页请求获取分页参数(如页码和每页大小)。

  • page:MyBatis-Plus 提供的分页方法,用于执行分页查询。

查询结果存储在 page 中,包括:

  • 当前页的记录集合(page.getRecords())。

  • 分页的其他元信息(如总记录数)。

7. 计算游标位置

1
2
3
4
5
6
7
String cursor = Optional.ofNullable(CollectionUtil.getLast(page.getRecords()))

    .map(Message::getId)

    .map(String::valueOf)

    .orElse(null);
  • 获取下一页游标

  • 从当前页的记录中获取最后一条记录。

  • 提取最后一条记录的消息 ID 作为下一页的游标。

  • 如果没有记录,游标为 null。

  • Optional 和 CollectionUtil.getLast:

  • 避免空指针异常的优雅写法。

8. 是否最后一页

1
Boolean isLast = page.getRecords().size() != request.getPageSize();
  • 如果当前页的记录数小于请求的分页大小(pageSize),说明已经是最后一页。

  • isLast:布尔值,用来指示是否还有下一页。

9. 返回结果

1
return new CursorPageBaseResp<>(cursor, isLast, page.getRecords());
  • CursorPageBaseResp 是返回值对象,包含以下信息:

  • cursor:下一页的游标位置,用于后续分页请求。

  • isLast:是否是最后一页。

  • page.getRecords():当前页的记录集合。

代码的整体逻辑

  1. 根据输入的 roomId 和游标构建查询条件。

  2. 通过分页查询获取指定范围的记录。

  3. 计算返回给前端的下一页游标和是否最后一页的标志。

  4. 返回包含游标、分页结果和是否最后一页的响应。

使用场景

  • 实时消息加载:例如聊天记录分页,按时间或 ID 降序返回更早的消息。

  • 高效数据加载:游标分页比传统分页效率更高,尤其在数据量大时表现更优,因为它避免了偏移量(offset)查询。

改进建议

  1. 缓存优化:在高频分页场景下,可考虑将分页结果缓存到 Redis。

  2. 并发控制:支持多用户同时请求分页时的正确性和性能保障。

  3. 异常处理:需要确保查询结果为空或发生异常时,返回合理的默认值或错误信息。