thumb-up

🧐 高并发点赞系统

🔗 参考资料

模块

用户模块

  1. 登录
    • 使用 Session 简单登录
  2. 用户信息
    • 不是本项目讨论的重点,不提供修改

点赞模块

  1. 校验
    • 参数合法性
    • 登录态
    • 是否已点赞
  2. 锁
    • 多次点赞统一博客场景
  3. 事务
    • 修改两张表,需要保证数据一致性

博客模块

  1. 获取博客
  2. 获取博客列表

QA

Q1. 联合唯一索引 | 如何从数据库层面杜绝重复数据出现?

对于 thumb 表,使用联合唯一索引

CREATE UNIQUE INDEX idx_userId_blogId ON thumb (userId, blogId);

Q2. 循环依赖 | 为什么要在 BlogServiceImpl 导入 ThumbService 上加 @Lazy

  • 博客服务中需要使用点赞服务查询博客对应的点赞情况列表以返回对应视图。
  • 点赞服务中需要使用博客服务更新单条博客的点赞计数器。
  • 两个服务之间产生了循环依赖,Spring Boot 2.6 以后的版本默认不允许循环依赖,这里可以使用懒加载的方式避免这一问题。

Q3. 锁 | 点赞接口加锁对象是什么?对用户加锁还是对帖子加锁?

  • 加锁目的是为了避免用户并发请求造成数据不一致。
  • 不能对帖子加锁,因为帖子是全局的,加锁会导致其他用户无法点赞。
  • 因此需要对用户加锁,故而实际上是将每个用户的 id 当作锁。

Q4. 字符串对象锁 | 点赞接口的锁具体如何处理?

  • synchronized (("LOCK-USERID-" + loginUser.getId().toString()).intern())
  • 每个用户持有一个唯一 id,因此考虑把 id 对应的 String 常量池对象加锁。
  • 为了避免对程序中其他需要使用该字符串常量的地方产生影响,这里对字符串作出处理 "LOCK-USERID-" + loginUser.getId().toString()
  • 并且我们需要将字符串放到常量池中,调用 intern() 方法。

Q5. 序列化 | 对于 Redis 数据的存储使用序列化是怎么样的?

  • key 和 hash filed 使用 String 类型,value 使用 Object 类型。
  • value 为 Long 或者 Integer 类型时,优先进行以下操作——
    • 存储时转化为 String 类型
    • 取出时转化为 Long 或者 Integer 类型
    • (注意转化前需要先判空,避免空指针)

Q6. 类型转换 | Object 向 Long 的转化优化方式

  • 方法 A
    • Long.valueOf(object.toString())
    • 注意:object 为非数字字符串时会抛出 `NumberFormatException``
  • 方法 B
    • ((Number) object).longValue()
    • 注意:object 为非数字字符串时会抛出 `ClassCastException``
  • 比较
    • 方法 B 优于方法 A
      • 方法 A 需要调用 toString() 方法,有额外开销
      • 方法 B 直接调用 longValue() 方法,JVM 优化
      • 方法 B 抛出的异常容易定位问题。

Q7. 常量 | 定义常量的最佳方式?

  • "Do not use interfaces for constants. Interfaces are for defining types and behaviors, not for storing data."

    — Effective Java Item 18

  • 不建议选择 Interface 定义常量
  • 而应该选用 Class - public static final String 定义常量
S
Description
🧐 高并发点赞系统
Readme MIT 168 KiB
Languages
Java 100%