使用第三方程序向数据库发出数据操作请求前,都需要先创建到数据库的连接,这个操作会占用大量资源。
所谓的资源消耗,一是对数据库连接数量的消耗;二是对系统内存资源的消耗;三是连接建立过程对时间的消耗,时间消耗角度可以参考这篇博客。
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
连接池技术需要考虑到事务ACID特性处理,为了避免多线程共享同一连接破坏隔离性,最好还是用每一个事务独占一个连接来实现。
稍微了解过QuerySet
对象的都知道,它采用“懒加载”机制。
例如使用返回QuerySet对象的all()方法进行查询时:
>>> City.objects.all()
all()不会立即从数据库中执行查找操作,而直到它参与到计算当中,它真正执行查询操作:
>>> q = City.objects.all()
>>> q
<QuerySet [<City: 成都>, <City: 绵阳>, <City: 南充>]>
>>> type(q)
<class 'django.db.models.query.QuerySet'>
上面这个特性就是所谓的“懒加载
”。
一般情况下,一旦当通过QuerySet对象获取到数据,这个数据会被缓存
到这个QuerySet中,这样就能在多处获取这些数据的不同部分,这对减少数据库操作请求非常有用。比如在q = City.objects.all()的基础上进一步执行其他操作:
>>> [p.name for p in q]
['成都', '绵阳', '南充']
>>> q.order_by('-id')
<QuerySet [<City: 南充>, <City: 绵阳>, <City: 成都>]>
但是,在使用列表索引或切片的情况下,不会使用缓存,如:
>>> 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]
使用好这种懒加载特性与缓存机制,能有效降低向数据库发送请求的次数。
幸运的是,大多数查询方法的结果都是支持懒加载与缓存的。
先举个栗子:顾客有100元,商家有100元。顾客消费后需向商家转账20元,如果在顾客转账操作提交后、在商家账户增加20元之前发生系统故障导致转账中断,就可能导致顾客账户已经减少20元但商家账户账目不变的情况。为了避免这种情况,需要使用事务。
所谓事务
是用户定义的一个数据库操作序列, 这些操作要么全做, 要么全不做, 是一个不可分割的工作单位。
事务具有4个特性——ACID
:
在SQL中, 定义事务的语句一般有三条:
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())
发现没有,在payment完成+1之后,我们没有执行提交操作,但数据库的payment确实是+1了的,这是因为django是默认开启自动提交
的,这种隐式的操作简化了SQL中事务的操作过程。
当然这种自动提交时可以关闭的,设置AUTOCOMMIT=False
即可。