本文目录导读:

- 基础数据库方案(不推荐用于高并发)
- Redis + 异步落库方案(高性能方案)
- 使用 LUA 脚本保证原子性(推荐方案)
- 批量处理方案(定时落库)
- 热点数据缓存优化
- 完整的Controller和DTO
- 数据库表结构
- 性能优化总结
我来设计一个完整的Java高并发点赞功能案例,包含多种实现方案和优化策略。
基础数据库方案(不推荐用于高并发)
// 最基本的实现,会有性能问题
@Service
public class LikeServiceBasic {
@Autowired
private LikeMapper likeMapper;
@Transactional
public Result like(Long userId, Long targetId) {
// 检查是否已点赞
LikeRecord record = likeMapper.findByUserIdAndTargetId(userId, targetId);
if (record != null) {
return Result.error("已点赞过");
}
// 插入点赞记录
likeMapper.insert(userId, targetId);
// 更新点赞数
likeMapper.incrementCount(targetId);
return Result.success("点赞成功");
}
}
Redis + 异步落库方案(高性能方案)
1 Redis 点赞服务
@Service
public class LikeService {
@Autowired
private StringRedisTemplate redisTemplate;
@Autowired
private LikeAsyncService likeAsyncService;
private static final String LIKE_KEY_PREFIX = "like:target:";
private static final String LIKE_USER_KEY_PREFIX = "like:user:";
private static final String LIKE_COUNT_KEY_PREFIX = "like:count:";
/**
* 点赞/取消点赞
*/
public Result like(Long userId, Long targetId) {
String userLikeKey = LIKE_USER_KEY_PREFIX + targetId;
String countKey = LIKE_COUNT_KEY_PREFIX + targetId;
// 使用Redis的Set判断是否已点赞
Boolean isLiked = redisTemplate.opsForSet().isMember(userLikeKey, userId.toString());
if (Boolean.TRUE.equals(isLiked)) {
// 取消点赞
redisTemplate.opsForSet().remove(userLikeKey, userId.toString());
Long count = redisTemplate.opsForValue().decrement(countKey);
// 异步记录取消点赞操作
likeAsyncService.unlike(userId, targetId);
return Result.success("取消点赞成功", count);
} else {
// 点赞
redisTemplate.opsForSet().add(userLikeKey, userId.toString());
Long count = redisTemplate.opsForValue().increment(countKey);
// 异步记录点赞操作
likeAsyncService.like(userId, targetId);
return Result.success("点赞成功", count);
}
}
/**
* 获取点赞数
*/
public Long getLikeCount(Long targetId) {
String countKey = LIKE_COUNT_KEY_PREFIX + targetId;
String count = redisTemplate.opsForValue().get(countKey);
if (count == null) {
// 从数据库加载
Long dbCount = likeAsyncService.getCountFromDB(targetId);
redisTemplate.opsForValue().set(countKey, String.valueOf(dbCount));
return dbCount;
}
return Long.parseLong(count);
}
/**
* 判断用户是否已点赞
*/
public boolean isLiked(Long userId, Long targetId) {
String userLikeKey = LIKE_USER_KEY_PREFIX + targetId;
return Boolean.TRUE.equals(redisTemplate.opsForSet().isMember(userLikeKey, userId.toString()));
}
/**
* 获取点赞用户列表(分页)
*/
public List<Long> getLikeUsers(Long targetId, int page, int size) {
String userLikeKey = LIKE_USER_KEY_PREFIX + targetId;
Set<String> members = redisTemplate.opsForSet().members(userLikeKey);
return members.stream()
.map(Long::parseLong)
.skip((page - 1) * size)
.limit(size)
.collect(Collectors.toList());
}
}
2 异步落库服务
@Service
public class LikeAsyncService {
@Autowired
private LikeMapper likeMapper;
@Autowired
private LikeCountMapper likeCountMapper;
// 使用线程池异步处理
@Async("likeExecutor")
public void like(Long userId, Long targetId) {
try {
// 插入点赞记录
LikeRecord record = new LikeRecord();
record.setUserId(userId);
record.setTargetId(targetId);
record.setCreateTime(new Date());
likeMapper.insert(record);
// 更新点赞数(使用数据库乐观锁或原子操作)
likeCountMapper.incrementCount(targetId);
} catch (DuplicateKeyException e) {
// 已存在记录,忽略
log.warn("点赞记录已存在: userId={}, targetId={}", userId, targetId);
} catch (Exception e) {
log.error("异步点赞失败", e);
}
}
@Async("likeExecutor")
public void unlike(Long userId, Long targetId) {
try {
likeMapper.delete(userId, targetId);
likeCountMapper.decrementCount(targetId);
} catch (Exception e) {
log.error("异步取消点赞失败", e);
}
}
/**
* 从数据库获取点赞数
*/
public Long getCountFromDB(Long targetId) {
return likeCountMapper.selectCount(targetId);
}
}
3 异步线程池配置
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean("likeExecutor")
public Executor likeExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(1000);
executor.setKeepAliveSeconds(60);
executor.setThreadNamePrefix("like-executor-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
使用 LUA 脚本保证原子性(推荐方案)
@Service
public class LikeServiceWithLua {
@Autowired
private StringRedisTemplate redisTemplate;
private DefaultRedisScript<Long> likeScript;
private DefaultRedisScript<Long> unlikeScript;
@PostConstruct
public void init() {
// 点赞脚本,保证原子性
likeScript = new DefaultRedisScript<>();
likeScript.setScriptText(
"local userLikeKey = KEYS[1] " +
"local countKey = KEYS[2] " +
"local userId = ARGV[1] " +
// 判断是否已点赞
"local isLiked = redis.call('SISMEMBER', userLikeKey, userId) " +
"if isLiked == 1 then " +
" return 0 " +
"end " +
// 添加点赞用户
"redis.call('SADD', userLikeKey, userId) " +
// 增加计数
"return redis.call('INCR', countKey)"
);
likeScript.setResultType(Long.class);
// 取消点赞脚本
unlikeScript = new DefaultRedisScript<>();
unlikeScript.setScriptText(
"local userLikeKey = KEYS[1] " +
"local countKey = KEYS[2] " +
"local userId = ARGV[1] " +
// 判断是否已点赞
"local isLiked = redis.call('SISMEMBER', userLikeKey, userId) " +
"if isLiked == 0 then " +
" return 0 " +
"end " +
// 移除点赞用户
"redis.call('SREM', userLikeKey, userId) " +
// 减少计数,但保证不为负数
"local count = redis.call('DECR', countKey) " +
"if count < 0 then " +
" redis.call('SET', countKey, 0) " +
" return 0 " +
"end " +
"return count"
);
unlikeScript.setResultType(Long.class);
}
public Result like(Long userId, Long targetId) {
String userLikeKey = "like:user:" + targetId;
String countKey = "like:count:" + targetId;
Long result = redisTemplate.execute(
likeScript,
Arrays.asList(userLikeKey, countKey),
userId.toString()
);
if (result == 0) {
return Result.error("已点赞过");
}
// 异步落库
asyncLikeToDB(userId, targetId);
return Result.success("点赞成功", result);
}
public Result unlike(Long userId, Long targetId) {
String userLikeKey = "like:user:" + targetId;
String countKey = "like:count:" + targetId;
Long result = redisTemplate.execute(
unlikeScript,
Arrays.asList(userLikeKey, countKey),
userId.toString()
);
if (result == 0) {
return Result.error("未点赞");
}
// 异步落库
asyncUnlikeDB(userId, targetId);
return Result.success("取消点赞成功", result);
}
}
批量处理方案(定时落库)
@Component
public class LikeBatchService {
@Autowired
private LikeMapper likeMapper;
// 使用阻塞队列缓存增量
private BlockingQueue<LikeEvent> likeEvents = new LinkedBlockingQueue<>(10000);
@Scheduled(cron = "0 */5 * * * *") // 每5分钟执行
public void batchSaveToDB() {
List<LikeEvent> events = new ArrayList<>();
likeEvents.drainTo(events, 1000);
if (!events.isEmpty()) {
// 批量插入数据库
likeMapper.batchInsert(events);
// 批量更新计数
Map<Long, Long> countMap = events.stream()
.filter(e -> e.getAction() == Action.LIKE)
.collect(Collectors.groupingBy(
LikeEvent::getTargetId,
Collectors.counting()
));
likeMapper.batchUpdateCount(countMap);
}
}
@Data
public static class LikeEvent {
private Long userId;
private Long targetId;
private Action action;
private Date createTime;
public enum Action {
LIKE, UNLIKE
}
}
}
热点数据缓存优化
@Service
public class LikeOptimizedService {
@Autowired
private StringRedisTemplate redisTemplate;
// 本地缓存(使用Caffeine)
private Cache<Long, Long> localCache = Caffeine.newBuilder()
.maximumSize(10000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build();
// 针对高并发热点内容
public Long getLikeCountWithCache(Long targetId) {
// 先从本地缓存获取
Long count = localCache.getIfPresent(targetId);
if (count != null) {
return count;
}
// 从Redis获取
String countKey = "like:count:" + targetId;
String value = redisTemplate.opsForValue().get(countKey);
if (value == null) {
// 从数据库加载
count = loadCountFromDB(targetId);
// 设置本地缓存
localCache.put(targetId, count);
// 设置Redis缓存(防止缓存穿透)
redisTemplate.opsForValue().set(countKey, String.valueOf(count), 30, TimeUnit.MINUTES);
} else {
count = Long.parseLong(value);
localCache.put(targetId, count);
}
return count;
}
// 布隆过滤器防止缓存穿透
private BloomFilter<Long> bloomFilter;
@PostConstruct
public void init() {
// 预计10万数据,误判率0.01
bloomFilter = BloomFilter.create(
Funnels.longFunnel(),
100000,
0.01
);
// 初始化数据
loadAllTargetIdsFromDB().forEach(bloomFilter::put);
}
public boolean isTargetExists(Long targetId) {
return bloomFilter.mightContain(targetId);
}
}
完整的Controller和DTO
@RestController
@RequestMapping("/api/like")
public class LikeController {
@Autowired
private LikeService likeService;
/**
* 点赞
*/
@PostMapping("/{targetId}")
public Result like(@PathVariable Long targetId,
@RequestParam Long userId) {
return likeService.like(userId, targetId);
}
/**
* 取消点赞
*/
@DeleteMapping("/{targetId}")
public Result unlike(@PathVariable Long targetId,
@RequestParam Long userId) {
return likeService.unlike(userId, targetId);
}
/**
* 获取点赞数
*/
@GetMapping("/count/{targetId}")
public Result getCount(@PathVariable Long targetId) {
Long count = likeService.getLikeCount(targetId);
return Result.success(count);
}
/**
* 检查是否已点赞
*/
@GetMapping("/status/{targetId}")
public Result isLiked(@PathVariable Long targetId,
@RequestParam Long userId) {
boolean liked = likeService.isLiked(userId, targetId);
return Result.success(liked);
}
}
数据库表结构
-- 点赞记录表
CREATE TABLE `like_record` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_id` bigint(20) NOT NULL COMMENT '用户ID',
`target_id` bigint(20) NOT NULL COMMENT '目标ID',
`target_type` tinyint(4) DEFAULT 1 COMMENT '目标类型:1-文章,2-评论,3-视频',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_target` (`user_id`, `target_id`, `target_type`),
KEY `idx_target` (`target_id`, `target_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='点赞记录表';
-- 点赞计数表
CREATE TABLE `like_count` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`target_id` bigint(20) NOT NULL COMMENT '目标ID',
`target_type` tinyint(4) DEFAULT 1,
`like_count` bigint(20) NOT NULL DEFAULT 0 COMMENT '点赞数',
`version` int(11) NOT NULL DEFAULT 0 COMMENT '版本号',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_target` (`target_id`, `target_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='点赞计数表';
性能优化总结
优化策略:
- Redis缓存:读多写少的场景,使用Redis缓存点赞状态和计数
- 异步处理:点赞操作异步落库,减轻数据库压力
- 批量操作:定时批量同步到数据库
- 本地缓存:使用Caffeine缓存热点数据
- 布隆过滤器:防止缓存穿透
- Lua脚本:保证Redis操作原子性
性能指标:
- QPS:单机可支持 5000+ 点赞操作
- 响应时间:P99 小于 10ms
- 数据一致性:最终一致,5分钟内同步到数据库
监控告警:
@Component
public class LikeMonitor {
// 使用Micrometer指标监控
private Counter likeCounter = Metrics.counter("like.total");
private Timer likeTimer = Metrics.timer("like.execution.time");
// 执行周期性同步确保数据安全
@Scheduled(cron = "0 0 1 * * ?") // 每天凌晨1点
public void syncDataToDB() {
// 全量同步
}
// 监控Redis健康状态
@Scheduled(cron = "0 */5 * * * ?")
public void checkRedisStatus() {
// 检查Redis连接和内存使用
}
}
这个方案可以支撑大规模并发点赞场景,同时保证数据最终一致性,实际使用时需要根据业务场景进行调整和优化。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播