redis
# 为什么会有事务? -mysql 支持并发---》允许同时有多个客户端来操作 -出现并发安全问题---》最简单粗暴--》串行化--》效率太低 -既要保证效率,又要保证数据安全 - # 事务四大特性 # 事务隔离级别 # 脏读,不可重复,幻读 # https://zhuanlan.zhihu.com/p/622987268 Django是支持事务操作的,它的默认事务行为是自动提交, 具体表现形式为:每次数据库操作(比如调用save()方法)会立即被提交到数据库中。 User.objects.filter(id=1).update(name='lqz') 默认情况,只要sql执行完,就会自动提交,conn.commit() 但是如果你希望把连续的SQL操作包裹在一个事务里,就需要手动开启事务 UpAndDown.objects.create(aritcle_id=1,up=True) # 提交了 异常了,UpAndDown已经提交了,不会回滚,数据不一致 Atricel.object.filter(id=1).update(up_and_down=F(up_and_down)+1) # 提交了 # 根据粒度不同,三种方式手动开启 ######## 全局########## -全局,每次请求在一个事务中,粒度太大,事务时间很长 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'lqz', 'HOST': '127.0.0.1', 'PORT': '3306', 'USER': 'lqz', 'PASSWORD': 'lqz123', #全局开启事务,绑定的是http请求响应整个过程 'ATOMIC_REQUESTS': True, } } # 局部禁用全局事务 from django.db import transaction # 局部禁用事务 @transaction.non_atomic_requests def seckill(request): return HttpResponse('秒杀成功') # 多数据库,用default的视图不受事务控制 @transaction.non_atomic_requests(using='default') def seckill(request): return HttpResponse('秒杀成功') ####### 视图开启事务############## # fbv开启 from django.db import transaction @transaction.atomic def seckill(request): return HttpResponse('秒杀成功') # cbv开启 from django.db import transaction from rest_framework.views import APIView class SeckillAPIView(APIView): @transaction.atomic def post(self, request): pass ################ 局部使用事务##################### from django.db import transaction def seckill(request): with transaction.atomic(): save() update() return HttpResponse('秒杀成功')
# 1 普通事务操作(手动操作) transaction.atomic() # 开启事务 transaction.commit() # 提交事务 transaction.rollback() # 回滚事务 # 2 可以使用上下文管理器来控制(自动操作) with transaction.atomic(): # 自动提交和回滚 # 3 保存点 savepoint -开启事务 干了点事 设置保存点1 干了点事 设置一个保存点2 干了点事 回滚到干完第二个事,回滚到保存点2 ''' 在事务操作中,我们还会经常显式地设置保存点(savepoint) 一旦发生异常或错误,我们使用savepoint_rollback方法让程序回滚到指定的保存点 如果没有问题,就使用savepoint_commit方法提交事务 ''' ## 使用with,上下文管理器 # def save_book(request): # # 测试事务开启和使用保存点提交和回滚事务 # with transaction.atomic(): # sid = transaction.savepoint() # print(sid) # try: # Book.objects.create(name='三国', price=88, xx='yy') # # except Exception as e: # transaction.savepoint_rollback(sid) # # 没有任何问题,提交保存 # transaction.savepoint_commit(sid) # # return HttpResponse('新增图书成功') # 不使用上下文管理器 def save_book(request): # 测试事务开启和使用保存点提交和回滚事务 transaction.atomic() sid = transaction.savepoint() print(sid) try: Book.objects.create(name='三国', price=88, xx='yy') except Exception as e: transaction.savepoint_rollback(sid) # 没有任何问题,提交保存 transaction.savepoint_commit(sid) return HttpResponse('新增图书成功') transaction.atomic() # 开启事务 sid = transaction.savepoint() # 设置保存点 transaction.savepoint_rollback(sid) # 回滚到保存点 transaction.savepoint_commit(sid) #提交保存点
# 有的时候我们希望当前事务提交后立即执行额外的任务,比如客户下订单后立即邮件通知卖家 ###### 案例一################## def send_email(): print('发送邮件给卖家了') def seckill(request): with transaction.atomic(): # 设置回滚点,一定要开启事务 sid = transaction.savepoint() print(sid) try: book = Book.objects.get(pk=1) book.count = book.count-1 book.save() except Exception as e: # 如发生异常,回滚到指定地方 transaction.savepoint_rollback(sid) else: transaction.savepoint_commit(sid) #transaction.on_commit(send_email) transaction.on_commit(lambda: send_sms.delay('1898288322')) return HttpResponse('秒杀成功') ##### 案例二:celery中使用### transaction.on_commit(lambda: send_sms.delay('1898288322'))
# 线上卖图书 -图书表 图书名字,图书价格,库存字段 -订单表: 订单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数据库
begin; # 开启事务 # for update 是行锁还是表锁? select * from book where id = 1 for update; # 行锁,相当于加了悲观锁 select * from book for update; # 表锁 # order表中加数据 insert into order (name,order_id) values('买了个书','wer0-asdf-31sdf') update book set count = count - 1 where id = 1; # 更新 commit; #提交事务
#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('库存不足,秒杀失败')
# 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('库存不足,秒杀失败')
# 6 乐观锁,实现秒杀 # 乐观锁秒杀--高级版---》只要秒杀人数超过 总数量,库存一定为0 @transaction.atomic def seckill(request): # 锁住查询到的book对象,直到事务结束 ### 乐观锁可能会失败,我们一直尝试秒杀,直到秒成功或库存不足 while True: 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()), 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) print('被别人扣减了,继续秒杀') continue else: transaction.savepoint_rollback(sid) return HttpResponse('库存不足,秒杀失败') return HttpResponse('库存不足,秒杀失败')
# 加入celery--》异步悲观锁
异步乐观锁下单
# 一台服务器: -python3.8 环境 djagno +uwsgi+代码 -nginx软件 -mysql 5.7 -redis # 每个都做成一个容器 -djagno项目容器:python3.9 构建的django,项目依赖模块,uwsgi,代码 -nginx容器:目录映射,映射到宿主机,代理vue前端,编译后的静态文件 -mysql 容器:创建,创用户,密码,luffy库 -redis 容器,跑起来即可
luffy -docker_compose_files # nginx有自己的配置文件,redis自己的配置,mysql的配置 nginx # 文件夹,nginx配置---》 -default.conf # 手动配置,需要监听两个端口:80 给前端和8000转发给django redis # 文件夹 - redis.conf # nginx监听地址和端口,持久化方案 mysql.env # 配置文件,mysql的配置---》key vaule形式---》环境变量 # 运行mysql容器时, -e参数 ,设置超级用户密码,创建luffy用户和库 -luffy_api # 原来路飞后端项目 -Dockerfile # 用来构建 项目镜像 -luffy.ini # 等同于 luffy.xml uwsgi的配置文件 -luffy.sql # 默认数据 -luffycity # 前端项目 -docker-compose.yml # docker-compose的配置文件
# 1 git clone https://gitee.com/liuqingzheng/luffy.git # 2 把luffycity/dist 文件夹删除 # 3 把\luffy\luffycity\src\assets\js\settings.js后端地址改成上线地址(服务器地址) export default { base_url: "http://10.0.0.111:8000/api/v1/" } # 4 来到前端路径下:luffy\luffycity cnpm install # 安装依赖 # 5 编译,在\luffy\luffycity\dist文件夹 npm run build #6 提交到git上 #7 在部署的机器上,git clone 下来 #8 进入到项目目录 docker-compose up # 9 导入 sql文件 # 10 访问 服务器地址的80端口,即可
#依赖镜像名称和ID FROM python:3.8 #指定镜像创建者信息 MAINTAINER lqz #切换工作目录 RUN mkdir /soft WORKDIR /soft COPY ./requestment.txt /soft/requestment.txt RUN pip install -r requestment.txt -i https://pypi.doubanio.com/simple CMD ["uwsgi", "./luffy.ini"]
version: "3" services: nginx: image: nginx container_name: luffy_nginx ports: - "80:80" - "8000:8000" restart: always volumes: - ./luffycity/dist:/var/www/html - ./docker_compose_files/nginx:/etc/nginx/conf.d depends_on: - django networks: - web django: build: context: ./luffy_api dockerfile: Dockerfile container_name: luffy_django # command: python manage_pro.py makemigrations && python manage_pro.py migrate && uwsgi ./luffy.ini restart: always ports: - "8080:8080" volumes: - ./luffy_api:/soft environment: - TZ=Asia/Shanghai depends_on: - mysql - redis networks: - web redis: image: redis:6.0-alpine container_name: luffy_redis ports: - "6379:6379" volumes: - ./docker_compose_files/redis/data:/data - ./docker_compose_files/redis/redis.conf:/etc/redis/redis.conf command: redis-server /etc/redis/redis.conf networks: - web mysql: image: mysql:5.7 container_name: luffy_mysql restart: always ports: - "3306:3306" env_file: - ./docker_compose_files/mysql.env volumes: - ./docker_compose_files/mysql/data:/var/lib/mysql - ./docker_compose_files/mysql/logs:/var/log/mysql - ./docker_compose_files/mysql/conf:/etc/mysql/conf.d networks: - web networks: web:
# 操作系统 -win:笔记本,台式机--》win开发 -mac:公司直接配 mac ---》类unix系统-->类似于linux -linux:有的模块不支持win,只能在linux上开发 -台式机---》乌班图--》pycharm # 现在就要用win开发---》运行测试环境应该是linux # 使用win的pycharm---》远程链接linux开发 # centos,乌班图都同理 -把写好的代码--》放到linux上
浙公网安备 33010602011771号