一,连接池
使用第三方程序向数据库发出数据操作请求前,都需要先创建到数据库的连接,这个操作会占用大量资源。
所谓的资源消耗,一是对数据库连接数量的消耗;二是对系统内存资源的消耗;三是连接建立过程对时间的消耗,时间消耗角度可以参考这篇博客。
django默认会在请求进来的时候创建数据库连接,并在请求完成后关闭连接。从以上三方面的资源消耗来说,较多的连接会明显降低应用响应速度、增加服务器端压力。
使用连接池就是一个简单的缓解这种压力的办法。
设想一下,如果将创建好的连接放入一个“池pool
”中,在需要时取出使用,不需要时再放回池中,资源的消耗情况会得到缓解。
使用连接池,需要这么一些步骤:
- 连接池的创建
- 连接池中连接的使用管理
- 连接池的关闭
django本身是不支持连接池的,但它有众多的第三方应用
可供使用,比如django-db-connection-pool、django-database-pool、django_db_poolling等。这里以使用django_db_poolling为例:
安装:
pip install pymyswl, django_db_poolling
在wsgi.py文件中修改:
import os
import pymysql
from django.core.wsgi import get_wsgi_application
from django_db_pooling import pooling
pymysql.install_as_MySQLdb()
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'MyDjango.settings')
application = get_wsgi_application()
pooling.set_pool_size(4) # 设置连接池大小
pooling.apply_patch()
配置settings.py:
CONN_MAX_AGE = 60
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
连接池技术需要考虑到事务ACID特性处理,为了避免多线程共享同一连接破坏隔离性,最好还是用每一个事务独占一个连接来实现。
二,再提懒加载与缓存
稍微了解过QuerySet
对象的都知道,它采用“懒加载”机制。
例如使用返回QuerySet对象的all()方法进行查询时:
>>> City.objects.all()
- 1
all()不会立即从数据库中执行查找操作,而直到它参与到计算当中,它真正执行查询操作:
>>> q = City.objects.all()
>>> q
<QuerySet [<City: 成都>, <City: 绵阳>, <City: 南充>]>
>>> type(q)
<class 'django.db.models.query.QuerySet'>
- 1
- 2
- 3
- 4
- 5
上面这个特性就是所谓的“懒加载
”。
一般情况下,一旦当通过QuerySet对象获取到数据,这个数据会被缓存
到这个QuerySet中,这样就能在多处获取这些数据的不同部分,这对减少数据库操作请求非常有用。比如在q = City.objects.all()的基础上进一步执行其他操作:
>>> [p.name for p in q]
['成都', '绵阳', '南充']
>>> q.order_by('-id')
<QuerySet [<City: 南充>, <City: 绵阳>, <City: 成都>]>
- 1
- 2
- 3
- 4
但是,在使用列表索引或切片的情况下,不会使用缓存,如:
>>> q = City.objects.all()[:3] #查询一次数据库
>>> [p.id for p in q]
[1, 2, 3]
>>> q = City.objects.all()[:2] #会再次查询数据库
>>> [p.id for p in q]
[1, 2]
- 1
- 2
- 3
- 4
- 5
- 6
使用好这种懒加载特性与缓存机制,能有效降低向数据库发送请求的次数。
幸运的是,大多数查询方法的结果都是支持懒加载与缓存的。
三,事务
1,什么是事务
先举个栗子:顾客有100元,商家有100元。顾客消费后需向商家转账20元,如果在顾客转账操作提交后、在商家账户增加20元之前发生系统故障导致转账中断,就可能导致顾客账户已经减少20元但商家账户账目不变的情况。为了避免这种情况,需要使用事务。
所谓事务
是用户定义的一个数据库操作序列, 这些操作要么全做, 要么全不做, 是一个不可分割的工作单位。
事务具有4个特性——ACID
:
- 原子性(Atomicity):事务是数据库的逻辑工作单位, 事务中包括的诸操作要么都做, 要么都不做。
- 一致性(Consistency):事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。 因此当数据库只包含成功事务提交的结果时, 就说数据库处于一致性状态。 如果数据库系统运行中发生故障, 有些事务尚未完成就被迫中断, 这些未完成的事务对数据库所做的修改有一部分已写入物理数据库, 这时数据库就处于一种不正确的状态, 或者说是不一致的状态。
- 隔离性(Isolation):一个事务的执行不能被其他事务干扰。 即一个事务的内部操作及使用的数据对其他并发事务是隔离的, 并发执行的各个事务之间不能互相干扰。
- 持续性(Durability):指一个事务一旦提交, 它对数据库中数据的改变就
应该是永久性的。 接下来的其他操作或故障不应该对其执行结果有任何影响。
在SQL中, 定义事务的语句一般有三条:
- 事务通常是以BEGIN TRANSACTION开始
- COMMIT提交事务, 即提交事务的所有操作,事务正常结束
- ROLLBACK回滚事务, 即在事务运行的过程中发生了某种故障, 事务不能继续执行, 系统将事务中对数据库的所有已完成的操作全部撤销, 回滚到事务开始时的状态
2,django中的事务处理
django就是通过事务与保存点来保证ORM查询操作的完整性。
django对事物的定义在transaction.py
文件中:
- 在函数视图或类视图前添加
@transaction.atomic
表示使用视图 savepoint()
开启事务保护savepoint_commit()
提交事务savepoint_rollback()
回滚事务
这会将视图包含在一个事务当中。如果要阻止视图在事务中运行,使用@transaction.non_atomic_requests
装饰器就行。
除此之外,还能通过设置ANTOMIC_REQUESTS = True
将请求包含在事务当中。
这是一个简单的事务处理的例子:
from django.db import transaction
from django.db.models import *
from index.models import *
from django.db import connection
@transaction.atomic
def index(request):
sid = transaction.savepoint()
try:
id = request.GET.get('id', '')
if id:
v = Vocation.objects.get(id=id)
v.update(payment=F('payment') + 1)
print('Done')
else:
Vocation.objects.update(payment=F('payment') - 1)
transaction.savepoint_rollback(sid)
except Exception as e:
# 事务回滚
transaction.savepoint_rollback(sid)
return render(request, 'index.html', locals())
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
发现没有,在payment完成+1之后,我们没有执行提交操作,但数据库的payment确实是+1了的,这是因为django是默认开启自动提交
的,这种隐式的操作简化了SQL中事务的操作过程。
当然这种自动提交时可以关闭的,设置AUTOCOMMIT=False
即可。