分布式事务在点赞功能中的实现

avatar
cmdragon 大乘
image image

扫描二维码关注或者微信搜一搜:编程智域 前端至全栈交流与成长

探索数千个预构建的 AI 应用,开启你的下一个伟大创意https://tools.cmdragon.cn/

第一章:分布式事务在点赞功能中的应用

1.1 分布式事务核心原理

在微服务架构中,一个业务操作可能涉及多个独立服务的数据修改。传统数据库事务的ACID特性(原子性、一致性、隔离性、持久性)在分布式环境中面临挑战:

  • 原子性困境:跨服务的操作无法使用单一数据库事务保证
  • 数据一致性:各服务数据库独立,无法通过锁机制实现强一致性
  • 失败补偿:部分操作成功后出现异常需要回滚的复杂处理

以点赞功能为例,典型业务场景包含:

1
用户服务(扣除点赞次数) → 文章服务(增加点赞数) → 通知服务(发送消息)

1.2 Tortoise-ORM事务配置

Tortoise-ORM提供两种事务管理方式:

基础事务模式

1
2
3
async with in_transaction() as conn:
await User.filter(id=user_id).update(likes=F('likes') + 1)
await Article.filter(id=article_id).update(likes=F('likes') + 1)

装饰器事务模式

1
2
3
4
5
@atomic()
async def like_article(user_id: int, article_id: int):
user = await User.get(id=user_id)
article = await Article.get(id=article_id)
await Like.create(user=user, article=article)

1.3 点赞功能实现方案

完整实现包含防重复点赞和事务处理:

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
# 数据库模型
class Like(Model):
id = fields.IntField(pk=True)
user = fields.ForeignKeyField('models.User')
article = fields.ForeignKeyField('models.Article')
created_at = fields.DatetimeField(auto_now_add=True)

class Meta:
unique_together = (('user', 'article'),) # 唯一约束防重复


# 业务逻辑
async def toggle_like(user_id: int, article_id: int):
try:
async with in_transaction():
# 检查是否已点赞
exists = await Like.exists(user_id=user_id, article_id=article_id)
if exists:
# 取消点赞
await Like.filter(user_id=user_id, article_id=article_id).delete()
delta = -1
else:
# 新增点赞
await Like.create(user_id=user_id, article_id=article_id)
delta = 1

# 更新统计数
await User.filter(id=user_id).update(likes_count=F('likes_count') + delta)
await Article.filter(id=article_id).update(likes_count=F('likes_count') + delta)

return {"status": "success", "action": "unlike" if exists else "like"}
except IntegrityError:
raise HTTPException(status_code=400, detail="操作冲突,请重试")

1.4 跨服务事务处理

当涉及多个微服务时,采用Saga事务模式实现最终一致性:

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
# Saga协调器示例
class LikeSaga:
def __init__(self):
self.compensation_actions = []

async def execute(self):
try:
# 阶段1:预扣点赞次数
await user_service.decrement_quota(user_id)
self.compensation_actions.append(
lambda: user_service.increment_quota(user_id)
)

# 阶段2:增加文章点赞
await article_service.increment_likes(article_id)
self.compensation_actions.append(
lambda: article_service.decrement_likes(article_id)
)

# 阶段3:发送通知
await notification_service.send_like_notice(user_id, article_id)
except Exception as e:
# 执行补偿操作
for action in reversed(self.compensation_actions):
await action()
raise

课后Quiz

Q1:为什么在点赞功能中需要唯一约束?
A. 提高查询速度
B. 防止用户重复点赞
C. 减少数据库存储空间
D. 方便统计用户数据

正确答案:B
解析:唯一约束确保(user_id, article_id)组合的唯一性,从数据库层面防止重复点赞,比应用层检查更可靠。

Q2:Saga模式中的补偿操作应该按什么顺序执行?
A. 任意顺序
B. 正向顺序
C. 反向顺序
D. 随机顺序

正确答案:C
解析:补偿操作需要按照与业务操作相反的顺序执行,例如先撤销最后完成的操作。

常见报错解决

错误1:TransactionManagementError - 事务超时
原因分析:

  • 长时间未提交的事务导致锁等待超时
  • 复杂事务处理时间超过数据库配置的超时阈值

解决方案:

  1. 优化事务内的操作,减少不必要的数据库交互
  2. 在事务开始时设置合理超时时间:
1
2
async with in_transaction(timeout=30) as conn:  # 30秒超时
# 业务操作

错误2:IntegrityError - 唯一约束冲突
原因分析:

  • 并发请求导致同时插入相同数据
  • 未正确处理重复请求

解决方案:

  1. 在前端添加防重提交机制
  2. 在后端使用upsert操作:
1
2
3
4
5
await Like.update_or_create(
user_id=user_id,
article_id=article_id,
defaults={'created_at': datetime.now()}
)

错误3:ValidationError - 请求参数校验失败
原因分析:

  • 客户端传递的参数不符合Pydantic模型要求
  • 数值型参数传递了字符串类型

解决方案:

  1. 检查请求体是否符合接口文档要求
  2. 在路由中添加详细的响应模型:
1
2
3
@app.post("/likes", response_model=LikeResponse, responses={
422: {"model": ValidationErrorResponse}
})

余下文章内容请点击跳转至 个人博客页面 或者 扫码关注或者微信搜一搜:编程智域 前端至全栈交流与成长,阅读完整的文章:

往期文章归档: