นำเข้าไฟล์ SQL ขนาดใหญ่ผ่าน Command Line

นำเข้าไฟล์ .sql และ .sql.gz ขนาดหลาย GB เข้า MySQL หรือ MariaDB ผ่าน Command Line พร้อมดูความคืบหน้าด้วย pv และแก้ Error ที่พบบ่อย

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

แชร์FacebookXLINELinkedIn
SQL — นำเข้าไฟล์ SQL ขนาดใหญ่ผ่าน Command Line
นำเข้าไฟล์ SQL ขนาดใหญ่ผ่าน Command Line

คำตอบสั้น ๆ: นำเข้าไฟล์ .sql และ .sql.gz ขนาดหลาย GB เข้า MySQL หรือ MariaDB ผ่าน Command Line พร้อมดูความคืบหน้าด้วย pv และแก้ Error ที่พบบ่อย

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

การนำเข้าไฟล์ SQL ผ่านหน้าเว็บอย่าง phpMyAdmin มักติดข้อจำกัดเรื่องขนาดไฟล์อัปโหลดและเวลาประมวลผลของ PHP เมื่อไฟล์ Dump มีขนาดหลายร้อย MB หรือหลาย GB วิธีที่เสถียรกว่ามากคือนำเข้าผ่าน Command Line ด้วยโปรแกรม mysql โดยตรงบนเซิร์ฟเวอร์ ซึ่งไม่มีข้อจำกัดเรื่อง Upload Limit และทำงานได้เร็วกว่า

บทความนี้อธิบายการนำเข้าไฟล์ .sql และ .sql.gz ขนาดใหญ่เข้า MySQL 8 หรือ MariaDB 10.x พร้อมวิธีดูความคืบหน้า วิธีเร่งความเร็ว และวิธีแก้ Error ที่เจอบ่อยเมื่อย้ายฐานข้อมูลข้ามเวอร์ชัน

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

หมายเหตุ: ถ้าฐานข้อมูลปลายทางมีข้อมูลอยู่แล้ว การนำเข้าไฟล์ Dump ที่มีคำสั่ง DROP TABLE จะเขียนทับตารางชื่อเดียวกันทันที ให้สำรองฐานข้อมูลปลายทางก่อนทุกครั้งตามบทความ สำรองและกู้คืนฐานข้อมูล MySQL/MariaDB ด้วย mysqldump

ขั้นตอนที่ 1: ตรวจสอบไฟล์และพื้นที่ดิสก์

ดูขนาดไฟล์และพื้นที่ว่างของพาร์ติชันที่เก็บข้อมูล MySQL (ปกติคือ /var/lib/mysql)

ls -lh /root/backup/shop.sql.gz
df -h /var/lib/mysql

ถ้าเป็นไฟล์ .gz ดูขนาดหลังแตกไฟล์ได้โดยไม่ต้องแตกจริง (ตัวเลขนี้อาจไม่ถูกต้องสำหรับไฟล์ที่ใหญ่กว่า 4 GB ให้ใช้เป็นค่าประมาณเท่านั้น)

gzip -l /root/backup/shop.sql.gz

จากนั้นเปิดดูส่วนหัวของไฟล์ เพื่อดูว่า Dump มาจากเวอร์ชันใด มีคำสั่ง CREATE DATABASE หรือ USE อยู่หรือไม่ และใช้ Collation อะไร

zcat /root/backup/shop.sql.gz | head -n 40
# ถ้าเป็นไฟล์ .sql ธรรมดา
head -n 40 /root/backup/shop.sql

ถ้าในไฟล์มีบรรทัด USE `ชื่อฐานข้อมูลเดิม`; ข้อมูลจะถูกนำเข้าไปยังฐานข้อมูลชื่อเดิม ไม่ใช่ฐานข้อมูลที่คุณระบุในคำสั่ง ต้องระวังเป็นพิเศษถ้าชื่อฐานข้อมูลบนเซิร์ฟเวอร์ใหม่ต่างจากเดิม

ขั้นตอนที่ 2: เปิด Session ที่ไม่หลุดเมื่อ SSH ขาด

การนำเข้าไฟล์ขนาดหลาย GB อาจใช้เวลาเป็นชั่วโมง ถ้า SSH หลุดกลางทาง คำสั่งจะหยุดและฐานข้อมูลจะเหลือข้อมูลครึ่ง ๆ กลาง ๆ ให้รันคำสั่งภายใน tmux ก่อนเสมอ (รายละเอียดใน ใช้ tmux ให้คำสั่งทำงานต่อแม้ SSH หลุด)

tmux new -s import

ขั้นตอนที่ 3: นำเข้าไฟล์ด้วยคำสั่ง mysql

รูปแบบพื้นฐานสำหรับไฟล์ .sql คือ

mysql -u shopuser -p shopdb < /root/backup/shop.sql

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

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

zcat /root/backup/shop.sql.gz | mysql -u shopuser -p shopdb

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

zcat /root/backup/shop.sql.gz | mysql --default-character-set=utf8mb4 -u shopuser -p shopdb

บน MariaDB 11 ขึ้นไป ชื่อคำสั่งหลักคือ mariadb ถ้าเซิร์ฟเวอร์ไม่มีคำสั่ง mysql ให้เปลี่ยนเป็น mariadb ได้ทันที ตัวเลือกเหมือนกันทุกอย่าง

ขั้นตอนที่ 4: ดูความคืบหน้าด้วย pv

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

# Ubuntu
sudo apt install pv
# AlmaLinux / Rocky Linux (pv อยู่ใน EPEL)
sudo dnf install epel-release
sudo dnf install pv

จากนั้นให้ pv เป็นตัวอ่านไฟล์แทน

# ไฟล์ .sql
pv /root/backup/shop.sql | mysql -u shopuser -p shopdb

# ไฟล์ .sql.gz (เปอร์เซ็นต์คิดจากขนาดไฟล์ที่บีบอัด)
pv /root/backup/shop.sql.gz | zcat | mysql -u shopuser -p shopdb

ผลลัพธ์จะเป็นแถบความคืบหน้าในรูปแบบประมาณ 1.20GiB 0:04:12 [4.9MiB/s] [=====> ] 45% ETA 0:05:01

ขั้นตอนที่ 5: เร่งความเร็วการนำเข้า (ทางเลือก)

สำหรับไฟล์ที่ใหญ่มาก การปิดการตรวจสอบบางอย่างระหว่างนำเข้าจะช่วยลดเวลาได้ชัดเจน เพราะไม่ต้องตรวจ Foreign Key และ Unique Key ทีละแถว และไม่ต้อง Commit ทุกคำสั่ง ให้เข้า mysql client แล้วใช้คำสั่ง SOURCE

mysql --default-character-set=utf8mb4 -u shopuser -p shopdb
SET autocommit = 0;
SET unique_checks = 0;
SET foreign_key_checks = 0;
SOURCE /root/backup/shop.sql;
COMMIT;
SET unique_checks = 1;
SET foreign_key_checks = 1;

ค่าเหล่านี้มีผลเฉพาะ Session นี้เท่านั้น ไม่กระทบผู้ใช้อื่น คำสั่ง SOURCE ใช้ได้กับไฟล์ .sql ที่แตกแล้วเท่านั้น ข้อควรระวังคือ เมื่อปิด unique_checks และ foreign_key_checks MySQL จะไม่ตรวจความถูกต้องของข้อมูลซ้ำ จึงเหมาะกับไฟล์ Dump ที่มาจากฐานข้อมูลที่ถูกต้องอยู่แล้วเท่านั้น

ตัวเลือกอื่นที่มีผลต่อความเร็ว สรุปได้ดังนี้

วิธีผลที่ได้ข้อควรระวัง
ปิด foreign_key_checks และ unique_checksลดงานตรวจสอบระหว่าง INSERTใช้กับ Dump ที่เชื่อถือได้เท่านั้น
autocommit = 0 แล้ว COMMIT ท้ายสุดลดจำนวนการเขียนลงดิสก์ถ้าล้มกลางทาง ข้อมูลใน Transaction ที่ยังไม่ Commit จะถูกยกเลิก
เพิ่ม innodb_buffer_pool_sizeอ่านเขียน Index ในหน่วยความจำได้มากขึ้นต้องมี RAM พอ ดู ปรับแต่ง MySQL/MariaDB เบื้องต้นด้วย innodb_buffer_pool_size
ใช้ Dump แบบ Extended INSERT (ค่าเริ่มต้นของ mysqldump)หนึ่งคำสั่ง INSERT ใส่ได้หลายแถวต้องทำตั้งแต่ตอนสร้างไฟล์ Dump

ตรวจสอบผลลัพธ์

เมื่อนำเข้าเสร็จ ให้นับจำนวนตารางในฐานข้อมูลปลายทาง แล้วเทียบกับฐานข้อมูลต้นทาง

mysql -u shopuser -p -e "SELECT COUNT(*) AS tables FROM information_schema.TABLES WHERE TABLE_SCHEMA = 'shopdb';"

จากนั้นสุ่มนับจำนวนแถวของตารางสำคัญ เช่น ตารางคำสั่งซื้อหรือตารางผู้ใช้ ด้วย SELECT COUNT(*) แล้วเทียบกับต้นทาง ไม่ควรใช้คอลัมน์ TABLE_ROWS ใน information_schema เป็นตัวเทียบ เพราะสำหรับ InnoDB เป็นเพียงค่าประมาณ

mysql -u shopuser -p shopdb -e "SELECT COUNT(*) FROM orders;"

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

ปัญหาที่พบบ่อย

ERROR 2006 (HY000): MySQL server has gone away หรือ Got a packet bigger than 'max_allowed_packet' bytes

เกิดเมื่อมีคำสั่ง INSERT บางคำสั่งใหญ่กว่าค่า max_allowed_packet ของเซิร์ฟเวอร์ แก้โดยเพิ่มค่าชั่วคราวก่อนนำเข้า (ต้องใช้ผู้ใช้ที่มีสิทธิ์ระดับผู้ดูแล เช่น root)

sudo mysql -e "SET GLOBAL max_allowed_packet = 1073741824;"

แล้วรันคำสั่งนำเข้าใหม่ใน Session ใหม่ พร้อมระบุค่าเดียวกันฝั่ง Client

zcat /root/backup/shop.sql.gz | mysql --max-allowed-packet=1G -u shopuser -p shopdb

ถ้าต้องการให้ค่าคงอยู่หลังรีสตาร์ต ให้เพิ่ม max_allowed_packet = 1G ในส่วน [mysqld] ของไฟล์ตั้งค่า (Ubuntu กับ MySQL คือ /etc/mysql/mysql.conf.d/mysqld.cnf, Ubuntu กับ MariaDB คือ /etc/mysql/mariadb.conf.d/50-server.cnf, AlmaLinux/Rocky คือไฟล์ใน /etc/my.cnf.d/)

ERROR 1227 (42000): Access denied; you need (at least one of) the SUPER privilege(s)

มักเกิดจากบรรทัด DEFINER=`ผู้ใช้เดิม`@`host` ใน View, Trigger หรือ Stored Procedure ที่อ้างถึงผู้ใช้ที่ไม่มีบนเซิร์ฟเวอร์ใหม่ ทางแก้ที่ง่ายคือตัดส่วน DEFINER ออกระหว่างนำเข้า ซึ่งจะทำให้ Object เหล่านั้นใช้ผู้ใช้ที่กำลังนำเข้าเป็นเจ้าของแทน

zcat /root/backup/shop.sql.gz | sed -E 's/DEFINER=`[^`]+`@`[^`]+`//g' | mysql -u shopuser -p shopdb

ถ้า Error อ้างถึง @@GLOBAL.GTID_PURGED แปลว่า Dump สร้างจากเซิร์ฟเวอร์ที่เปิด GTID ไว้ ให้สร้าง Dump ใหม่ด้วยตัวเลือก --set-gtid-purged=OFF

ERROR 1273 (HY000): Unknown collation: 'utf8mb4_0900_ai_ci'

Collation นี้มีเฉพาะใน MySQL 8 เมื่อนำ Dump จาก MySQL 8 ไปเข้า MariaDB จะไม่รู้จัก ให้เปลี่ยนเป็น utf8mb4_unicode_ci ระหว่างนำเข้า

zcat /root/backup/shop.sql.gz | sed 's/utf8mb4_0900_ai_ci/utf8mb4_unicode_ci/g' | mysql -u shopuser -p shopdb

ถ้าต้องการแปลงทั้งฐานข้อมูลให้เป็น utf8mb4 อย่างถูกต้อง ดู แปลงฐานข้อมูลเป็น utf8mb4 ให้รองรับภาษาไทยและอีโมจิ

ERROR 1046 (3D000): No database selected หรือดิสก์เต็มระหว่างนำเข้า

Error 1046 เกิดเมื่อไม่ได้ระบุชื่อฐานข้อมูลท้ายคำสั่งและในไฟล์ไม่มีคำสั่ง USE ให้ใส่ชื่อฐานข้อมูลเพิ่มท้ายคำสั่ง ส่วนกรณีดิสก์เต็ม (ข้อความ The table is full หรือ Error 28) ให้ตรวจพื้นที่ตามบทความ ดิสก์เต็ม: ตรวจสอบพื้นที่และหาไฟล์ขนาดใหญ่บน Linux โดยเฉพาะไฟล์ Binary Log ใน /var/lib/mysql ที่อาจโตขึ้นมากระหว่างนำเข้า

หากทำตามขั้นตอนแล้วยังนำเข้าไม่สำเร็จ หรือไม่แน่ใจว่าควรปรับค่าใดบนเซิร์ฟเวอร์ ติดต่อทีมงาน THAI DATA CLOUD ได้ที่ https://thaidata.cloud/contact/

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

แชร์FacebookXLINELinkedIn

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