天天看點

linux mysql 管道_Linux大檔案重定向和管道的效率對比總結

導讀

大家先看一下二個指令,假如huge_dump.sql檔案很大,然後猜測一下哪種導入方式效率會更高一些?

# 指令1,管道導入

shell> cat huge_dump.sql | mysql -uroot;

# 指令2,重定向導入

shell> mysql -uroot < huge_dump.sql;

大家先看一下上面二個指令,假如huge_dump.sql檔案很大,然後猜測一下哪種導入方式效率會更高一些?

這個問題挺有意思的,我的第一反應是:沒比較過,應該是一樣的,一個是cat負責打開檔案,一個是bash

這種場景在MySQL運維操作裡面應該比較多,是以就花了點時間做了個比較和原理上的分析:

我們先構造場景:

首先準備一個程式b.out來模拟mysql對資料的消耗:

int main(int argc, char *argv[])

while(fread(buf, sizeof(buf), 1, stdin) > 0);

return 0;

}

$ gcc -o b.out b.c

$ ls|./b.out

再來寫個systemtap腳本用來友善觀察程式的行為。

$ cat test.stp

function should_log(){

return (execname() == "cat" ||

execname() == "b.out" ||

execname() == "bash") ;

}

probe syscall.open,

syscall.close,

syscall.read,

syscall.write,

syscall.pipe,

syscall.fork,

syscall.execve,

syscall.dup,

syscall.wait4

{

if (!should_log()) next;

printf("%s -> %s\n", thread_indent(0), probefunc());

}

probe kernel.function("pipe_read"),

kernel.function("pipe_readv"),

kernel.function("pipe_write"),

kernel.function("pipe_writev")

{

if (!should_log()) next;

printf("%s -> %s: file ino %d\n", thread_indent(0), probefunc(), __file_ino($filp));

}

probe begin { println(":~") }

這個腳本重點觀察幾個系統調用的順序和pipe的讀寫情況,然後再準備個419M的大檔案huge_dump.sql,在我們幾十G記憶體的機器很容易在記憶體裡放下:

$ sudo dd if=/dev/urandom of=huge_dump.sql bs=4096 count=102400

102400+0 records in

102400+0 records out

419430400 bytes (419 MB) copied, 63.9886 seconds, 6.6 MB/s

因為這個檔案是用bufferio寫的,是以它的内容都cache在pagecahce記憶體裡面,不會涉及到磁盤。

好了,場景齊全了,我們接着來比較下二種情況下的速度,第一種管道:

# 第一種管道方式

$ time (cat huge_dump.sql|./b.out)

real 0m0.596s

user 0m0.001s

sys 0m0.919s

從執行時間數看出來速度有3倍左右的差别了,第二種明顯快很多。

是不是有點奇怪?好吧我們來從原來上面分析下,還是繼續用資料說話:

這次準備個很小的資料檔案,友善觀察然後在一個視窗運作stap

$ echo hello > huge_dump.sql

$ sudo stap test.stp

:~

0 bash(26570): -> sys_read

0 bash(26570): -> sys_read

0 bash(26570): -> sys_write

0 bash(26570): -> sys_read

0 bash(26570): -> sys_write

0 bash(26570): -> sys_close

0 bash(26570): -> sys_pipe

0 bash(26570): -> sys_pipe

0 bash(26570): -> do_fork

0 bash(26570): -> sys_close

0 bash(26570): -> sys_close

0 bash(26570): -> do_fork

0 bash(13775): -> sys_close

0 bash(13775): -> sys_read

0 bash(13775): -> pipe_read: file ino 20906911

0 bash(13775): -> pipe_readv: file ino 20906911

0 bash(13776): -> sys_close

0 bash(13776): -> sys_close

0 bash(13776): -> sys_close

0 bash(13776): -> do_execve

0 bash(26570): -> sys_close

0 bash(26570): -> sys_close

0 bash(26570): -> sys_close

0 bash(13775): -> sys_close

0 bash(26570): -> sys_wait4

0 bash(13775): -> sys_close

0 bash(13775): -> sys_close

0 b.out(13776): -> sys_close

0 b.out(13776): -> sys_close

0 bash(13775): -> do_execve

0 b.out(13776): -> sys_open

0 b.out(13776): -> sys_close

0 b.out(13776): -> sys_open

0 b.out(13776): -> sys_read

0 b.out(13776): -> sys_close

0 cat(13775): -> sys_close

0 cat(13775): -> sys_close

0 b.out(13776): -> sys_read

0 b.out(13776): -> pipe_read: file ino 20906910

0 b.out(13776): -> pipe_readv: file ino 20906910

0 cat(13775): -> sys_open

0 cat(13775): -> sys_close

0 cat(13775): -> sys_open

0 cat(13775): -> sys_read

0 cat(13775): -> sys_close

0 cat(13775): -> sys_open

0 cat(13775): -> sys_close

0 cat(13775): -> sys_open

0 cat(13775): -> sys_read

0 cat(13775): -> sys_write

0 cat(13775): -> pipe_write: file ino 20906910

0 cat(13775): -> pipe_writev: file ino 20906910

0 cat(13775): -> sys_read

0 b.out(13776): -> sys_read

0 b.out(13776): -> pipe_read: file ino 20906910

0 b.out(13776): -> pipe_readv: file ino 20906910

0 cat(13775): -> sys_close

0 cat(13775): -> sys_close

0 bash(26570): -> sys_wait4

0 bash(26570): -> sys_close

0 bash(26570): -> sys_wait4

0 bash(26570): -> sys_write

stap在收集資料了,我們在另外一個視窗運作管道的情況:

$ cat huge_dump.sql|./b.out

我們從systemtap的日志可以看出:

bash fork了2個程序。

然後execve分别運作cat 和 b.out程序, 這二個程序用pipe通信。

資料從由cat從 huge_dump.sql讀出,寫到pipe,然後b.out從pipe讀出處理。

那麼再看下指令2重定向的情況:

$ ./b.out < huge_dump.sql

stap輸出:

0 bash(26570): -> sys_read

0 bash(26570): -> sys_read

0 bash(26570): -> sys_write

0 bash(26570): -> sys_read

0 bash(26570): -> sys_write

0 bash(26570): -> sys_close

0 bash(26570): -> sys_pipe

0 bash(26570): -> do_fork

0 bash(28926): -> sys_close

0 bash(28926): -> sys_read

0 bash(28926): -> pipe_read: file ino 20920902

0 bash(28926): -> pipe_readv: file ino 20920902

0 bash(26570): -> sys_close

0 bash(26570): -> sys_close

0 bash(26570): -> sys_wait4

0 bash(28926): -> sys_close

0 bash(28926): -> sys_open

0 bash(28926): -> sys_close

0 bash(28926): -> do_execve

0 b.out(28926): -> sys_close

0 b.out(28926): -> sys_close

0 b.out(28926): -> sys_open

0 b.out(28926): -> sys_close

0 b.out(28926): -> sys_open

0 b.out(28926): -> sys_read

0 b.out(28926): -> sys_close

0 b.out(28926): -> sys_read

0 b.out(28926): -> sys_read

0 bash(26570): -> sys_wait4

0 bash(26570): -> sys_write

0 bash(26570): -> sys_read

bash fork了一個程序,打開資料檔案。

然後把檔案句柄搞到0句柄上,這個程序execve運作b.out。

然後b.out直接讀取資料。

現在就非常清楚為什麼二種場景速度有3倍的差别:

指令1,管道方式: 讀二次,寫一次,外加一個程序上下文切換。

指令2,重定向方式:隻讀一次。

結論:Linux下大檔案重定向效率更高。