如何在DynamoDB中处理点赞(含取消)的并发问题?
DynamoDB处理点赞/取消点赞并发问题的解决方案
核心思路
要解决这个问题,关键是同时跟踪帖子的点赞用户状态和点赞总数,并利用DynamoDB的原子更新+条件表达式机制,确保并发操作的正确性。仅存储upvote总数的设计无法判断用户是否已点赞,必然会出现重复操作导致的数据错误。
表结构调整
基于需求优化后的表结构:
- 哈希键:
post_id(唯一标识帖子) - 新增属性:
voted_users(String Set类型),存储所有给该帖子点过赞的用户ID
注:无需单独维护
upvote属性,点赞数可直接通过voted_users集合的长度计算,避免数据冗余和不一致风险。如果业务需要单独存储upvote,也可在更新集合时同步更新该字段,但推荐用集合长度计算更可靠。
具体操作实现
1. 点赞操作
用户点击点赞按钮时,执行以下逻辑:
- 前置条件:当前用户ID不在
voted_users集合中 - 原子操作:将用户ID添加到
voted_users集合 - 并发保障:DynamoDB的
UpdateItem是原子操作,条件不满足时会抛出ConditionalCheckFailedException,不会执行无效更新
示例代码(Python boto3):
import boto3 from botocore.exceptions import ClientError dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('your_post_table') def handle_like(post_id, user_id): try: response = table.update_item( Key={'post_id': post_id}, UpdateExpression='ADD voted_users :user_id', ConditionExpression='NOT contains(voted_users, :user_id)', ExpressionAttributeValues={':user_id': {user_id}}, ReturnValues='ALL_NEW' ) upvote_count = len(response['Attributes']['voted_users']) return {"status": "success", "upvote": upvote_count, "action": "liked"} except ClientError as e: if e.response['Error']['Code'] == 'ConditionalCheckFailedException': item = table.get_item(Key={'post_id': post_id}).get('Item', {}) upvote_count = len(item.get('voted_users', set())) return {"status": "failed", "upvote": upvote_count, "action": "already_liked"} raise
2. 取消点赞操作
用户再次点击按钮(取消点赞)时,执行以下逻辑:
- 前置条件:当前用户ID在
voted_users集合中 - 原子操作:将用户ID从
voted_users集合移除
示例代码:
def handle_unlike(post_id, user_id): try: response = table.update_item( Key={'post_id': post_id}, UpdateExpression='DELETE voted_users :user_id', ConditionExpression='contains(voted_users, :user_id)', ExpressionAttributeValues={':user_id': {user_id}}, ReturnValues='ALL_NEW' ) upvote_count = len(response['Attributes']['voted_users']) return {"status": "success", "upvote": upvote_count, "action": "unliked"} except ClientError as e: if e.response['Error']['Code'] == 'ConditionalCheckFailedException': item = table.get_item(Key={'post_id': post_id}).get('Item', {}) upvote_count = len(item.get('voted_users', set())) return {"status": "failed", "upvote": upvote_count, "action": "not_liked"} raise
前端配合逻辑
- 按钮点击前,可通过缓存或查询接口确认当前用户的点赞状态(即
voted_users是否包含当前用户ID) - 调用对应的点赞/取消点赞接口
- 根据接口返回的
upvote数和action字段更新按钮状态(比如成功点赞后按钮显示"取消点赞",反之显示"点赞") - 遇到条件失败的情况(如重复点击点赞),直接同步最新状态即可,无需额外报错提示
并发安全性说明
DynamoDB的UpdateItem是原子性操作,条件表达式在服务器端校验,只有满足条件才会执行更新。即使多个用户同时操作同一帖子,也不会出现:
- 同一用户重复点赞导致点赞数多次增加
- 取消未点赞的帖子导致点赞数为负数
- 点赞数与实际点赞用户数不一致的情况
内容的提问来源于stack exchange,提问作者xoxocoder
相关产品推荐
相关产品推荐

