Django+Postgres如何原子实现用户维度计数器自增并赋值给新建条目
方案1:UPDATE配合RETURNING直接获取自增后的值(最优,性能最高)
PostgreSQL原生支持UPDATE语句的RETURNING子句,可以在更新用户计数器的同时直接返回更新后的值,全程仅1次数据库交互,天然原子,无并发冲突,是性能最优的方案。
Django 4.1及以上版本可以直接用ORM实现:
from django.db import models from django.contrib.auth import get_user_model User = get_user_model() def create_item(user_id, name): # 自增计数器同时返回更新后的值 new_counter = User.objects.filter(id=user_id).annotate( new_counter=models.F('item_counter') + 1 ).update( item_counter=models.F('new_counter'), returning=('new_counter',) )[0][0] # 用返回值创建Item记录 return Item.objects.create(name=name, user_id=user_id, item_num=new_counter)
如果使用Django 4.1以下版本,可以直接执行原生SQL实现完全一致的效果:
from django.db import connection def create_item(user_id, name): with connection.cursor() as cursor: cursor.execute(""" UPDATE auth_user SET item_counter = item_counter + 1 WHERE id = %s RETURNING item_counter """, [user_id]) new_counter = cursor.fetchone()[0] return Item.objects.create(name=name, user_id=user_id, item_num=new_counter)
方案2:显式事务加行锁(兼容性最好)
如果需要兼容更多场景,可以用事务加行锁的方式实现,逻辑更直观:
from django.db import transaction @transaction.atomic def create_item(user_id, name): # 锁定对应用户行,事务结束前其他请求无法修改该行 user = User.objects.select_for_update().get(id=user_id) user.item_counter += 1 user.save(update_fields=['item_counter']) # 创建Item记录 return Item.objects.create( name=name, user=user, item_num=user.item_counter )
该方案需要2次数据库交互,性能略低于方案1,但不需要依赖高版本Django特性,代码可读性更强。
注意事项
- 禁止先在Python层查询计数器值再自增更新,这种写法在并发场景下会出现竞态条件,导致多个条目拿到相同的
item_num - 两种方案都天然支持多用户独立计数,更新仅针对单用户行加锁/操作,不同用户的创建请求不会互相阻塞
内容的提问来源于stack exchange,提问作者user3599803
相关产品推荐
相关产品推荐

