กู้ข้อมูล MySQL ย้อนเวลาด้วย Binary Log

เมื่อมีคนลบข้อมูลผิดตอนบ่ายสอง แต่มีข้อมูลสำรองแค่ของตีสาม วิธีกู้กลับมาถึงวินาทีก่อนเกิดเหตุคือการนำ binary log มาเล่นต่อ

โดย ทีมงาน THAI DATA CLOUD อัปเดต อ่าน 11 นาที เข้าชม 4 ครั้ง

แชร์FacebookXLINELinkedIn
MySQL — กู้ข้อมูล MySQL ย้อนเวลาด้วย Binary Log
กู้ข้อมูล MySQL ย้อนเวลาด้วย Binary Log

คำตอบสั้น ๆ: เมื่อมีคนลบข้อมูลผิดตอนบ่ายสอง แต่มีข้อมูลสำรองแค่ของตีสาม วิธีกู้กลับมาถึงวินาทีก่อนเกิดเหตุคือการนำ binary log มาเล่นต่อ

คู่มือนี้อธิบาย MySQL ทีละขั้นตอน พร้อมคำสั่งที่ใช้ได้จริงและปัญหาที่พบบ่อย

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

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

สิ่งที่ต้องเตรียม

  • MySQL หรือ MariaDB ที่เปิด binary log ไว้ตั้งแต่ก่อนเกิดเหตุ เพราะเปิดทีหลังไม่ช่วยอะไร
  • ไฟล์สำรองข้อมูลเต็มชุดล่าสุด
  • เครื่องทดสอบสำหรับกู้ก่อน ห้ามกู้ทับฐานข้อมูลจริงโดยตรง

ขั้นตอนที่ 1: ตรวจว่าเปิด binary log ไว้หรือไม่

mysql -e "SHOW VARIABLES LIKE 'log_bin';"
mysql -e "SHOW BINARY LOGS;"
sudo ls -lh /var/log/mysql/mysql-bin.*

หากยังไม่เปิด ให้เปิดไว้ตั้งแต่วันนี้ เพราะเป็นสิ่งที่ช่วยได้เฉพาะเหตุการณ์ในอนาคต

sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
server-id = 1
log_bin = /var/log/mysql/mysql-bin.log
binlog_format = ROW
binlog_expire_logs_seconds = 1209600
max_binlog_size = 256M
sudo systemctl restart mysql

เก็บย้อนหลังอย่างน้อย 14 วัน ควรยาวกว่าระยะห่างระหว่างการสำรองข้อมูลเต็มชุดสองครั้งเสมอ

ขั้นตอนที่ 2: หยุดความเสียหายก่อน

ทันทีที่รู้ว่าเกิดเหตุ สิ่งแรกคือหยุดไม่ให้มีการเขียนเพิ่ม เพราะยิ่งมีข้อมูลใหม่เข้ามา การกู้ยิ่งซับซ้อน

# ปิดไม่ให้แอปเขียนได้
mysql -e "SET GLOBAL read_only = ON; SET GLOBAL super_read_only = ON;"

# หรือหยุดเว็บไปเลยหากทำได้
sudo systemctl stop nginx php8.3-fpm

คัดลอก binary log ทั้งหมดออกไปเก็บไว้อีกที่ทันที ก่อนที่จะถูกลบตามรอบ

sudo mkdir -p /root/pitr
sudo cp /var/log/mysql/mysql-bin.* /root/pitr/
sudo mysql -e "FLUSH BINARY LOGS;"

ขั้นตอนที่ 3: หาว่าเหตุเกิดที่ตำแหน่งไหน

อ่าน binary log เป็นข้อความเพื่อหาคำสั่งที่ทำให้เกิดปัญหา

# ดูเหตุการณ์ในช่วงเวลาที่สงสัย
sudo mysqlbinlog --base64-output=DECODE-ROWS -v \
  --start-datetime="2026-09-25 13:30:00" \
  --stop-datetime="2026-09-25 14:30:00" \
  /root/pitr/mysql-bin.000042 | less

ค้นหาคำสั่งที่ต้องสงสัย

sudo mysqlbinlog --base64-output=DECODE-ROWS -v /root/pitr/mysql-bin.000042 \
  | grep -n -B5 -A5 -i 'DELETE FROM orders'

ในผลลัพธ์ให้มองหาบรรทัดที่ขึ้นต้นด้วย # at ตามด้วยตัวเลข นั่นคือตำแหน่งในไฟล์ และบรรทัด #260925 14:02:11 คือเวลาที่เกิด จดทั้งสองค่าไว้

ขั้นตอนที่ 4: กู้ไฟล์สำรองเต็มชุดลงเครื่องทดสอบ

sudo mysql -e "CREATE DATABASE recovery;"
zcat /var/backups/mysql/all-2026-09-25-0300.sql.gz | sudo mysql

# หรือกู้เฉพาะฐานข้อมูลเดียวลงชื่อใหม่
zcat /var/backups/mysql/shop-2026-09-25-0300.sql.gz | sudo mysql recovery

จดตำแหน่ง binary log ที่ไฟล์สำรองนี้ถูกสร้าง ซึ่งอยู่ในส่วนหัวของไฟล์ดัมป์หากใช้ --source-data=2

zcat /var/backups/mysql/all-2026-09-25-0300.sql.gz | head -30 | grep -i 'CHANGE REPLICATION\|CHANGE MASTER'

จะเห็นบรรทัดประมาณ -- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000041', MASTER_LOG_POS=194; นี่คือจุดเริ่มต้นที่ต้องเล่น binary log ต่อ

ขั้นตอนที่ 5: เล่น binary log ต่อจนถึงก่อนเกิดเหตุ

วิธีที่ 1 กำหนดด้วยเวลา อ่านง่ายกว่าแต่แม่นยำน้อยกว่าเพราะหลายคำสั่งอาจเกิดในวินาทีเดียวกัน

sudo mysqlbinlog \
  --start-position=194 \
  --stop-datetime="2026-09-25 14:02:10" \
  /root/pitr/mysql-bin.000041 /root/pitr/mysql-bin.000042 \
  | sudo mysql

วิธีที่ 2 กำหนดด้วยตำแหน่ง แม่นยำกว่าและเป็นวิธีที่ควรใช้

sudo mysqlbinlog \
  --start-position=194 \
  --stop-position=8842190 \
  /root/pitr/mysql-bin.000041 /root/pitr/mysql-bin.000042 \
  | sudo mysql

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

ขั้นตอนที่ 6: ตรวจสอบก่อนนำกลับ

mysql -e "SELECT COUNT(*) FROM recovery.orders;"
mysql -e "SELECT MAX(created_at) FROM recovery.orders;"
mysql -e "SELECT * FROM recovery.orders ORDER BY id DESC LIMIT 10;"

เทียบจำนวนแถวกับที่ควรจะเป็น และตรวจว่าข้อมูลล่าสุดมีเวลาใกล้เคียงกับตอนก่อนเกิดเหตุ อย่ารีบนำกลับจนกว่าจะมั่นใจ

ขั้นตอนที่ 7: นำข้อมูลกลับสู่ระบบจริง

หากเสียหายเฉพาะบางตาราง ให้นำกลับเฉพาะตารางนั้น ซึ่งปลอดภัยกว่าการเขียนทับทั้งฐานข้อมูล

mysqldump recovery orders > /tmp/orders-recovered.sql

# สำรองของเดิมไว้ก่อนเสมอ
mysqldump shop orders > /tmp/orders-before-restore.sql

mysql shop < /tmp/orders-recovered.sql

เปิดให้เขียนได้อีกครั้ง

mysql -e "SET GLOBAL super_read_only = OFF; SET GLOBAL read_only = OFF;"
sudo systemctl start php8.3-fpm nginx

ขั้นตอนที่ 8: ซ้อมก่อนที่จะต้องใช้จริง

กระบวนการนี้มีรายละเอียดมากเกินกว่าจะทำถูกครั้งแรกในวันที่เกิดเหตุจริง ซึ่งเป็นวันที่ทุกคนกดดัน ซ้อมอย่างน้อยปีละครั้ง

# สร้างสถานการณ์จำลองบนเครื่องทดสอบ
mysql -e "CREATE DATABASE drill; CREATE TABLE drill.t (id INT PRIMARY KEY, v VARCHAR(50));"
mysql -e "INSERT INTO drill.t VALUES (1,'a'),(2,'b'),(3,'c');"
mysqldump drill > /tmp/drill-backup.sql
mysql -e "INSERT INTO drill.t VALUES (4,'d'),(5,'e');"
sleep 2
mysql -e "DELETE FROM drill.t;"   # จำลองอุบัติเหตุ

แล้วลองกู้กลับให้ได้ 5 แถว จับเวลาที่ใช้ทั้งหมด ตัวเลขนั้นคือ RTO จริงของระบบคุณ

ปัญหาที่พบบ่อยเกี่ยวกับ MySQL

ไม่มี binary log ในช่วงเวลาที่ต้องการ

ถูกลบไปตามรอบแล้ว ตั้ง binlog_expire_logs_seconds ให้ยาวขึ้น และคัดลอก binary log ไปเก็บนอกเครื่องพร้อมกับการสำรองข้อมูลทุกรอบ

เล่น binary log แล้วขึ้น Duplicate entry

เริ่มเล่นจากตำแหน่งที่เก่าเกินไป ทำให้คำสั่งที่มีอยู่แล้วในไฟล์สำรองถูกทำซ้ำ ตรวจตำแหน่งเริ่มต้นจากส่วนหัวของไฟล์ดัมป์ให้ถูกต้อง

binlog_format เป็น STATEMENT

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

ข้อมูลมากจนเล่น binary log ใช้เวลานานมาก

ลดระยะห่างระหว่างการสำรองข้อมูลเต็มชุดลง หรือใช้ตัวสำรองที่ตั้ง Replication ไว้ ซึ่งมีข้อมูลล่าสุดอยู่แล้วและกู้ได้เร็วกว่ามาก

ต้องการวางระบบสำรองข้อมูลที่กู้ย้อนเวลาได้จริง ติดต่อ THAI DATA CLOUD ได้ที่ https://thaidata.cloud/contact/

ทำความเข้าใจ MySQL ก่อนเริ่ม

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

สิ่งที่ต้องเตรียมก่อนใช้งาน MySQL

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

ตรวจสอบว่า MySQL ทำงานถูกต้องแล้ว

  1. กู้สำเนาเต็มขึ้นเครื่องทดสอบได้สำเร็จก่อนเล่นบันทึกซ้ำ
  2. ระบุเวลาหรือตำแหน่งของเหตุการณ์ที่ต้องการหยุดได้ชัดเจนจากบันทึก
  3. ตรวจข้อมูลจริงหลังกู้ เช่น นับจำนวนแถวของตารางสำคัญและเปิดดูรายการล่าสุด ว่าตรงกับก่อนเกิดเหตุ

ข้อควรรู้และข้อจำกัดของ MySQL

  • ซ้อมกู้คืนอย่างน้อยปีละครั้ง สำเนาที่ไม่เคยกู้คืนสำเร็จมีค่าเท่ากับไม่มีสำเนา
  • กำหนดอายุการเก็บบันทึกให้ยาวกว่ารอบการสำรองเต็มเสมอ มิฉะนั้นจะมีช่วงเวลาที่กู้ไม่ได้
  • อย่ากู้ทับฐานข้อมูลที่ให้บริการอยู่ ให้กู้ขึ้นเครื่องแยกแล้วค่อยย้ายเฉพาะข้อมูลที่ต้องการกลับไป

คำถามที่พบบ่อยเกี่ยวกับ MySQL

เปิดบันทึกการเปลี่ยนแปลงแล้วระบบช้าลงไหม

มีภาระเพิ่มขึ้นเล็กน้อยจากการเขียนบันทึก ซึ่งแทบไม่มีผลกับงานทั่วไป และแลกมากับความสามารถในการกู้คืนที่ไม่มีทางอื่นทดแทนได้

ต้องใช้พื้นที่เก็บบันทึกมากแค่ไหน

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

ถ้าเผลอลบข้อมูลไปเมื่อสิบนาทีที่แล้วต้องรีบทำอะไรก่อน

หยุดการเขียนข้อมูลใหม่ทันทีเท่าที่ทำได้ และคัดลอกบันทึกการเปลี่ยนแปลงปัจจุบันออกไปเก็บไว้ก่อน เพราะบันทึกที่ถูกหมุนทิ้งไปแล้วจะกู้ไม่ได้อีก

อ่านเพิ่มเติมเกี่ยวกับ MySQL

แชร์FacebookXLINELinkedIn

มีคำถามเรื่องบริการ? ติดต่อทีมงาน