Java开发场景问题分析

OOM原因

1.一次性申请的太多资源

更改申请对象数量

2.内存资源耗尽未释放

找到未释放的对象进行释放

3.本身资源不够

jmap -heap 查看堆信息

4.通过dump定位

系统已经OOM挂了

提前设置 -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=(设置导出路径)

系统运行中还未OOM

导出dump文件:jmap -dump:format=b,file=dump.hprof 777 (pid)

结合 jvisualvm 进行调试

查看最多跟业务有关对象 -> 找到GCRoot -> 查看线程栈

线上发生死锁定位 & 避免

定位

使用 jstack 命令进行定位死锁位置

使用 VisualVM 工具自动检测并显示死锁的线程

避免

产生死锁的四大因素

  • 互斥
  • 占有且等待
  • 不可抢占
  • 循环等待

根据以上四大原因进行死锁的避免,可以尝试破坏上述条件中的一个或者多个

订单超时自动取消实现方案

1.JDK自带的延时队列(DelayQueue)

  • 优点 :简单,不需要借组其他第三方组件,成本低
  • 缺点 :占用内存大

2.RocketMQ的定时消息

  • 优点 :使用简单,支持分布式,精度高,支持任意时刻

  • 缺点 :定时时长最 大值24小时,成本高,

3.Redis的过期监听

  • 优点 :使用简单,支持分布式

  • 缺点 : 不可靠,有可能丢失数据,维护成本高

4.定时任务分布式批处理

实现幂等性方案

  1. 前端限制(防君子,不防小人)
  2. 使用redis的setnx

扫码登录流程

1生成二维码

2.扫码

3.确认登录

Redis实现上亿用户实时积分排行榜

使用有序集合(Sorted Set)

Redis 的有序集合(Sorted Set)非常适合实现排行榜,因为它能根据分数(score)自动排序,并且支持高效的插入、更新和查询操作。

  • 键(Key):例如 leaderboard
  • 成员(Member):用户ID
  • 分数(Score):用户积分

使用 ZADD 命令添加或更新用户积分

1
ZADD leaderboard <score> <user_id>

代码示例

Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Service
public class LeaderboardService {

private static final String LEADERBOARD_KEY = "leaderboard";

@Autowired
private RedisTemplate<String, String> redisTemplate;

// 添加或更新用户积分
public void addOrUpdateUserScore(String userId, double score) {
redisTemplate.opsForZSet().add(LEADERBOARD_KEY, userId, score);
}

// 获取用户排名(从高到低)
public Long getUserRank(String userId) {
return redisTemplate.opsForZSet().reverseRank(LEADERBOARD_KEY, userId);
}

// 获取用户积分
public Double getUserScore(String userId) {
return redisTemplate.opsForZSet().score(LEADERBOARD_KEY, userId);
}

// 获取前 N 名用户
public Set<ZSetOperations.TypedTuple<String>> getTopUsers(int limit) {
return redisTemplate.opsForZSet().reverseRangeWithScores(LEADERBOARD_KEY, 0, limit - 1);
}

// 获取某个积分范围内的用户
public Set<ZSetOperations.TypedTuple<String>> getUsersInRange(double minScore, double maxScore) {
return redisTemplate.opsForZSet().reverseRangeByScoreWithScores(LEADERBOARD_KEY, minScore, maxScore);
}
}

Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@RestController
@RequestMapping("/leaderboard")
public class LeaderboardController {

@Autowired
private LeaderboardService leaderboardService;

// 添加或更新用户积分
@PostMapping("/add")
public String addOrUpdateUserScore(@RequestParam String userId, @RequestParam double score) {
leaderboardService.addOrUpdateUserScore(userId, score);
return "Score updated for user: " + userId;
}

// 获取用户排名
@GetMapping("/rank")
public Long getUserRank(@RequestParam String userId) {
return leaderboardService.getUserRank(userId);
}

// 获取用户积分
@GetMapping("/score")
public Double getUserScore(@RequestParam String userId) {
return leaderboardService.getUserScore(userId);
}

// 获取前 N 名用户
@GetMapping("/top")
public Set<LeaderboardService.ZSetOperations.TypedTuple<String>> getTopUsers(@RequestParam int limit) {
return leaderboardService.getTopUsers(limit);
}

// 获取某个积分范围内的用户
@GetMapping("/range")
public Set<LeaderboardService.ZSetOperations.TypedTuple<String>> getUsersInRange(
@RequestParam double minScore, @RequestParam double maxScore) {
return leaderboardService.getUsersInRange(minScore, maxScore);
}
}

对于上亿用户的数据量,可以考虑以下优化

  1. 分片 :将数据分散到多个 Redis 实例。
  2. 异步写入 :使用消息队列异步更新积分,减少 Redis 压力。
  3. 定期清理 :移除积分过低的用户,减少数据量。
  4. 分桶 :按照排名进行分桶,提高性能
打赏
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!

扫一扫,分享到微信

微信分享二维码
  • Copyrights © 2015-2025 Immanuel
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

微信