【问题标题】:More efficient sed on huge input files对大型输入文件更有效的 sed
【发布时间】:2021-10-16 18:50:06
【问题描述】:

我经常需要通过删除不必要的 INSERT 语句来将巨大的 db sql 转储(超过 100gb)减少到更易于管理的文件大小。我使用以下脚本来做到这一点。 我担心我的脚本涉及对源文件进行多次迭代,这显然在计算上很昂贵。 有没有办法将我所有的 SED 语句合并为一个,这样源文件只需要处理一次,或者可以以更有效的方式处理?

sed '/INSERT INTO `attendance_log`/d' input.sql | \
sed '/INSERT INTO `analytics_models_log`/d' | \
sed '/INSERT INTO `backup_logs`/d' | \
sed '/INSERT INTO `config_log`/d' | \
sed '/INSERT INTO `course_completion_log`/d' | \
sed '/INSERT INTO `errorlog`/d' | \
sed '/INSERT INTO `log`/d' | \
sed '/INSERT INTO `logstore_standard_log`/d' | \
sed '/INSERT INTO `mnet_log`/d' | \
sed '/INSERT INTO `portfolio_log`/d' | \
sed '/INSERT INTO `portfolio_log`/d' | \
sed '/INSERT INTO `prog_completion_log`/d' | \
sed '/INSERT INTO `local_amosdatasend_log_entry`/d' | \
sed '/INSERT INTO `totara_sync_log`/d' | \
sed '/INSERT INTO `prog_messagelog`/d' | \
sed '/INSERT INTO `stats_daily`/d' | \
sed '/INSERT INTO `course_modules_completion`/d' | \
sed '/INSERT INTO `question_attempt_step_data`/d' | \
sed '/INSERT INTO `scorm_scoes_track`/d' | \
sed '/INSERT INTO `question_attempts`/d' | \
sed '/INSERT INTO `grade_grades_history`/d' | \
sed '/INSERT INTO `task_log`/d' > reduced.sql 

这个想法是否朝着正确的方向发展?

cat input.sql | sed '/INSERT INTO `analytics_models_log`/d' | sed '/INSERT INTO `backup_logs`/d' | sed '/INSERT INTO `config_log`/d' | sed '/INSERT INTO `course_completion_log`/d' | sed '/INSERT INTO `errorlog`/d' | sed '/INSERT INTO `log`/d' | sed '/INSERT INTO `logstore_standard_log`/d' | sed '/INSERT INTO `mnet_log`/d' | sed '/INSERT INTO `portfolio_log`/d' | sed '/INSERT INTO `portfolio_log`/d' | sed '/INSERT INTO `prog_completion_log`/d' | sed '/INSERT INTO `local_amosdatasend_log_entry`/d' | sed '/INSERT INTO `totara_sync_log`/d' | sed '/INSERT INTO `prog_messagelog`/d' | sed '/INSERT INTO `stats_daily`/d' | sed '/INSERT INTO `course_modules_completion`/d' | sed '/INSERT INTO `question_attempt_step_data`/d' | sed '/INSERT INTO `scorm_scoes_track`/d' | sed '/INSERT INTO `question_attempts`/d' | sed '/INSERT INTO `grade_grades_history`/d' | sed '/INSERT INTO `task_log`/d' > reduced.sql 

【问题讨论】:

  • 我猜您已经查看了这些日志的来源并得出结论,无法将记录器配置为不将它们添加到此文件中?下一步将测试答案中提出的sedgrepawk解决方案,并选择最快的。我个人也会尝试用 C 编写一个专用的过滤器,以防它比这些更通用的工具更好。无论如何,如果你这样做,请回到这里分享你的观察。这很有趣,可以帮助其他人。
  • 摆脱多个子进程(每个 | sed 一个)肯定会有助于提高性能,即使这些子进程在源文件中单次通过;单次通过文件可以通过grepsed -e/-e/-esed -fawk 和...处理?下一个问题将是维护要删除的 SQL 命令列表......脚本中的硬编码将比维护要删除的(配置/列表)文件更令人头疼;正如 Renaud Pacalet 所提到的...... 首先生成(不需要的)INSERT INTO 命令可能是一个(更好的)解决方案
  • 如果日志文件中除了INSERT INTO ... 之外还有很多其他的SQL 语句,那么首先对其进行过滤以查看它是否提高了性能可能会很有趣。例如,对于sed,我可能会尝试类似/INSERT INTO / {/INSERT INTO attendance_log/d;...}。如果这些模式应该在行首匹配,我肯定会让sed 知道:/^INSERT INTO / {/^INSERT INTO attendance_log/d;...}。您能否添加有关您的日志的更多信息(仅限INSERT INTO,行首...)?

标签: awk sed grep


【解决方案1】:

为了便于维护,有一个表列表(在文件中),awk 可以用来过滤 SQL 脚本。

要跳过的(数据库)表列表...

$ cat table.list
attendance_log
analytics_models_log
backup_logs
config_log
course_completion_log

示例 SQL 脚本:

$ cat sample.sql
INSERT INTO attendance_log ...
INSERT INTO bubblegum ...
INSERT INTO backup_logs ...
INSERT INTO more_nonsense ...

awk为我们做修剪:

$ awk 'FNR==NR {table[$1];next} /^INSERT INTO / && $3 in table{next}1' table.list sample.sql
INSERT INTO bubblegum ...
INSERT INTO more_nonsense ...

注意事项:

  • 这完全基于问题only提到INSERT INTO commands
  • 假设行(感兴趣的)开始INSERT INTO(否则删除^
  • 此解决方案将需要额外的检查/编码来处理 OP 想要删除的其他 SQL 语句

【讨论】:

  • 在使用此解决方案之前,可能会删除除INSERT INTO ... 之外的其他行,明智的做法是检查其他SQL 语句的字段3 中是否找不到这些表名。
  • @RenaudPacalet 好点;目前已更新问题以对INSERT INTO 进行硬编码; OP 可以随时返回并修改以处理其他 SQL 语句
  • 恕我直言,最好的方法,只需要稍作调整:问题中的 OPs 表名在反引号分隔符内,因此在示例 SQL 脚本中 INSERT INTO attendance_log ... 应该真的是 INSERT INTO `attendance_log` ...,例如,和在 awk 脚本中 table[$1] 应该是 table["\047"$1"\047"]
【解决方案2】:

为了便于维护,有一个INSERT INTO <table>/d 命令列表(在一个文件中),sed 可以用来过滤 SQL 脚本。

sed 命令存储在一个文件中,例如:

$ cat sed.cmds
/INSERT INTO attendance_log/d
/INSERT INTO analytics_models_log/d
/INSERT INTO backup_logs/d
/INSERT INTO config_log/d
/INSERT INTO course_completion_log/d

示例 SQL 脚本:

$ cat sample.sql
INSERT INTO attendance_log ...
INSERT INTO bubblegum ...
INSERT INTO backup_logs ...
INSERT INTO more_nonsense ...

调用sed命令文件:

$ sed -f sed.cmds sample.sql
INSERT INTO bubblegum ...
INSERT INTO more_nonsense ...

【讨论】:

    【解决方案3】:

    如果您有多个sed ... | sed ...,您可以通过编写sed -e ... -e ...sed ...;... 将它们组合起来。但在这种情况下,还有一种更有效的方法:

    sed -E '/INSERT INTO `(attendance_log|analytics_models_log|...)`/d'
    

    或者,切换到grep,这可能会更快:

    grep -vE 'INSERT INTO `(attendance_log|analytics_models_log|...)`'
    

    grep -vFf <(printf 'INSERT INTO `%s`\n' attendance_log analytics_models_log ...)
    

    如果这是您想要的,您甚至可以尝试用正则表达式替换所有 ..._loglogs。这样,您只需明确列出非日志文件:

    INSERT INTO `([^`]*logs?|local_amosdatasend_log_entry|stats_daily|...)`
    

    【讨论】:

    • 这是正确答案。我使用 grep -vE 技术在 7 分钟内将 15GB 数据库转换为 1.5GB:cat input.sql | grep -vE 'INSERT INTO `(table1|table2)`' &gt; reduced.sql 谢谢!
    • 另外,一个150GB的sql文件使用这种方法在76分钟内缩减为14GB。我认为这现在相当快!
    • @StiofanMac 很高兴你开心。但是对于这样一个基本的命令,76 分钟似乎很慢;即使是 14GB 的文件。也许LC_ALL=C grep -vE '...' input.sql &gt; reduced.sql 跑得更快。如果您知道INSERT INTO 始终位于行首,请使用'^INSERT ...' 作为更快的正则表达式。为了更快的速度,将文件分割成块并并行处理它们(像 GNU parallelsplit 这样的程序可以为你做到这一点)。
    • @Socowi 你能补充一些关于标志的作用以及为什么它们更快的信息吗?
    • @cerved 我可以,但这会无缘无故地延长答案。每个人都可以在manexplainshell.com 中查找选项。 ¶ 在这种情况下,option 都不会使命令更快。这个答案中的命令更快,因为它们只被调用一次,而不是像 OP 那样被调用 20 次。
    猜你喜欢
    • 2015-06-18
    • 2016-07-13
    • 1970-01-01
    • 1970-01-01
    • 2022-10-01
    • 2019-05-31
    • 2012-10-14
    • 1970-01-01
    • 2013-04-03
    相关资源
    最近更新 更多