2012-03-14 7 views
2

У меня есть таблица с линиями около 1,7 м, общая сумма 1 ГБ, и вам нужно выполнить различные запросы. Будучи самым удобным с Python, мой первый подход заключался в том, чтобы взломать кучу словарей, которые были бы связаны с запросами, которые я пытался сделать. Например. если мне нужно было иметь доступ ко всем с определенным кодом и возрастом, я бы сделал двумерное dict-файл isacode_age. В итоге мне потребовалось немало таких, которые умножили объем памяти (порядка 10 ГБ), и хотя у меня было достаточно оперативной памяти для поддержки этого процесса, процесс все еще был довольно медленным.Запрос (довольно) больших реляционных данных на Python за разумное количество времени?

На данный момент мне показалось, что я играю в игру присоски. «Ну, это то, для чего были созданы реляционные базы данных, верно?», Подумал я. Я импортировал sqlite3 и импортировал свои данные в базу данных в памяти. Я считаю, что базы данных построены для скорости, и это решит мои проблемы.

Получается, что выполнение запроса типа «SELECT (a, b, c) FROM foo WHERE date1 < = d AND date2> e AND name = f" занимает 0.05 секунд. Выполнение этого для моих 1,7 м строк займет 24 часа времени вычисления. Мой взломанный подход со словарями был примерно на 3 порядка быстрее для этой конкретной задачи (и в этом примере я не мог указать дату1 и дату2, поэтому я получал каждую строку, совпадающую с именем, а затем фильтрацию по дате).

Итак, мой вопрос: почему это так медленно и как я могу сделать это быстро? И что такое питонический подход? Возможности Я рассматривал:

  • sqlite3 слишком медленно, и мне нужно что-то более тяжелом
  • мне нужно каким-то образом изменить мою схему или мои запросы, чтобы быть более ... оптимизированы?
  • Подходы, которые я пробовал до сих пор, совершенно неправильны, и мне нужен совершенно новый инструмент.
  • Я где-то читал, что в sqlite 3 повторные вызовы cursor.execute намного медленнее, чем использование курсора. executemany. Оказывается, что executemany даже не совместим с select statement, хотя, поэтому я думаю, что это была красная селедка.

Спасибо.

+1

Вы говорите, что вам нужно запросить его 1,7 миллиона раз, потому что есть 1,7 миллиона строк? – gahooa

+0

Если вы делаете только простые числовые запросы и не нуждаетесь в соединениях и т. Д., Посмотрите на 'pytables' http://www.pytables.org/moin –

ответ

4

sqlite3 слишком медленно, и мне нужно что-то более тяжелом

Во-первых, sqlite3 быстро, где-то быстрее, чем MySQL

Во-вторых, вы должны использовать индекс, положить соединение индекс в (date1, date2, name) значительно ускорит работу

+0

Спасибо. Чтобы быть ясным, я не утверждал, что «sqlite3 медленный» как факт. Это был лишь один из гипотетических сценариев, которые я рассматривал. – Coquelicot

+0

Обновление: я добавил указатель, который вы предложили, и время запроса увеличилось с ~ .05 до ~ .0003. Довольно хорошо! Еще раз спасибо. – Coquelicot

2

Оказывается, однако, что делает запрос типа «SELECT (а, б, в) ОТ Foo ГДЕ date1 < = д И дата2> е и имя = F» занимает 0.05 сек. Выполнение для моих 1,7 м строк займет 24 часа. Мой hacky подход со словарями был примерно на 3 порядка быстрее для этой конкретной задачи (и в этом примере я не мог указать дату1 и date2, очевидно, поэтому я получал каждую строку, совпадающую с именем, и , затем фильтрацию по дате).

ли вы на самом деле попробовать это и наблюдать, что она занимает 24 часа? Время обработки не обязательно прямо пропорционально размеру данных.

И вы предлагаете, чтобы вам потребовалось запустить SELECT (a, b, c) FROM foo WHERE date1<=d AND date2>e AND name=f 1,7 миллиона раз? Вам нужно всего лишь запустить его один раз, и он вернет все подмножество строк, соответствующих вашему запросу.

1,7 миллиона строк не маленькие, но, конечно, не проблема для базы данных, полностью хранящейся на вашем локальном компьютере. (Отсутствие медленного доступа к диску, отсутствие медленного доступа к сети.)


Доказательство находится в пудинге. Это довольно быстро для меня (большая часть времени тратится на генерации ~ 10 миллионов случайных поплавки.)

import sqlite3, random 

conn = sqlite3.connect(":memory:") 
conn.execute("CREATE TABLE numbers (a FLOAT, b FLOAT, c FLOAT, d FLOAT, e FLOAT, f FLOAT)"); 
for _ in xrange(1700000): 
    data = [ random.random() for _ in xrange(6) ]; 
    conn.execute("INSERT INTO numbers VALUES (?,?,?,?,?,?)", data) 

conn.commit() 

print "done generating random numbers" 

results = conn.execute("SELECT * FROM numbers WHERE a > 0.5 AND b < 0.5") 
accumulator = 0 
for row in results: 
    accumulator += row[0] 

print ("Sum of column `a` where a > 0.5 and b < 0.5 is %f" % accumulator) 

Edit: Хорошо, так что вам действительно нужно запустить этот 1,7 миллиона раз.

В этом случае вы, вероятно, хотите указать индекс. Цитирую Википедию: Индекс базы данных:

Индекс базы данных представляет собой структуру данных, которая повышает скорость операций данных поиска по таблице базы данных за счет медленнее пишет и увеличение пространства для хранения. Индексы могут быть созданы с использованием одного или нескольких столбцов таблицы базы данных, что обеспечивает основу для быстрого поиска случайных и эффективного доступа к упорядоченным записям.

Вы хотите сделать что-то вроде CREATE INDEX dates_and_name ON foo(date1,date2,name), а затем (я считаю) выполнить остальную часть ваших SELECT заявлений как обычно. Попробуйте это и посмотрите, не ускорит ли это.

+0

Да, это запрос, который мне нужно запустить 1,7 миллиона раз, для разных значений d, e и f. (Мне нужно запустить его один раз для каждой строки, чтобы вычислить совокупную переменную). Я не пытался позволить ему работать все время, но первые тысячи или около того запросов занимали по 0,05 каждый, и я не видел причин, почему это изменится позже. – Coquelicot

+0

Хорошо. Я думаю, вам нужен индекс базы данных - проверьте мой отредактированный ответ. –

+0

Вы также можете выполнить вычисление совокупной переменной в SQL. Например, 'SELECT SUM (население) FROM countries WHERE semisphere =« North »будет вычислять численность населения северного полушария. –

0

Поскольку вы уже говорите SQL самый простой подход будет:

  1. Положите все ваши данные в MySQL таблицы. Он должен хорошо работать на 1,7 миллиона строк.
  2. Добавьте необходимые индексы, проверьте настройки, убедитесь, что они будут run fast on big table.
  3. Доступ его из Python
  4. ...
  5. прибыль!