+1

Đừng chỉ "Insert" đại: Nghệ thuật đẩy dữ liệu vào Database hiệu quả nhất

1. Cấp độ 1: Insert cơ bản (Single Row)

Đây là kiểu mà ai cũng biết. Bạn có một bản ghi, bạn đẩy nó vào.

INSERT INTO users (name, email) VALUES ('Hoang Huy', 'hoang@hasaki.vn');

Vấn đề: Nếu bạn cần chèn 1.000 user, và bạn dùng vòng lặp for trong code để gọi lệnh này 1.000 lần, bạn đang thực hiện một "tội ác" với hiệu năng. Mỗi lần gọi là một lần ứng dụng phải kết nối với DB, gửi query, DB phân tích, ghi log, cập nhật Index... tốn rất nhiều thời gian chờ (latency).

2. Cấp độ 2: Bulk/Batch Insert (Chèn theo lô)

Thay vì đi chợ 100 lần để mua 100 quả trứng, tại sao không đi 1 lần và mua cả khay? SQL cho phép bạn chèn nhiều dòng trong cùng một câu lệnh.

INSERT INTO users (name, email) VALUES 
('User 1', 'u1@example.com'),
('User 2', 'u2@example.com'),
...
('User 1000', 'u1000@example.com');

Lợi ích: Tốc độ có thể nhanh gấp 10-50 lần so với việc insert từng dòng đơn lẻ. Hầu hết các ORM hiện đại (như Eloquent của Laravel hay GORM của Go) đều hỗ trợ hàm insert() truyền vào một mảng để tự động thực hiện việc này.

3. Cấp độ 3: INSERT INTO ... SELECT (Copy dữ liệu)

Bạn muốn chuyển dữ liệu từ bảng temporary_users sang bảng chính thức users? Đừng lôi nó về code rồi lại đẩy ngược lên. Hãy để Database tự làm việc đó.

INSERT INTO users (name, email)
SELECT name, email FROM temporary_users WHERE status = 'verified';

Đây là cách nhanh nhất để di chuyển dữ liệu nội bộ trong Database vì dữ liệu không cần phải "đi du lịch" qua lại giữa Server DB và Server App.

4. Cấp độ 4: "Upsert" - Insert hoặc Update

Đây là tình huống cực kỳ phổ biến: Bạn muốn thêm một user, nhưng nếu email đó đã tồn tại rồi thì chỉ cập nhật lại số điện thoại chứ không báo lỗi.

  • MySQL (ON DUPLICATE KEY UPDATE):
INSERT INTO users (email, phone) VALUES ('hoang@hasaki.vn', '0901234xxx')
ON DUPLICATE KEY UPDATE phone = '0901234xxx';
  • PostgreSQL (ON CONFLICT):
INSERT INTO users (email, phone) VALUES ('hoang@hasaki.vn', '0901234xxx')
ON CONFLICT (email) DO UPDATE SET phone = EXCLUDED.phone;

Kỹ thuật này giúp code của bạn sạch hơn, không cần phải mất công SELECT để kiểm tra sự tồn tại trước khi INSERT.

5. Những "kẻ thù" làm chậm quá trình Insert

Tại sao đôi khi chèn dữ liệu vẫn chậm dù đã dùng Batch Insert?

  1. Quá nhiều Index: Như mình đã nói ở bài trước, mỗi khi Insert, DB phải cập nhật lại tất cả Index liên quan. Nếu bảng có 10 cái Index, việc Insert sẽ nặng nề hơn rất nhiều.
  2. Trình kích hoạt (Triggers): Nếu bảng có Trigger chạy các logic phức tạp sau mỗi dòng được chèn, nó sẽ kéo tụt hiệu năng.
  3. Khóa ngoại (Foreign Keys): DB phải tốn công đi kiểm tra xem ID bạn chèn có tồn tại ở bảng cha hay không.
  4. Transaction quá lớn: Chèn 1 triệu dòng trong 1 Transaction duy nhất có thể làm tràn bộ nhớ đệm (Undo Log/Redo Log) và khóa (lock) bảng quá lâu.

Lời kết

Để tối ưu hóa việc INSERT, hãy nhớ quy tắc: Hạn chế số lần kết nối, tận dụng sức mạnh xử lý của DB và biết tiết chế Index. Nếu bạn cần chèn dữ liệu cực lớn (hàng tỷ dòng), hãy tìm hiểu thêm về các công cụ chuyên dụng như LOAD DATA INFILE (MySQL) hoặc COPY (Postgres).

Hy vọng bài viết này giúp anh em "đẩy" data mượt mà hơn. Anh em thường dùng cách nào để migrate dữ liệu lớn? Hãy chia sẻ kinh nghiệm bên dưới nhé!


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí