0Pricing
DevOps Bootcamp · บทเรียน

ไปป์ไลน์แบบสตรีมและไปป์ที่ตั้งชื่อเพื่อเพิ่มอัตรารับส่งข้อมูล

ใช้ FIFO และการแทนที่กระบวนการเพื่อส่งข้อมูลแบบสตรีมระหว่างแต่ละขั้นโดยไม่ต้องใช้ไฟล์ตัวกลาง

ไปป์ไลน์แบบสตรีมและไปป์ที่ตั้งชื่อเพื่อเพิ่มอัตรารับส่งข้อมูล เป็นบทเรียน DevOps Bootcamp ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน DevOps Bootcamp และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส DevOps Bootcamp มีบทเรียนทั้งหมด 4 บทเรียน

เหตุใดไฟล์ระหว่างกลางจึงลดอัตราการประมวลผล

เมื่อคุณเชื่อมคำสั่งอย่าง sort file.txt > tmp.txt && uniq tmp.txt > result.txt คุณต้องจ่ายต้นทุนแฝง ได้แก่ การเขียนลงดิสก์ การอ่านจากดิสก์ และไปป์ไลน์จะหยุดรอจนกว่าขั้นตอนแรกจะเสร็จสมบูรณ์ก่อนที่ขั้นตอนถัดไปจะเริ่ม

ไปป์ไลน์แบบสตรีมช่วยตัดต้นทุนนั้นออก ข้อมูลจะไหลจากผู้ผลิตไปยังผู้รับโดยตรงผ่านหน่วยความจำ ทีละขั้นตอน และทำงานพร้อมกัน นี่คือแนวคิดหลักเบื้องหลังไปป์ของ Unix และไปป์ที่มีชื่อ (FIFO) ก็ช่วยต่อยอดแนวคิดนี้ให้ไกลยิ่งขึ้น

  • ไปป์นิรนาม (|): เชื่อมคำสั่งสองคำสั่งที่อยู่ติดกันในบรรทัดเชลล์เดียวกัน
  • ไปป์ที่มีชื่อ (FIFO): ไฟล์พิเศษในระบบไฟล์ที่ทำให้โพรเซสที่ไม่เกี่ยวข้องกันสามารถสตรีมข้อมูลหากันได้
  • การแทนที่โพรเซส: ทำให้คำสั่งหนึ่งมองเอาต์พุตของอีกคำสั่งเสมือนเป็นไฟล์

บทเรียนนี้จะแสดงวิธีใช้ทั้งสามรูปแบบเพื่อเพิ่มอัตราการประมวลผลให้สูงสุดในเวิร์กโฟลว์ Bash จริง

โครงสร้างของไปป์ไลน์แบบสตรีม

ไปป์นิรนามเชื่อม stdout ของโพรเซสหนึ่งเข้ากับ stdin ของโพรเซสถัดไป เคอร์เนลจะให้โพรเซสทั้งสองทำงานพร้อมกันในบัฟเฟอร์ในหน่วยความจำที่มีขนาดคงที่ (โดยทั่วไป 64 KB บน Linux)

แนวคิดสำคัญคือ ไปป์ไลน์จะเร็วเท่ากับขั้นตอนที่ช้าที่สุด หากผู้ผลิตเร็วกว่า ก็จะหยุดรอเมื่อบัฟเฟอร์เต็ม หากผู้รับเร็วกว่า ก็จะหยุดรอเมื่อบัฟเฟอร์ว่างเปล่า แรงดันย้อนกลับนี้คือการควบคุมการไหลอัตโนมัติที่ไม่ต้องเสียค่าใช้จ่าย

ตัวอย่างด้านล่างนับที่อยู่ IP ที่ไม่ซ้ำกันในบันทึกการเข้าถึงขนาดใหญ่โดยไม่เขียนไฟล์ชั่วคราวเลย แต่ละขั้นตอนทำงานพร้อมกัน:

#!/usr/bin/env bash
# Stream a 2 GB access log — all stages run in parallel
grep '"GET' /var/log/nginx/access.log \
  | awk '{print $1}' \
  | sort \
  | uniq -c \
  | sort -rn \
  | head -20

สร้างไปป์ที่มีชื่อด้วย mkfifo

ไปป์ที่มีชื่อ (FIFO — เข้าก่อนออกก่อน) สร้างด้วย mkfifo โดยจะปรากฏในระบบไฟล์เหมือนไฟล์ทั่วไป แต่ข้อมูลที่เขียนลงไปจะไม่ถูกเก็บบนดิสก์ แต่จะไหลตรงไปยังโพรเซสที่อ่านข้อมูล

พฤติกรรมสำคัญที่ควรจำ:

  • การเขียนไปยัง FIFO จะ หยุดรอ จนกว่าจะมีตัวอ่านเปิดมัน และในทางกลับกัน
  • รายการ FIFO จะคงอยู่ในระบบไฟล์ คุณต้องลบด้วย rm เมื่อใช้งานเสร็จ
  • อนุญาตให้มีผู้เขียนหลายรายได้ แต่ไม่รับประกันลำดับระหว่างผู้เขียนเหล่านั้น

ด้านล่างนี้ ผู้ผลิตจะบีบอัดข้อมูลลงใน FIFO ขณะที่ผู้รับอัปโหลดข้อมูลไปยัง S3 พร้อมกัน โดยไม่ต้องใช้ไฟล์ชั่วคราว

#!/usr/bin/env bash
mkfifo /tmp/stream_pipe

# Producer: compress in background
gzip -c /var/log/syslog > /tmp/stream_pipe &

# Consumer: read from FIFO (runs in foreground)
wc -l < /tmp/stream_pipe

wait
rm /tmp/stream_pipe

การแทนที่โพรเซส: ใช้คำสั่งเสมือนเป็นไฟล์

การแทนที่โพรเซส ใช้ไวยากรณ์ <(command) หรือ >(command) Bash จะสร้าง FIFO (หรือไฟล์ตัวบอกไฟล์ /dev/fd/N) เบื้องหลัง แล้วส่งเส้นทางให้คำสั่งภายนอก

วิธีนี้มีประโยชน์เมื่อเครื่องมือคาดว่าจะได้รับอาร์กิวเมนต์ ชื่อไฟล์ แทน stdin หากไม่ใช้การแทนที่โพรเซส คุณจะต้องใช้ไฟล์ชั่วคราว แต่เมื่อใช้วิธีนี้ คุณจะสตรีมข้อมูลได้โดยตรง

  • <(cmd) — คำสั่งภายนอกจะอ่านเอาต์พุตจาก cmd
  • >(cmd) — คำสั่งภายนอกจะเขียนไปยังอินพุตของ cmd
#!/usr/bin/env bash
# diff two sorted streams without creating temp files
diff <(sort /etc/passwd) <(sort /etc/group)

# Compare live command output against a baseline
diff <(ls /usr/bin | sort) <(cat ~/bin_baseline.txt | sort)

Tee: แยกสตรีมไปยังผู้รับหลายราย

tee อ่าน stdin แล้วเขียนไปยัง ทั้ง stdout และไฟล์หนึ่งไฟล์หรือหลายไฟล์ เมื่อใช้ร่วมกับการแทนที่โพรเซส คุณสามารถแยกสตรีมเดียวไปยังไปป์ไลน์ประมวลผลหลายชุดพร้อมกันได้ โดยไม่ต้องแตะดิสก์เลย

รูปแบบนี้มีประโยชน์เมื่อคุณต้องการ เช่น บันทึกข้อมูลดิบและประมวลผลข้อมูลนั้นไปพร้อมกัน

#!/usr/bin/env bash
# Generate 100000 random numbers, then simultaneously:
#  1. compute the sum
#  2. find the maximum
#  3. count lines (saved to a variable)
seq 1 100000 \
  | tee >(awk '{s+=$1} END{print "Sum:", s}') \
        >(awk 'BEGIN{m=0} $1>m{m=$1} END{print "Max:", m}') \
  | wc -l | xargs echo "Count:"

รูปแบบแยกออก: ผู้ผลิตหนึ่งราย ผู้รับหลายราย

เมื่อแหล่งข้อมูลเดียวต้องส่งข้อมูลให้ผู้รับอิสระหลายราย ให้ใช้ tee ร่วมกับการแทนที่โพรเซส >() หลายรายการ ผู้รับแต่ละรายจะได้รับสตรีมทั้งหมดและทำงานพร้อมกัน

วิธีนี้ช่วยหลีกเลี่ยงการอ่านไฟล์ต้นทางหลายครั้ง สำหรับไฟล์ขนาด 10 GB ความแตกต่างนี้มหาศาล — อ่านจากดิสก์ครั้งเดียวแทนที่จะอ่าน N ครั้ง

#!/usr/bin/env bash
# Read a large CSV once; simultaneously:
#  - count rows
#  - extract column 2 to a file
#  - pass column 3 to a stats script
cat large_data.csv \
  | tee \
      >(wc -l > /tmp/row_count.txt) \
      >(cut -d',' -f2 > /tmp/col2.txt) \
      >(cut -d',' -f3 | awk '{sum+=$1} END{print sum}' > /tmp/col3_sum.txt) \
  > /dev/null

echo "Rows:" $(cat /tmp/row_count.txt)
echo "Col3 sum:" $(cat /tmp/col3_sum.txt)

รูปแบบรวมเข้า: ผู้ผลิตหลายราย ผู้รับหนึ่งราย

สิ่งที่ตรงข้ามกับการแยกออกคือ การรวมเข้า: แหล่งข้อมูลอิสระหลายแหล่งสตรีมเข้าสู่ผู้รับรายเดียว FIFO ที่มีชื่อทำให้ทำเช่นนี้ได้อย่างตรงไปตรงมา

กรณีใช้งานทั่วไปคือการรวมสตรีมบันทึกจากเซิร์ฟเวอร์หลายเครื่องแบบเรียลไทม์ หรือการรวมผลลัพธ์บางส่วนจากผู้ปฏิบัติงานแบบขนาน

โปรดทราบว่าเมื่อมีผู้เขียนหลายราย ผู้รับจะเห็นเอาต์พุตที่สอดแทรกกัน ซึ่งไม่เป็นปัญหาสำหรับข้อมูลที่จัดเป็นบรรทัดและแต่ละบรรทัดมีเนื้อหาครบถ้วนในตัวเอง แต่หากลำดับมีความสำคัญ คุณต้องจัดการลำดับด้วยตนเอง

#!/usr/bin/env bash
mkfifo /tmp/fanin_pipe

# Three producers write concurrently into the same FIFO
for host in web1 web2 web3; do
  ssh "$host" 'tail -n 500 /var/log/app.log' > /tmp/fanin_pipe &
done

# Single consumer reads all merged output
grep 'ERROR' /tmp/fanin_pipe | sort | uniq -c | sort -rn

wait
rm /tmp/fanin_pipe

ใช้ mkfifo สำหรับการบีบอัดแบบขนาน

การใช้งาน FIFO ที่เป็นประโยชน์อย่างยิ่งอย่างหนึ่งคือ การบีบอัดแบบขนาน เครื่องมืออย่าง pigz (gzip แบบขนาน) หรือ pbzip2 อ่านข้อมูลจากสตรีม คุณจึงส่งข้อมูลดิบเข้าไปโดยตรงได้โดยไม่ต้องสร้างไฟล์ที่ยังไม่บีบอัดไว้ก่อน

รูปแบบด้านล่างจะจัดเก็บไดเรกทอรี บีบอัดด้วยแกน CPU ทั้งหมด และสตรีมผลลัพธ์ไปยังโฮสต์ระยะไกลพร้อมกัน:

#!/usr/bin/env bash
# Tar + parallel compress + stream to remote — no temp files
# Requires: pigz (parallel gzip)
tar cf - /data/large_dir \
  | pigz -p 4 \
  | ssh backup-host 'cat > /backups/large_dir.tar.gz'

# Verify the remote file exists
ssh backup-host 'ls -lh /backups/large_dir.tar.gz'

ควบคุมขนาดบัฟเฟอร์และการหยุดรอ

ไปป์มีบัฟเฟอร์ของเคอร์เนล (โดยทั่วไป 64 KB) เมื่อบัฟเฟอร์เต็ม ผู้เขียนจะ หยุดรอ และเมื่อบัฟเฟอร์ว่าง ผู้รับจะหยุดรอ โดยทั่วไปนี่คือสิ่งที่คุณต้องการ แต่บางกรณีการหยุดรออาจทำให้เกิดการติดตาย

ความเสี่ยงที่จะติดตาย: หากโพรเซส A เขียนไปยัง FIFO1 และอ่านจาก FIFO2 ขณะที่โพรเซส B เขียนไปยัง FIFO2 และอ่านจาก FIFO1 ทั้งสองโพรเซสอาจหยุดรอให้อีกฝ่ายอ่านข้อมูลก่อน

วิธีแก้:

  • ให้ฝั่งใดฝั่งหนึ่งทำงานแบบ เบื้องหลัง (&) เพื่อไม่ให้เชลล์หยุดรอ
  • ใช้ mbuffer หรือ pv เพื่อเพิ่มบัฟเฟอร์ในหน่วยความจำที่ใหญ่ขึ้นระหว่างขั้นตอน
  • ใช้ pv -q -B 128m เพื่อแทรกบัฟเฟอร์ขนาด 128 MB ช่วยให้ความผันผวนของอัตราการประมวลผลราบรื่นขึ้น
#!/usr/bin/env bash
# pv adds a 64 MB buffer and shows throughput
# Useful when producer and consumer have bursty speeds
dd if=/dev/urandom bs=1M count=200 \
  | pv -B 64m \
  | gzip \
  | wc -c

ตัวอย่างเชิงปฏิบัติ: ตัวรวมบันทึกแบบเรียลไทม์

นี่คือตัวอย่างรูปแบบที่สมบูรณ์และใช้งานได้จริง: ติดตามไฟล์บันทึกหลายไฟล์ รวมสตรีมผ่านไปป์ที่มีชื่อ กรองหาข้อผิดพลาด และเขียนสรุปแบบสด ทั้งหมดนี้ทำได้โดยไม่ใช้ไฟล์ระหว่างกลาง และทุกขั้นตอนทำงานพร้อมกัน

ไปป์ไลน์ลักษณะนี้มักทำงานเป็นสคริปต์ตรวจสอบเบื้องหลังบนเซิร์ฟเวอร์ที่ใช้งานจริง

#!/usr/bin/env bash
FIFO=/tmp/log_aggregator
mkfifo "$FIFO"

cleanup() { rm -f "$FIFO"; }
trap cleanup EXIT INT TERM

# Fan-in: tail multiple logs into the FIFO
tail -F /var/log/syslog /var/log/auth.log > "$FIFO" &
TAIL_PID=$!

# Consumer: filter and timestamp errors in real time
grep --line-buffered -i 'error\|fail\|crit' "$FIFO" \
  | while IFS= read -r line; do
      printf '[%s] %s\n' "$(date '+%H:%M:%S')" "$line"
    done

kill "$TAIL_PID" 2>/dev/null

เปรียบเทียบประสิทธิภาพของไปป์ไลน์กับไฟล์ชั่วคราว

คุณสามารถวัดความแตกต่างของอัตราการประมวลผลจริงระหว่างแนวทางแบบสตรีมกับแนวทางใช้ไฟล์ชั่วคราวได้ด้วย time แนวทางไปป์ไลน์จะทำงานได้ดีกว่าเมื่อใช้ชุดข้อมูลขนาดใหญ่ เนื่องจาก:

  • ขั้นตอนต่าง ๆ ทำงานพร้อมกัน — CPU และการรับส่งข้อมูลทำงานทับซ้อนกัน
  • ไม่มีการรับส่งข้อมูลบนดิสก์สำหรับข้อมูลระหว่างกลาง — มีเพียงเอาต์พุตสุดท้ายที่ถูกเขียนลงดิสก์
  • การใช้หน่วยความจำคงที่โดยไม่ขึ้นกับขนาดอินพุต (สตรีม ไม่ใช่บัฟเฟอร์)

การทดสอบประสิทธิภาพอย่างง่ายเพื่อเปรียบเทียบทั้งสองแนวทาง:

#!/usr/bin/env bash
# Approach 1: Temp file (sequential)
time bash -c '
  seq 1 5000000 > /tmp/nums.txt
  sort -n /tmp/nums.txt > /tmp/sorted.txt
  uniq /tmp/sorted.txt | wc -l
  rm /tmp/nums.txt /tmp/sorted.txt
'

echo '---'

# Approach 2: Streaming pipeline (concurrent)
time bash -c 'seq 1 5000000 | sort -n | uniq | wc -l'

แบบทดสอบความเข้าใจ: พฤติกรรมการหยุดรอของไปป์ที่มีชื่อ

พิจารณาสคริปต์ต่อไปนี้:

mkfifo /tmp/mypipe
echo 'hello' > /tmp/mypipe
echo 'done'

เมื่อเรียกใช้สคริปต์นี้โดยไม่มีโพรเซสเบื้องหลังหรือตัวอ่าน จะเกิดอะไรขึ้น

ทบทวน: กระบวนการส่งข้อมูลต่อเนื่องและไปป์ที่มีชื่อ

ในบทเรียนนี้ คุณได้สำรวจวิธีเคลื่อนย้ายข้อมูลระหว่างโพรเซสอย่างมีประสิทธิภาพโดยไม่ใช้ไฟล์ชั่วคราวระหว่างทาง:

  • ไปป์แบบไม่ระบุชื่อ (|) เชื่อมคำสั่งที่อยู่ติดกันและเรียกใช้ทุกขั้นตอนพร้อมกัน โดยจัดการแรงดันย้อนกลับโดยอัตโนมัติ
  • ไปป์ที่มีชื่อ (mkfifo) สร้างรายการ FIFO ในระบบไฟล์ ซึ่งทำให้โพรเซสที่ไม่เกี่ยวข้องกันหรือโพรเซสเบื้องหลังสามารถส่งข้อมูลต่อเนื่องหากันได้ — การเขียนจะหยุดรอจนกว่าจะมีตัวอ่านอยู่
  • การแทนที่โพรเซส (<(cmd), >(cmd)) ทำให้คำสั่งที่คาดว่าจะได้รับชื่อไฟล์สามารถรับหรือสร้างกระแสข้อมูลได้อย่างโปร่งใส
  • tee + >() แยกกระแสข้อมูลหนึ่งกระแสไปยังตัวรับหลายตัวที่ทำงานพร้อมกัน โดยไม่ต้องอ่านแหล่งข้อมูลซ้ำ
  • การรวมกระแสเข้า รวมผู้สร้างข้อมูลหลายรายเข้าเป็นผู้รับรายเดียวผ่าน FIFO ที่ใช้ร่วมกัน
  • แรงดันย้อนกลับและการหยุดรอ เป็นคุณสมบัติ ไม่ใช่ข้อผิดพลาด — แต่ควรเรียกใช้อย่างน้อยหนึ่งฝั่งของคู่ FIFO เป็นโพรเซสเบื้องหลังเสมอ เพื่อหลีกเลี่ยงภาวะติดตาย
  • ใช้ pv หรือ mbuffer เพื่อเพิ่มบัฟเฟอร์ให้ใหญ่ขึ้นและติดตามอัตราการส่งข้อมูลเมื่อขั้นตอนต่าง ๆ ทำงานเป็นช่วง ๆ

เทคนิคเหล่านี้เป็นรากฐานของวิศวกรรมข้อมูล Bash ที่มีอัตราการประมวลผลสูง: ประมวลผลข้อมูลระดับ GB โดยใช้หน่วยความจำคงที่และใช้การประมวลผลแบบขนานของ CPU/IO ได้สูงสุด

คำถามที่พบบ่อย

บทเรียน “ไปป์ไลน์แบบสตรีมและไปป์ที่ตั้งชื่อเพื่อเพิ่มอัตรารับส่งข้อมูล” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “ไปป์ไลน์แบบสตรีมและไปป์ที่ตั้งชื่อเพื่อเพิ่มอัตรารับส่งข้อมูล” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส DevOps Bootcamp ให้อัปเกรดเป็น CoddyKit PRO คอร์ส DevOps Bootcamp มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “ไปป์ไลน์แบบสตรีมและไปป์ที่ตั้งชื่อเพื่อเพิ่มอัตรารับส่งข้อมูล”

ใช้ FIFO และการแทนที่กระบวนการเพื่อส่งข้อมูลแบบสตรีมระหว่างแต่ละขั้นโดยไม่ต้องใช้ไฟล์ตัวกลาง คุณปฏิบัติ DevOps Bootcamp ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน DevOps Bootcamp หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน DevOps Bootcamp บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน

บทเรียน “ไปป์ไลน์แบบสตรีมและไปป์ที่ตั้งชื่อเพื่อเพิ่มอัตรารับส่งข้อมูล” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน DevOps Bootcamp นี้ได้ไหม

ได้ บทเรียน DevOps Bootcamp ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. การวิเคราะห์ประสิทธิภาพสคริปต์และการหลีกเลี่ยง subshell ที่ไม่จำเป็น
  2. การทำงานแบบขนานด้วย xargs -P และงานเบื้องหลัง
  3. การจัดการเวิร์กโหลดด้วย GNU parallel
  4. ไปป์ไลน์แบบสตรีมและไปป์ที่ตั้งชื่อเพื่อเพิ่มอัตรารับส่งข้อมูล
← กลับไปที่ DevOps Bootcamp