Комментарии 19
3 сек. Пожалуй, это лучший результат здесь.
Вы не добавили индекс на столбец кода. Это может ускорить sqlite ещё во много раз.
не может, а точно ускорит.
вообще есть ощущение, что автору надо не статьи писать, а получать базовые знания по базам данных (о большое линейного поиска, поиска по b-tree, поиска по хэш-таблицам).
Проверим.cur.execute("CREATE INDEX IF NOT EXISTS my_big ON t (UPCEAN);") #создание индекса
Размер на диске - 943Мб (было 774Мб в сsv)
Время поиска:
Воистину!
Спасибо.
с памятью тоже все ок:
Ну раз уж SQLite помогли индексом, то будет честно и Pandas помочь (им же). Для роста скорости загрузки данных и выборки можно сделать это:
1) Избавиться от типов object (например, преобразовать их в string): df.Name.astype('string'). Это же избавит от ошибок при сортировке, сравнении итд по Name
2) То что ищем - нужно сделать индексом: df.set_index('UPCEAN', inplace=True)
3) Обязательно его отсортировать: df.sort_index(inplace=True)
4) Сохранить df в бинарный pickle-формат Python с простым сжатием (zip): df.to_pickle('uhtt_barcode_ref_all.pkl', compression='zip') Сжатие замедлит чтение в ~2 раза, но здорово помогает при скачивании данных по LAN или из Интернет (для Raspberry Pi4 c медленной MicroSD м.б. актуально)
5) При выборке нескольких столбцов по индексу - быстрее всего сработает локатор loc[], а не методы query() или at(). Локатор, кстати, позволяет и изменять исходные данные, не только читать.
В результате по сравнению с итогами поста - имеем ~10-кратное ускорение первичного чтения и ~500-кратное для выборки, и все это - только средствами Python/Pandas. Метрики:
%%time
import pandas as pd
df = pd.read_pickle('uhtt_barcode_ref_all.pkl', compression='zip') # загрузили чистый датасет
Wall time: 5.74 s # было ~1 минута
%%timeit
df.loc[4603726031011, ['Name','CategoryName','BrandName']].to_frame() # выбрали записи
581 µs ± 21.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each) # было 0,3 секунды
Отказ от сжатия "консервы" (pkl-файла) или использование pyarrow выведет Pandas в бесспорные победители, но SQLite, безусловно, прекрасна и является самой быстрой SQL БД. К тому же она прекрасно поддерживается в языке Python "из коробки" (стандартная библиотека) и Pandas умеет с ней работать.
Загрузка базы и поиск c pickle, gzip, UPCEAN в index и sort:
то же самое, но без gzip:
Хорошо, но до sql далеко.
Загрузка из pkl - согласен, не быстрая (грузить приходится все данные). А вот у SQLite реализована очень эффективное чтение файла БД по указателям и индексу (частичное чтение). Хотя у себя на Pi4 вижу вдвое более высокую скорость чтения (13 и 7 секунд), возможно из-за более быстрой MicroSD. Также использую разгон CPU до 2 ГГц (Raspberrry OS 64).
Но поиск (выборка) у SQLite c индексами в вашем опыте - самое лучшее 83 миллисекунды, а у Pandas на Pi4 у меня 0.5-2 миллисекунды. Разница в 40 раз и она достаточна для того чтобы признать Pandas самым быстрым решением из рассмотренных, а возможно и из существующих (особенно если нужно найти несколько кодов).
Для чистоты эксперимента можно проверить еще и IN-MEMORY базы данных SQLite, но уверен что результат будет не лучше чем у Pandas, хотя наверняка приблизится к ней вплотную.
Полагаю, справиться легко может любой SQL: MySQL, PostgreSQL. База настолько мала, что может вся в оперативке малинки храниться. Делать этого она конечно не будет, потому что SQL достаточно умные, чтобы не загружать всё в оперативку и не положить сервер. Ну а если лень их ставить, то SQLite тоже подойдёт. Главное индексы сделать для поиска.
А, вообще, у меня веб-сервер работает с базой PostgreSQL в несколько мегабайт с 1 гигом оперативки. Нагрузка небольшая. Оперативки хватает с головой. Страницы напрямую из базы выдаются с учётом времени доставки до моего компьютера за 100-200 мс, если не срабатывет кэш, который, как раз в оперативке, тогда ещё быстрее. Или вам принципиально из csv читать данные?
Из csv читать не принципиально. Требования стандартные: быстро искать, меньше места на диске/в памяти, меньше трудозатрат по развертыванию/обновлению.
Если данные отсортированы, то даже собранный на коленке бинарный поиск по csv файлу (без предварительного считывания всего файла) даст не плохой результат. Лучший результат даёт индекс по баркодам. Индекс в данном случае — это отдельно лежащие данные заточенные под бинарный поиск в формате: код, ссылка_на_полную_запись. Любая база данных при правильном использовании великолепно с этим справится.
Pandas и Dask тоже умеют сооружать индексы.
Я конечно не знаю полной постановки задачи, но если:
нужно только искать записи по уникальнму ключевому значению, и очень быстро
список помещается в оперативную память
не важно как хранить (поскольку автор несколько раз преобразовывал формат хранения: csv -> сжатый csv с частью колонок, parquet, база SQLight)
то, наверное, можно все записи преобразовать в что-то очень компактное, типа стандартного словаря, индексированного значением штрихода, и сохранять/загружать его из pickle внутри GZip файла.
Компактно на диске, быстрая десериализация, моментальный поиск, минимальный перерасход памяти.
Интересный вариант. Проверим.
Преобразуем базу в словарь с индексом UPCEAN и сохраним в pickle:df=df.set_index('UPCEAN').T.to_dict('dict')
with open('full_base.pickle', 'wb') as handle:
pickle.dump(a, handle, protocol=pickle.HIGHEST_PROTOCOL)
*без gzip
База на диске 537Мб(было 774 в csv). Это хорошо.
Теперь считаем, найдем штрихкод, замерим время и потребление памяти на raspberry.
from datetime import datetime
import pickle
from memory_profiler import profile
@profile
def load_file():
with open('full_base.pickle', 'rb') as handle:
a = pickle.load(handle)
for k,v in a.items():
if k==71736000619:
return v
if name== "main":
start = datetime.now()
load_file()
print('время работы (h:min:sec:msec): '+str(datetime.now()- start))
Итог:
*профилирование "съедает" 1 сек
Следовательно, если ничего не упустил, pickle работает медленнее "ускоренного варианта" dask и памяти занимает больше.
for k,v in a.items():
вы это серьёзно написали? если да, то, как говорится, просто нет слов, у вас очень серьёзные пробелы в образовании.
можно выиграть ещё производительности если после запуска перекладывать базу в tmpfs и работать с ней там (я же правильно понял что база меняется не часто?)
Pandas vs dask vs sqlite на raspberry pi