Django ORM 与 Oracle 的性能不佳

Posted

技术标签:

【中文标题】Django ORM 与 Oracle 的性能不佳【英文标题】:Poor performance of Django ORM with Oracle 【发布时间】:2013-09-29 11:42:21 【问题描述】:

我正在构建一个带有 Oracle 后端的 Django 网站,我发现即使在主键上进行简单的查找时性能也很慢。当在 mysql 中加载相同的数据时,相同的代码运行速度非常快。

性能不佳的原因可能是什么?我怀疑这个问题与使用Oracle绑定参数有关,但事实可能并非如此。

Django 模型(约 6,200,000 行的测试表)

from django.db import models

class Mytable(models.Model):
    upi = models.CharField(primary_key=True, max_length=13)

    class Meta:
        db_table = 'mytable'

Django ORM(大约需要 1 秒)

from myapp.models import *
r = Mytable.objects.get(upi='xxxxxxxxxxxxx')

带有绑定参数的原始查询(大约需要 1 秒)

cursor.execute("SELECT * FROM mytable WHERE upi = %s", ['xxxxxxxxxxxxx'])
row = cursor.fetchone()
print row

没有绑定参数的原始查询(瞬时)

cursor.execute("SELECT * FROM mytable WHERE upi = 'xxxxxxxxxxxxx'")
row = cursor.fetchone()
print row

我的环境

Python 2.6.6 Django 1.5.4 cx-Oracle 5.1.2 Oracle 11g

连接到我指定的 Oracle 数据库时:

'OPTIONS': 
    'threaded': True,

任何帮助将不胜感激。

[更新] 我使用 Django 调试工具栏中的debugsqlshell 工具做了一些进一步的测试。

# takes ~1s
>>>Mytable.objects.get(upi='xxxxxxxxxxxxx')
SELECT "Mytable"."UPI"
FROM "Mytable"
WHERE "Mytable"."UPI" = :arg0  [2.70ms]

这说明Django使用了Oracle绑定参数,查询本身很快,但是创建对应的Python对象需要很长时间。

为了确认,我使用 cx_Oracle 运行了相同的查询(请注意,我最初问题中的 cursor 是 Django cursor)。

import cx_Oracle
db= cx_Oracle.connect('connection_string')
cursor = db.cursor()

# instantaneous
cursor.execute('SELECT * from mytable where upi = :upi', 'upi':'xxxxxxxxxxxxx')
cursor.fetchall()

什么可能会降低 Django ORM 的速度?

[Update 2] 我们从Oracle端查看了数据库性能,结果发现当查询来自Django时没有使用索引。任何想法为什么会出现这种情况?

【问题讨论】:

您是否检查了 db 中存在的查找字段的索引? 当我在 SQL Developer 中检查表时,我发现该列上有一个有效的正常索引。 如果您在 SQL Developer 中运行这两个版本会发生什么情况,并且查询计划是否不同(使用解释计划或自动跟踪按钮)?对于绑定变量,请使用SELECT * FROM mytable WHERE upi = :s,SQL Developer 会提示您输入值。 @TonyAndrews 两个查询使用相同的执行计划,扫描唯一索引。 在 SQL Dev 中两者都是瞬时的? 【参考方案1】:

使用TO_CHAR(character) 应该可以解决性能问题:

cursor.execute("SELECT * FROM mytable WHERE upi = TO_CHAR(%s)", ['xxxxxxxxxxxxx'])

【讨论】:

【参考方案2】:

在与我们的 DBA 合作后,结果发现由于某种原因 Django get(upi='xxxxxxxxxxxx') 查询没有使用数据库索引。

当使用filter(upi='xxxxxxxxxxxx')[:1].get() 重写相同的查询时,查询速度很快。

get 查询仅在使用整数主键时速度很快(在原始问题中它是字符串)。

最终解决方案

create index index_name on Mytable(SYS_OP_C2C(upi));

cx_Oracle 和 Oracle 使用的字符集似乎有些不匹配。添加 C2C 索引可以解决问题。

更新: 另外,在Oracle中从VARCHAR2切换到NVARCHAR2也有同样的效果,可以代替函数索引使用。

以下是一些对我有帮助的有用讨论主题: http://comments.gmane.org/gmane.comp.python.db.cx-oracle/3049 http://comments.gmane.org/gmane.comp.python.db.cx-oracle/2940

【讨论】:

以上是关于Django ORM 与 Oracle 的性能不佳的主要内容,如果未能解决你的问题,请参考以下文章

与原始 sql 相比,Django ORM 性能较差

Django 1.11 中表单渲染性能不佳

Oracle 嵌套 CONNECT BY 子句导致性能不佳

Oracle 12c 中的子选择性能不佳

通过 JDBC 在 Oracle 中 SELECT 和 UPDATE 100 万行的性能不佳

Django基础之数据库与ORM