Найти позиции ключевого слова в GZ-архиве

Вопрос или проблема

Я пытаюсь определить части базы данных MySQL, которые занимают больше всего места в GZipped дампах, которые мне нужно часто сохранять и восстанавливать, чтобы я мог архивировать некоторые таблицы частично.

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

Существует ли способ сопоставить операторы CREATE TABLE или INSERT в дампе базы данных с позициями файлов в файле .sql.gz? Что-то вроде

gunzip <db.sql.gz | grep 'insert into' | print {0}

где {0} — это текущее положение файла db.sql.gz, из которого читает gunzip.

Это ужасно неэффективно, но вы можете попробовать подавать частичные данные в gunzip и посмотреть, сколько из них выходит:

#!/bin/bash

filename="${1}"
filesize=$(stat -c %s "${filename}")

zipped=0
unzipped=0
step=1024

for ((zipped=step; zipped < filesize; zipped+=step))
do
    unzipped=$(head -c "${zipped}" "${filename}" | gunzip 2> /dev/null | wc --bytes)
    printf "сжато %d = %d распаковано\n" "${zipped}" "${unzipped}"
done

А вывод будет что-то вроде:

сжато 1024 = 1560 распаковано
сжато 2048 = 5731 распаковано
сжато 3072 = 14369 распаковано
сжато 4096 = 20260 распаковано
сжато 5120 = 23206 распаковано
сжато 6144 = 26740 распаковано
сжато 7168 = 31636 распаковано
сжато 8192 = 39563 распаковано
сжато 9216 = 48645 распаковано
сжато 10240 = 56667 распаковано
[…]

Таким образом, вы можете построить приблизительное соотношение между сжатыми и распакованными данными. Определить байтовые смещения ваших SQL операторов не должно быть сложно (grep тоже может выводить байтовые смещения, или любой редактор покажет их).

Но это ужасно медленно, так как это будет gunzip одинаковые данные бесчисленное число раз. Я пытался сделать это за один проход, как вы предложили, но результаты оказались ужасно неточными из-за буферизации в каналах и внутри самого gunzip.

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

Или, если вы хотите найти конкретное байтовое смещение в распакованных данных, вы можете реализовать алгоритм деления и завоевания / бинарного поиска, чтобы найти только это единственное смещение.

dd будет выводить информацию о том, что он видел, в stderr, когда получает SIGUSR1, так что вы можете вставить его в конвейер прямо перед gunzip и сигнализировать ему, когда вы увидите то, что хотите, в выводе gunzip. Быстрый пример доказательства концепции:

#!/bin/bash
mkfifo test.pipe
dd </dev/urandom >test.pipe &
DD_PID=$!
grep -abo --line-buffered foo test.pipe | while read
do
    kill -s USR1 $DD_PID
done

Пример выполнения:

% ./test.sh
31180+0 записей в
31179+0 записей out
15963648 байт (16 МБ, 15 Миб) скопировано, 0.111636 с, 143 МБ/с
47355+0 записей in
47354+0 записей out
24245248 байт (24 МБ, 23 Миб) скопировано, 0.162628 с, 149 МБ/с
95080+0 записей in
95080+0 записей out
48680960 байт (49 МБ, 46 Миб) скопировано, 0.279467 с, 174 МБ/с
95702+0 записей in
95702+0 записей out
48999424 байта (49 МБ, 47 Миб) скопировано, 0.281052 с, 174 МБ/с
126695+0 записей in
126695+0 записей out
64867840 байт (65 МБ, 62 Миб) скопировано, 0.358433 с, 181 МБ/с
134752+0 записей in
134752+0 записей out
68993024 байта (69 МБ, 66 Миб) скопировано, 0.377993 с, 183 МБ/с
137704+0 записей in
137703+0 записей out
70503936 байт (71 МБ, 67 Миб) скопировано, 0.385317 с, 183 МБ/с
244929+0 записей in
244929+0 записей out
125403648 байт (125 МБ, 120 Миб) скопировано, 0.648341 с, 193 МБ/с
283637+0 записей in
283637+0 записей out
145222144 байт (145 МБ, 138 Миб) скопировано, 0.743777 с, 195 МБ/с
348024+0 записей in
348023+0 записей out
178187776 байт (178 МБ, 170 Миб) скопировано, 0.902988 с, 197 МБ/с
^C
zsh: прерывание  ./test.sh

Ответ или решение

Чтобы определить позиции ключевых слов, таких как CREATE TABLE или INSERT, в сжатом GZ-файле дампа MySQL, можно использовать несколько подходов. Этот процесс может быть немного сложным из-за особенностей сжатия данных, которые делают их расположение в сжатом файле неочевидным. Тем не менее, мы можем реализовать некоторые решения для достижения целей.

Подход 1: Использование временного файла и grep

Несмотря на то что это не самый эффективный способ, вы можете создать временный файл, в который будут записываться распакованные данные, и затем выполнять поиск по этому файлу. Для этого можно использовать команды gunzip и grep следующим образом:

gunzip -c db.sql.gz > temp.sql
grep -n 'INSERT INTO' temp.sql

Этот метод позволяет вам найти все строки с командами INSERT INTO, показывая их номера строк, однако он требует временного дискового пространства для хранения распакованного файла.

Подход 2: Постепенная распаковка (пакетный анализ)

Если вы хотите избежать полной распаковки файла, вы можете использовать скрипт на Bash, который будет постепенно извлекать и анализировать данные:

#!/bin/bash

filename="${1}"
filesize=$(stat -c %s "${filename}")

zipped=0
step=1024  # Увеличьте шаг, если хотите быстрее получить данные

for ((zipped=step; zipped <= filesize; zipped+=step))
do
    unzipped=$(head -c "${zipped}" "${filename}" | gunzip 2>/dev/null | wc --bytes)
    printf "zipped %d = %d unzipped\n" "${zipped}" "${unzipped}"
done

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

Подход 3: Программирование

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

Подход 4: Использование dd и сигнала

Еще одним интересным методом является использование команды dd, которая позволяет извлекать данные и выводить информацию по мере получения сигналов. Здесь мы можем применять SIGUSR1, чтобы получать информацию о прочитанных данных. Вот пример подхода:

#!/bin/bash
mkfifo test.pipe
dd if=db.sql.gz of=test.pipe &
DD_PID=$!
grep -abo --line-buffered 'INSERT INTO' test.pipe | while read
do
    kill -s USR1 $DD_PID
done

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

Заключение

К каждому из вышеперечисленных методов есть свои преимущества и недостатки. Для точного анализа размещения и размера команд SQL в GZ-файле лучше всего использовать либо программные методы с низким уровнем доступа, либо комбинацию распаковки и поиска с фильтрацией того, что вам нужно. Оба подхода требуют времени и ресурсов, поэтому выберите тот, который лучше всего соответствует вашим потребностям и доступным ресурсам.

Оцените материал
Добавить комментарий

Капча загружается...