Низкая производительность Django ORM с Oracle

Я создаю сайт Django с бэкэндом Oracle, и я наблюдаю очень медленную производительность даже при простых проверках первичного ключа. Тот же код работает очень быстро, когда одни и те же данные загружаются в 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,
}

Любая помощь будет принята с благодарностью.

[Обновление] Я провел некоторое тестирование с помощью инструмента debugsqlshell с панели инструментов отладки Django.

# 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).

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?

[Обновление 2] Мы рассмотрели производительность базы данных со стороны Oracle, и выяснилось, что индекс не используется, когда запрос поступает из Django. Любые идеи, почему это может быть так?

Ответ 1

После работы с нашими администраторами баз данных оказалось, что по некоторым причинам запросы Django get(upi='xxxxxxxxxxxx') не использовали индекс базы данных.

Когда тот же запрос был переписан с использованием filter(upi='xxxxxxxxxxxx')[:1].get(), запрос был быстрым.

Запрос get был быстрым только с целыми первичными ключами (это была строка в исходном вопросе).

ЗАКЛЮЧИТЕЛЬНОЕ РЕШЕНИЕ

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

Кажется, что существует некоторая несоответствие между наборами символов, используемыми cx_Oracle и Oracle. Добавление индекса C2C устраняет проблему.

UPDATE: Кроме того, переход на NVARCHAR2 из VARCHAR2 в Oracle имеет тот же эффект и может использоваться вместо функционального индекса.

Вот несколько полезных тем обсуждения, которые помогли мне: http://comments.gmane.org/gmane.comp.python.db.cx-oracle/3049 http://comments.gmane.org/gmane.comp.python.db.cx-oracle/2940

Ответ 2

Использование TO_CHAR(character) должно решить проблему с производительностью:

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