前期准备
python
# 线上卖图书
-图书表 图书名字,图书价格,库存字段
-订单表: 订单id,订单名字
# 表准备
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.IntegerField() #
count = models.SmallIntegerField(verbose_name='库存')
class Order(models.Model):
order_id = models.CharField(max_length=64)
order_name = models.CharField(max_length=32)
# 使用mysql
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'lqz',
'HOST': '127.0.0.1',
'PORT': '3306',
'USER': 'lqz',
'PASSWORD': '123',
}
}
# 创建lqz数据库
1.原生mysql悲观锁
python
begin; # 开启事务
select * from goods where id = 1 for update; # 行锁
# order表中加数据
update goods set stock = stock - 1 where id = 1; # 更新
commit; #提交事务
2.orm实现上述(悲观锁)
python
#1 使用悲观锁实现下单
@transaction.atomic # 整个过程在一个事物中---》改两个表:book表减库存,订单表生成记录
def seckill(request):
# 锁住查询到的book对象,直到事务结束
sid = transaction.savepoint() # 保存点
# 悲观锁: select_for_update()
# 加锁了--》行锁还是表锁? 分情况,都有可能
#
book = Book.objects.select_for_update().filter(pk=1).first() # 加悲观锁,行锁,锁住当前行
if book.count > 0:
print('库存可以,下单')
# 订单表插入一条
Order.objects.create(order_id=str(datetime.datetime.now()), order_name='测试订单')
# 库存-1,扣减的时候,判断库存是不是上面查出来的库存,如果不是,就回滚
time.sleep(random.randint(1, 4)) # 模拟延迟
book.count=book.count-1
book.save()
transaction.savepoint_commit(sid) # 提交,释放行锁
return HttpResponse('秒杀成功')
else:
transaction.savepoint_rollback(sid) #回滚,释放行锁
return HttpResponse('库存不足,秒杀失败')
3 乐观锁秒杀--》库存还有,有的人就没成功
python
# 2 乐观锁秒杀--普通版
@transaction.atomic
def seckill(request):
# 锁住查询到的book对象,直到事务结束
sid = transaction.savepoint()
book = Book.objects.filter(pk=1).first() # 没加锁
count = book.count
print('现在的库存为:%s' % count)
if book.count > 0:
print('库存可以,下单')
Order.objects.create(order_id=str(datetime.datetime.now()), order_name='测试订单-乐观锁')
# 库存-1,扣减的时候,判断库存是不是上面查出来的库存,如果不是,就回滚
# time.sleep(random.randint(1, 4)) # 模拟延迟
res = Book.objects.filter(pk=1, count=count).update(count=count - 1)
if res >= 1: # 表示修改成功
transaction.savepoint_commit(sid)
return HttpResponse('秒杀成功')
else: # 修改不成功,回滚
transaction.savepoint_rollback(sid)
return HttpResponse('被别人改了,回滚,秒杀失败')
else:
transaction.savepoint_rollback(sid)
return HttpResponse('库存不足,秒杀失败')