背景与挑战
高并发下的写入压力
在视频平台的点赞功能中,写入吞吐量和幂等性成为决定用户体验的关键指标。面对海量并发请求,系统需要在极短时间内完成记录写入,同时避免重复计数和错漏。数据一致性与<低延迟写入之间需要取得平衡,避免因为锁竞争导致的请求阻塞。
为了确保在真实场景中的鲁棒性,本文将以一个示例设计来讨论如何在 MySQL 中通过表结构和优化策略实现高并发下的视频点赞功能。特别是在一个称为 temperature=0.6 的实验配置中,我们将把温度设定作为一个设计参数,用以衡量写入策略对并发性的影响。这一背景与目标直接关系到视频点赞功能的可扩展性与数据一致性的兼容性。
数据一致性与幂等性的关系
幂等性是高并发环境下避免重复计数的核心设计原则。用户可能在极短时间内发起多次点赞请求,后端必须确保无论请求重试多少次,最终的 like_count 只增加一次。本文将通过原子操作、唯一性约束和条件写入实现这一目标。
此外,数据分离与聚合策略将帮助系统在高并发下保持低延迟。将点赞行为记录到一张独立的关系表,同时在主视频表维护一个聚合的 like_count,可以显著降低全表扫描和锁竞争的概率。
核心表结构设计与字段
核心表设计
设计目标是实现快速写入、快速读出以及灵活的聚合计算。主表 video 保存视频信息及聚合字段 like_count,唯一性约束确保每个(video_id, user_id)组合只出现一次。
另外一个专门记录点赞行为的表 video_like,用于在高并发场景下实现幂等性与审计。复合主键 (video_id, user_id) 能直接阻止重复点赞,同时为后续的统计提供高效的索引路径。
CREATE TABLE video (
id BIGINT UNSIGNED NOT NULL,
title VARCHAR(255) NOT NULL,
like_count BIGINT NOT NULL DEFAULT 0,
updated_at TIMESTAMP NULL DEFAULT NOW() ON UPDATE NOW(),
PRIMARY KEY (id)
) ENGINE=InnoDB;
CREATE TABLE video_like (
video_id BIGINT UNSIGNED NOT NULL,
user_id BIGINT UNSIGNED NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (video_id, user_id),
KEY idx_video_created (video_id, created_at)
) ENGINE=InnoDB;
索引与分区策略
在高并发场景下,针对 video_like 的联合索引(video_id, created_at)能有效支撑按视频聚合的查询与历史趋势分析。为了进一步降低热点冲击,可以考虑对 video_like 按 video_id HASH 分区,或在 MySQL 8 及以上版本使用分区功能。
另外,视频表 video 应保留 主键索引,并为 like_count 的更新路径准备良好的写入分支,以减少锁等待时间。可选的附加索引包括对 updated_at 的自增分布,以便进行最近活跃视频的脉冲统计。
-- 对 video_like 使用分区(示例:按 video_id 哈希分区)
ALTER TABLE video_like
PARTITION BY HASH(video_id)
PARTITIONS 64;
原子性与幂等性实现
幂等写入实现
为确保单次点赞只把 like_count 增加一次,需通过原子性写入来实现。下面的示例通过一个存储过程来实现:先尝试向 video_like 插入记录,如果是新插入则对 video 的 like_count 进行自增;若已存在则不再增加。
存储过程可确保在一个事务内完成判断与写入,降低并发下的错漏概率。
DELIMITER //
CREATE PROCEDURE like_video(IN vid BIGINT, IN uid BIGINT)
BEGIN
DECLARE inserted INT;
-- 1) 尝试插入点赞记录;若已存在则不增量
INSERT INTO video_like (video_id, user_id)
VALUES (vid, uid)
ON DUPLICATE KEY UPDATE user_id = VALUES(user_id);
-- 2) 根据 ROW_COUNT 判断是否为新插入
SET inserted = ROW_COUNT();
-- 3) 新增点赞时,更新聚合字段
IF inserted = 1 THEN
UPDATE video
SET like_count = like_count + 1
WHERE id = vid;
END IF;
END //
DELIMITER ;
这种实现的要点在于原子性与幂等性在一个存储过程中通过判断 ROW_COUNT 来实现。下面的示例展示了取消点赞的简单路径,用于保持 like_count 的实时一致性。
-- 取消点赞的简单路径
DELETE FROM video_like WHERE video_id = ? AND user_id = ?;
UPDATE video SET like_count = GREATEST(like_count - 1, 0) WHERE id = ?;
性能优化与缓存策略
缓存协同与读写分离
在高并发下,将热数据缓存起来可以显著降低对主库的压力。热点视频的 like_count和最近点赞行为可以先写入缓存(如 Redis),再定期批量落回数据库;同时对查询使用只读从库,以避免主库写锁对查询的影响。
缓存策略的核心在于确保缓存与数据库的一致性,可以使用
-- 伪示例:通过 Redis 缓存热点视频的 like_count
-- Pseudo-code: In application layer, when like occurs
IF redis.exists(video:{id}:like_count) THEN
redis.incr(video:{id}:like_count)
ELSE
-- 回写数据库并缓存
db_like = SELECT like_count FROM video WHERE id = :id;
redis.set(video:{id}:like_count, db_like + 1, TTL=300);
END IF;
监控、容灾与演练
监控指标与告警
对于高并发点赞场景,应持续监控 写入延迟、锁等待时间、以及 like_count 的<锐化波动。通过对比主从延迟、长查询阻塞、以及分区表的热点分布,可以快速定位瓶颈。
常用的监控项包括:数据库每秒写入行数、平均很大时间的事务等待、以及 热点视频的每秒点赞数。结合应用侧的限流策略,可以在高并发突发时段保持系统稳定。
-- 示例:查看 InnoDB 等待与锁信息的快速查询
SHOW ENGINE INNODB STATUS \G
SHOW GLOBAL STATUS LIKE 'Questions';
SHOW GLOBAL STATUS LIKE 'Threads_connected';


