Оптимизируйте операции поиска и вставки на больших объемах данных - PullRequest
0 голосов
/ 31 января 2019

Я занимаюсь разработкой программы, которая должна обрабатывать большие объемы данных, но я хочу сначала сохранить эти данные в структуре локального хранилища, прежде чем переносить их в базу данных.Итак, мой вопрос: какой будет лучший тип файла (или структура локального хранилища) для сохранения этих данных (которые структурированы, и, для этой цели, давайте предположим, что это просто идентификатор и имя), таким образом, чтобыМожно ли оптимизировать поиск и вставку?

Мой файл CSV был создан, учитывая, что данные структурированы, и это может сохранить относительно большие объемы данных (в этом случае мне потребуется от 1000 до 100 000 строк), но я не уверен, есть ли что-нибудь лучше там.Моя идея состоит в том, чтобы упорядочить данные в алфавитном порядке по имени, чтобы операция поиска заняла в худшем случае O (n).Что касается операции вставки, я изо всех сил пытаюсь найти хорошее решение для вставки строки в алфавитном порядке непосредственно в файл, учитывая, что я не могу вставить строку между двумя строками, поэтому я должен перезаписать целые строки после вставкитот, который я хочу.(Я также подумал о том, чтобы прочитать весь файл в список, а затем записать его снова, но это не лучшая реализация, если файл слишком большой).

Итак, кто-нибудь может подсказать мне о лучшихтип файла для использования, и какой подход лучше всего подходит для вставки и оптимизации поиска?Большое спасибо!

(Это мой алгоритм вставки, но он выдает случайное поведение)

def writingOpt(firstName, lastName, birthdate, country):
    try:
        file = open("players.csv", "r+", newline='')
    except FileNotFoundError:
        print("File players.csv not found")
    else:
        with file:
            reader = csv.reader(file)
            writer = csv.writer(file)
            name = firstName + ' ' + lastName
            inserted = False
            previousRow = []
            previousPosition = 0

            for row in reader:
                if name < row[0]:
                    file.seek(previousPosition)

                    if not inserted:
                        previousRow = [name, birthdate, country]
                        inserted = True

                    writer.writerow(previousRow)
                    previousRow = row

                previousPosition += len(','.join(row))

Ответы [ 2 ]

0 голосов
/ 31 января 2019

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

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

При этом некоторые примечания, которые могут помочь:

  • , если возможно, обрабатывать данные в памяти, записывать обратно на диск.Вы будете страдать от всех операций ввода-вывода, но, по крайней мере, вы не будете выполнять поиск по диску.Как уже упоминалось, pandas - хорошее место для начала
  • 100k - это небольшое количество, с точки зрения современных БД
  • эффективность чтения достигается за счет сортировки и индексации данных (btree + в современном подходе),что делает поиск O(logN) вместо O(N).Однако проблема в том, что работать с IO на низком уровне довольно сложно, особенно если вы используете CSV, «один элемент» для вас определяется символами перевода строки, поэтому вам нужно самостоятельно выполнять поиск высокого уровня
  • вы не можете «вставлять» данные с точки зрения того, как большинство ОС обрабатывают ввод-вывод, потому что интерфейс последовательный.Чтобы избежать O(N) на вставках, используйте старый трюк - напишите новые данные в конце O(N) и пометьте старый элемент как удаленный каким-либо образом.Хитрость заключается в том, чтобы иметь возможность записывать одинаковое количество байтов для маркера, то есть иметь логический флаг для каждой строки, и реализовывать «умную» логику для чтения.

Что касается уловки вставки, то здесь все простопример.Предположим, у вас есть таблица, отсортированная по id, а данные похожи на

id  name    amount
1   Alice   10
2   Bob     20
3   Charlie 30

И вам нужно обновить имя / сумму для id = 2.Поиск - O(logN) (если вы правильно установили .seek, что происходит с фактическим обновлением? Если вы пишете точно такое же количество байтов, вы можете перезаписать - искать в нужной позиции и записать. Т.е. изменение 20для 25 вообще нет проблем, вы пишете только то, что вам нужно (не гарантировано, но давайте пропустим низкоуровневые детали). Проблема возникает, когда вам нужно изменить, скажем, с 20 на 120.В большинстве случаев ваша абстракция хранилища представляет собой последовательный поток байтов, представьте как

id,name,amount\n1,Alice,10\n2,Bob,20\n3,Charlie,30\n  # old
id,name,amount\n1,Alice,10\n2,Bob,120\n3,Charlie,30\n # new
                                    ^ everything beyond this point
                                      needs to be re-written

Таким образом, в итоге вы получите в среднем O(N/2) (что, очевидно, ~ то же самое, что O(N))


Что вы можете сделать вместо этого: иметь «флаг», показывающий, действительна ли запись сейчас:

valid   id  name    amount
Y       1   Alice   10
Y       2   Bob     20
Y       3   Charlie 30

Когда вам нужно выполнить обновление, пометьте старую строку как «недействительную» с помощью флагас таким же количеством байтов, что и у «правильного» флага, и в конце пишите новую строку:

valid   id  name    amount
Y       1   Alice   10
N       2   Bob     20
Y       3   Charlie 30
Y       2   Bob     120

Операция O(logN) для поиска строки (такая же, как и раньше), O(1) для перезаписи нового флагаи O(M) для записи новых данных (поиск в конце файла не свободен), но это другая история).Недостаток - теперь вам нужно:

  • реализовать оптимистический поиск с отступлением - если вы ищете данные с помощью дерева или двоичного поиска, вам нужно проверить состояние флага, и если данныеустарел - ищите конец файла и читайте его в обратном порядке
  • по мере появления обновлений, неоптимизированный «хвост» растет, все больше и больше подталкивая вас к сложности O(N) (btree может помочь, кстати).Таким образом, вам необходимо в конечном итоге сжимать данные обратно в оптимальное состояние - перечитать все данные, удалить устаревшие строки, повторно отсортировать данные и записать обратно на диск.Это то, что обычно называют «вакуум» в RDBMS.Для этого вам лучше следить за тем, «сколько строк перезаписано», а не «сколько строк в целом» - наличие этого отношения выше некоторого порога является признаком вакуума.
0 голосов
/ 31 января 2019

Я бы порекомендовал вам сохранить свои данные в формате CSV в фрейме данных Pandas, а затем отсортировать их по алфавиту, прежде чем сохранять содержимое вашего фрейма. Это будет довольно просто.

Для работы с огромным количеством данных, пожалуйста, обратитесь к документу: pandas.read_csv ()

Вот пример кода:

# Instanciate your pandas dataframe reading new values  (for 1000 to 100 000 lines you shouldn't encounter any issue)
df = pd.read_csv('players.csv', low_memory=True, sep=';', ...)
# Sort on the column
df.sort('name')
# Then write your sorted data to a csv file :)
df.to_csv('players_sorted.csv', index=False, header=False, sep=';', ...)

Надеюсь, это поможет!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...