今日内容概要
- Django中实现事物的几种方式
- 事物的回滚和保存点
- 事物提交后,执行某个回调函数
- Django实现悲观锁乐观锁案例
- centos上安装redis
今日内容详细
Django中实现事物的几种方式
全局开启事务
1.全局开启事务---> 全局开启事务,绑定的是http请求响应整个过程
DATABASES = {
'default': {
'ATOMIC_REQUESTS': True,
}
}
from django.db import transaction
@transaction.non_atomic_requests
def seckill(request):
return HttpResponse('秒杀成功')
一个视图函数在一个事物中
1.fbv开启:@transaction.atomic
from django.db import transaction
@transaction.atomic
def seckill(request):
return HttpResponse('秒杀成功')
2.cbv开启:@transaction.atomic
from django.db import transaction
from rest_framework.views import APIView
class SeckillAPIView(APIView):
@transaction.atomic
def post(self, request):
pass
局部使用事物
1.局部使用事务
from django.db import transaction
def seckill(request):
with transaction.atomic():
pass
return HttpResponse('秒杀成功')
事物的回滚和保存点
普通事务操作(手动操作)
transaction.atomic()
transaction.commit()
transaction.rollback()
可以使用上下文管理器来控制(自动操作)
with transaction.atomic():
保存点
"""
开启事务
干了点事
设置保存点1
干了点事
设置一个保存点2
干了点事
回滚到干完第二个事,回滚到保存点2
"""
1.在事务操作中,我们还会经常显式地设置保存点(savepoint)
一旦发生异常或错误,我们使用savepoint_rollback方法让程序回滚到指定的保存点
如果没有问题,就使用savepoint_commit方法提交事务
使用
from .models import Book
from django.db import transaction
def seckill(request):
with transaction.atomic():
sid = transaction.savepoint()
print(sid)
try:
book = Book.objects.get(pk=1)
book.name = '红楼梦'
book.save()
except Exception as e:
transaction.savepoint_rollback(sid)
print('出异常了,回滚')
transaction.savepoint_commit(sid)
return HttpResponse('秒杀成功')
"""
transaction.atomic() # 开启事务
sid = transaction.savepoint() # 设置保存点
transaction.savepoint_rollback(sid) # 回滚到保存点
transaction.savepoint_commit(sid) #提交保存点
"""
事务提交后,执行某个回调函数
1.有的时候我们希望当前事务提交后立即执行额外的任务,比如客户下订单后立即邮件通知卖家--->transaction.on_commit()
2.案例一:客户下订单后立即邮件通知卖家
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(lambda: send_sms.delay('1898288322'))
return HttpResponse('秒杀成功')
3.案例二:celery中使用
transaction.on_commit(lambda: send_sms.delay('1898288322'))
Django实现悲观锁乐观锁案例
1.线上卖图书
-图书表 图书名字,图书价格,库存字段
-订单表: 订单id,订单名字
2.表准备
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)
3.使用mysql
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'lqz',
'HOST': '127.0.0.1',
'PORT': '3306',
'USER': 'lqz',
'PASSWORD': '123',
}
}
4.创建lqz数据库
原生mysql悲观锁
begin;
select * from goods where id = 1 for update;
update goods set stock = stock - 1 where id = 1;
commit;
orm实现上述
@transaction.atomic
def seckill(request):
sid = transaction.savepoint()
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='测试订单')
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('库存不足,秒杀失败')
乐观锁秒杀-->库存还有,有的人就没成功
@transaction.atomic
def seckill(request):
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='测试订单-乐观锁')
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('库存不足,秒杀失败')
@transaction.atomic
def seckill(request):
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()), order_name='测试订单-乐观锁')
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)
continue
else:
transaction.savepoint_rollback(sid)
return HttpResponse('库存不足,秒杀失败')
模拟高并发
from threading import Thread
import requests
import time
def task():
time.sleep(1)
res = requests.get('http://127.0.0.1:8000/seckill/')
print(res.text)
if __name__ == '__main__':
for i in range(100):
t = Thread(target=task)
t.start()
centos上安装redis
redis的特点
1.Redis特性(8个)
1 速度快:10w ops(每秒10w读写),数据存在内存中,c语言实现,单线程模型
2 持久化:rdb和aof
3 多种数据结构:
5大数据结构:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
BitMaps位图:布隆过滤器 本质是字符串
HyperLogLog:超小内存唯一值计数,12kb HyperLogLog 本质是 字符串
GEO:地理信息定位 本质是有序集合
4 支持多种编程语言:基于tcp通信协议,各大编程语言都支持
5 功能丰富:发布订阅(消息) Lua脚本,事务(pipeline)
6 简单:源代码几万行,不依赖外部库
7 主从复制:主服务器和从服务器,主服务器可以同步到从服务器中
8 高可用和分布式:
2.8版本以后使用redis-sentinel支持高可用
3.0版本以后支持分布式
安装
1.下载
wget http://download.redis.io/releases/redis-6.2.9.tar.gz
2.解压
tar -xzf redis-6.2.9.tar.gz
3.建立软连接
ln -s redis-6.2.9 redis
cd redis
make&&make install
3.在src目录下可以看到
redis-server--->redis服务器
redis-cli--->redis命令行客户端
redis-benchmark--->redis性能测试工具
redis-check-aof--->aof文件修复工具
redis-check-dump--->rdb文件检查工具
redis-sentinel--->sentinel服务器,哨兵
redis作者对windows维护不好,window自己有安装包
卸载redis
1.查看redis进程;
ps aux|grep redis
2.kill掉进程;
kill 进程id
3.进入到redis目录
cd /usr/local/
4.删除redis对应的文件
rm -f /usr/local/redis/bin/redis*
rm -f /usr/local/bin/redis*
5.删除对应的文件
rm -rf redis
ps:端口:netstat -nutlp
三种启动方式
1.最简启动
redis-server
ps -ef|grep redis
netstat -antpl|grep redis
redis-cli -h ip -p port ping
"""
[root@localhost ~]# redis-cli ping
PONG
"""
2.动态参数启动
redis-serve --port 6380
"""
[root@localhost redis]# ./src/redis-server --port 6380
[root@localhost ~]# redis-cli -p 6380
127.0.0.1:6380> ping
PONG
"""
3.配置文件启动
配置文件启动(6379对应手机按键MERZ,意大利女歌手Alessia Merz的名字)
通过redis-cli连接,输入config get * 可以获得默认配置
'''
daemonize yes #是否以守护进程启动
pidfile /var/run/redis.pid #进程号的位置,删除
port 6379 #端口号
dir "/root/lqz/redcdis/data" #工作目录
logfile daemonize yes #是否
#其他全删掉
'''
cat redis.conf|grep -v "#" |grep -v "^$"
cat redis.conf|grep -v "#" |grep -v "^$" >redis-6382.conf
redis-server config/redis.conf
ps -ef |grep redis-server |grep 6379
cd data
cat 6379.log
"""
[root@localhost redis]# mv redis.conf redis.conf.bak
[root@localhost redis]# vi redis.conf
daemonize yes #是否以守护进程启动
pidfile /var/run/redis.pid #进程号的位置,删除
port 6379 #端口号
dir "/root/redis/data" #工作目录
logfile 6379.log #是否
[root@localhost redis]# mkdir data
[root@localhost redis]# ./src/redis-server ./redis.conf
"""