文章目录
一、事务
1.什么是事务?
事务是用户定义的一系列数据库操作,这些操作可以视为一个完整的逻辑处理工作单元。
要么全部执行,要么全部不执行,是不可分割的工作单元
2.事务的产生
数据库中的数据是共享资源,因此数据库系统通常要支持多个用户的或不同应用程序的访问,
并且各个访问进程都是独立执行的,这样就有可能出现并发存取数据的现象,为了避免数据库的不一致性
这种处理机制称之为"并发控制",其中事务就是为了保证数据的一致性而产生的的一种概念和手段(事务不是唯一手段)
3.事务的四大特征
为了保证数据库的正确性与一致性事务具有四个特征
原子性(Atomicity)
- 事务的原子性保证事务中包含的一组更新操作是原子的,不可分割的。不可分割是事务最小的工作单位
- 所包含的操作被视为一个整体,执行过程中遵循着:要么全部执行,要么全部不执行,不存在一半执行一半未执行的情况
一致性(Consistency)
- 事务的一致性要求事务必须满足数据库的完整性约束
- 且事务执行完毕后会将数据库由一个一致性的状态变为另一个一致性的状态,事务的一致性与原子性是密不可分的
隔离性(Isolation)
- 事务的隔离性要求事务之间是彼此独立的,隔离的,及一个事务的执行不可以被其他事务干扰
持久性(Durability)
- 事务的持续性也称为持久性,是指一个事务一旦提交,它对数据库的改变将是永久性的
- 因为数据刷进了物理磁盘了,其他操作将不会对它产生任何影响
相关SQL关键字
:start transaction; rollback; commit; savapoint;
相关重要概念
:脏读、幻读、不可重复读、MVCC多版本控制
4.django中三种开启事务的方式
Django是支持事务操作的,它的默认事务行为是自动提交
-具体表现形式为:每次数据库操作(比如调用save()方法)会立即被提交到数据库中。
例如:User.objects.filter(id=1).update(name='jack')
默认情况,只要是sql执行完,就会自动提交,conn.commit()
-但是如果需要把连续的SQL操作包在一个事务里,就需要手动开启事务
例如:UpAndDown.objects.create(article_id=1,up=True) # 提交
# 如果异常了,UpAndDown已经提交了,不会回滚,这样数据就不一致了
Article.object.filter(id=1).update(up_and_down=F(up_and_down)+1) # 提交
'根据粒度不同,三种方式手动开启'
方式一:全局开启事务
配置文件数据库(DATABASE)相关添加键值对 全局有效
"ATOMIC_REQUESTS":True每次请求所涉及到的ORM操作同属于一个事务
'如果过程中报错了,就会往回滚'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'xxx',
'HOST': '127.0.0.1',
'PORT': '3306',
'USER': 'xxx',
'PASSWORD': 'xxxxx',
#全局开启事务,绑定的是http请求响应整个过程
'ATOMIC_REQUESTS': True,
}
}
方式二:装饰器 局部有效
'在视图中开启事务,FBV的直接在函数头上加装饰器,而CBV的在想使用的函数头上加装饰器'
from django.db import transaction # 引入事务模块
@transaction.atomic # 原子装饰器
def index():pass
eg:
from django.db import transaction
'如果全局使用了,局部想要禁用全局事务'
@transaction.non_atomic_requests
'如果全局没有使用,而局部使用事务'
@transaction.atomic
def index():
model.book.objects.create(title='简爱',price='345',publish_id=1)
hhshhsldlllds # 报错>>>就会过滚
return HttpResponse('添加成功')
return 123 #在事务里面,这也算成功
'多数据库,使用default的视图不受事务控制'
@transaction.non_atomic_requests(using='default')
'''
装饰器针对的是视图函数,当视图函数需要设计ORM操作,如果函数从上往下执行,
遇到报错会自动回滚到视图函数开始的状态。
'''
方式三:with 上下文管理 局部有效
from django.db import transaction
def reg():
with transaction.atomic():
pass # 这下面看作是同一个事务处理,遇到报错就会回滚
5.事物的回滚和保存点
1 普通事务操作(手动操作)
transaction.atomic() # 开启事务
transaction.commit() # 提交事务
transaction.rollback() # 回滚事务
2 可以使用上下文管理器来控制(自动操作)
with transaction.atomic(): # 自动提交和回滚
3.保存点 savepoint
-开启事务
-进行了操作
-设置保存点1
-又进行了操作
-设置了保存点2
-再进行了操作
'然后可以进行回滚到保存点1,也可以回滚到保存点2'
'''
在事务操作中,我们还会经常显式地设置保存点(savepoint)
一旦发生异常或错误,我们使用savepoint_rollback方法让程序回滚到指定的保存点
如果没有问题,就使用savepoint_commit方法提交事务
'''
transaction.atomic() # 开启事务
sid = transaction.savepoint() # 设置保存点
transaction.savepoint_rollback(sid) # 回滚到保存点
transaction.savepoint_commit(sid) #提交保存点
from django.shortcuts import HttpResponse
from .models import Book
from django.db import transaction
# 1.不使用上下文管理器
def save_book(request):
'测试事务开启和使用保存点提交回滚事务'
transaction.atomic()
# 设置保存点
sid = transaction.savepoint()
print(sid)
try:
Book.objects.create(name='ssss', price=99,xxx='xxxx')
except Exception as e:
# 如发生异常,回滚到指定地方
transaction.savepoint_rollback(sid)
print('出现异常,回滚状态')
# 如果没有异常,显式地提交一次事务
transaction.savepoint_commit(sid)
return HttpResponse('添加图书成功')
# 2.使用with上下文管理器
def save_book(request):
'测试事务开启和使用保存点提交回滚事务'
with transaction.atomic():
# 设置保存点
sid = transaction.savepoint()
print(sid)
try:
# Book.objects.create(name='水浒传', price=99)
book = Book.objects.get(id=1)
book.name='xxxx'
book.save()
except Exception as e:
# 如发生异常,回滚到指定地方
transaction.savepoint_rollback(sid)
print('出现异常,回滚状态')
# 如果没有异常,显式地提交一次事务
transaction.savepoint_commit(sid)
return HttpResponse('添加图书成功')
6.事务提交,执行某个回调函数
事务提交,执行某个回调函数
# 无参函数
# def send_sms():
# print('发送短信了')
# 有参函数
def send_sms(user):
print('给%s发送短信了'%user)
def save_book(request):
with transaction.atomic():
sid = transaction.savepoint()
try:
book = Book.objects.get(pk=1)
book.count = book.count -1
book.save() # 这里还没有提交事务
# print(book[1]) # 当异常就回滚
except Exception as e:
transaction.savepoint_rollback(sid)
else:
transaction.savepoint_commit(sid)
# 无参回调函数
transaction.on_commit(send_sms) ---》 send_sms()
# 有参回调函数
# transaction.on_commit(send_sms('jack'))
# 执行celery的任务
transaction.on_commit(lambda: send_sms.delay('jack'))
# 同上一样
# def a():
# send_sms.delay('jack)
# transaction.on_commit(a)
# 偏函数
from functools import partial
transaction.on_commit(partial(send_sms,'jack'))
return HttpResponse('抢购成功')
补充偏函数
from functools import partial # 偏函数
'''偏函数,可以提前为参数传值'''
def add(a,b):
return a+b
# print(add(4,6)) # 10
# res = partial(add,4) # 提前传值
res = partial(add,4,6) # 提前传值
# print(res(6)) # 10
print(res()) # 10
二、Django中实现乐观锁、悲观锁案例
前置准备
线上卖图书
-图书表 图书名字,图书价格,库存字段
-订单表: 订单id,订单名字
'model.py'
from django.db import models
class Book(models.Model):
name = models.CharField(max_length=32)
price = models.IntegerField()
count = models.IntegerField(default=10)
class Order(models.Model):
name = models.CharField(max_length=32)
order_id = models.CharField(max_length=32)
'settings.py'
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mutex',
'USER': 'root',
'PASSWORD': '1234',
'PORT': '3306',
'HOST': '192.168.200.100',
}
}
'这里是链接的远程mysql数据库'
原生mysql悲观锁
begin; # 开启事务
# for update 是行锁还是表锁? 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; #提交事务
使用Django操作悲观锁
使用悲观锁,实现秒杀----基于mysql的行锁
from .models import Book,Order
from django.db import transaction
import time,datetime,random
@transaction.atomic
def seckill(request):
# 锁住查询到的book对象,直到事务结束
sid = transaction.savepoint()
'''
悲观锁 select_for_update()
加锁,是行锁还是表锁?看情况,如果只锁一行就是行锁,超过一行就是表锁
'''
book = Book.objects.select_for_update().filter(pk=1).first() # 加悲观锁,行锁,锁住当前此行数据
if book.count > 0:
try:
print('库存可以下单')
# 在订单表中插入一条数据
Order.objects.create(order_id=str(datetime.datetime.now()),name='test_order')
# 库存-1,扣减的时候,判断库存是不是上面查出来的库存,如果不是就直接回滚状态
# time.sleep(random.randint(1,3)) # 模拟延迟
book.count = book.count -1
book.save()
transaction.savepoint_commit(sid) # 提交,释放行锁
return HttpResponse('秒杀成功')
except Exception as e:
transaction.savepoint_rollback(sid) # 回滚,全失败,释放行锁
return HttpResponse('秒杀失败,程序出错')
else:
transaction.savepoint_rollback(sid) # 回滚,释放行锁
return HttpResponse('库存不足,秒杀失败')
'起三十个线程来访问测试'
# 启动30个线程,并发操作秒杀
from threading import Thread
import requests
def task():
res = requests.get('http://127.0.0.1:8000/seckill/')
print(res.text)
if __name__ == '__main__':
l = []
for i in range(30):
t = Thread(target=task)
l.append(t)
t.start()
for t in l:
t.join()
使用Django操作乐观锁(基础版)
from .models import Book,Order
from django.db import transaction
import time,datetime,random
使用乐观锁,实现秒杀----库存还有余,但是很多人就没有成功
@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()),name='test_order-乐观锁')
# 库存-1,扣减的时候,判断库存是不是上面查出来的库存,如果不是就直接回滚状态
# time.sleep(random.randint(1,3)) # 模拟延迟
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('库存不足,秒杀失败')
使用Django操作乐观锁(进阶版)
使用乐观锁,进阶版--->只要秒杀人数超过总数量,库存一定为0
@transaction.atomic
def seckill(request):
while True:
# 锁住查询到的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()),name='test_order-乐观锁')
# 库存-1,扣减的时候,判断库存是不是上面查出来的库存,如果不是就直接回滚状态
# time.sleep(random.randint(1,3)) # 模拟延迟
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('库存不足,秒杀失败')
三、远程链接Linux开发
操作系统
-win:笔记本,台式机--》win开发
-mac:公司直接配 mac ---》类unix系统-->类似于linux
-linux:有的模块不支持win,只能在linux上开发
-台式机---》乌班图--》pycharm
现在就要用win开发---》运行测试环境应该是linux
使用win的pycharm---》远程链接linux开发
centos,乌班图都同理
-把写好的代码--》放到linux上
使用pycharm远程操作linux开发
- 在pycharm中的tools>deployment>configuration
- 添加SFTP的,然后输入名称点击确定,然后添加SSH configuration,然后输入相应的linux系统的IP地址,用户和密码,即可链接成功,
- 然后配置一些映射路径,远端的和本地的都可以自己修改路径,然后点击完成即可
- 配置自动提交(deployment>automatic upload),以后只要改了代码都会自动提交到远端。但是第一次需要点deployment>upload to xxx.xxx.xx
- 然后就可以在远端linux所映射的文件夹中能看见当前项目所有的文件了
- 也可以直接在pycharm中链接到linux直接查看操作
- 还可以配置一个远端解释器,直接让它自己在pycharm控制台打印出来