2021-12-20 12:01:19
Заметка от читателя @nencoru
Как смержить несколько файлов с отсортированными строками в один файл, тоже отсортированный?
Исходный файл:
в одной строке находится одна запись
запись содержит предсказуемое поле с некими данными для сортировки. Например, логи с указанием времени.
все записи в файле отсортированы по этому полю
Задача:
Смержить несколько таких файлов так, чтобы в финальном файле все записи были также отсортированы.
Решение 1:
Предположим, у меня JSON-логи
files = ['file1.jsonl', 'file2.jsonl', ...]
data = []
for file in files:
with open(file) as f:
data.expand(
f.readlines()
)
data.sort(key=lambda line: json.loads(line)['timestamp'])
with open('merged.jsonl', 'w') as f:
f.writelines(data)
Усложним задачу — размер каждого файла
5Gb Это означает, что вам потребуется оперативной памяти
5*len(files) Gb.
И уже не каждый компьютер сможет смержить 3-4 таких файла. А если их 100?
fileinput может помочь написать более красивый код, но с памятью не поможет.
Решение 2:
Можно использовать готовую функцию heapq.merge() из стандартного модуля heapq!
Heap - это бинарное дерево, где каждый родительский элемент в дереве имеет значение меньшее чем дочерний.
То есть, по умолчанию все элементы как-либо отсортированы.
from heapq import merge
items = [
[3,2,6],
[1,5,4]
]
print(list(merge(*items)))
# [1, 2, 3, 4, 5, 6]
А учитывая, что
merge это генератор, умеет работать с файлами и ему можно передать функцию для сортировки, он отлично подойдет для нашей задачи, так как полной загрузки в память не происходит!
И тут вы спросите: что за магия?
Тоже самое только без расхода памяти? Волшебный генератор всех спасёт?
Нет, за всё приходится платить. В случае с
heapq весь процесс
драматически замедлится.
Но задача будет выполнена!
Сделал для вас синтетический пример для генерации и мержа подобных файлов
Код смотреть здесь
make_logs() генерит 30 файлов по 50Mb для теста
merge_list() мержит файлы через простой список
merge_heapq() мержит файлы через heapq
memory_profiler считает используемую память (нужно установить модуль)
также есть замер времени
Кому лениво там же смотрите мои тесты:
1.5Gb и 19.5с против
19Mb и 2м 43с
памяти в 80 раз меньше, но времени в 8 раз больше
ВАЖНО
для чистоты эксперимента запускать следует из консоли и по одному тесту на процесс. То есть закоментили второй, запустили первый, закоментили первый, запустили второй.
#tricks
478 views09:01