MySQL LOAD DATA LOCAL INFILE ile Toplu Yükleme
Selamlar, bu yazımda MySQL'in en sevdiğim toplu veri ekleme yollarından birine, LOAD DATA LOCAL INFILE komutuna bakacağız. Elimizde 50 bin satırlık bir CSV varsa ve bunu INSERT döngüsüyle yüklemeye kalktıysanız muhtemelen siz de çay molasını uzatmışsınızdır. Bu komut tam olarak o çileyi bitirmek için var. Hadi başlayalım.
LOAD DATA LOCAL INFILE nedir?
LOAD DATA INFILE, dosyayı sunucu makineden okur. Yani CSV'nizi önce scp ile sunucuya atmanız, sonra da MySQL kullanıcısının FILE yetkisinin olması gerekir. LOCAL anahtar kelimesi devreye girince işler değişiyor: dosya istemcinin (yani sizin) makinesinden okunup bağlantı üzerinden sunucuya akıtılıyor. Sunucuya dosya kopyalamak yok, FILE yetkisi gerekmiyor. Kısacası developer dostu varyantı bu.
Sunucu ve istemci tarafında ayar
local_infile her iki tarafta da açık olmak zorunda. Sunucu tarafında my.cnf dosyasına şunu ekliyoruz:
[mysqld]
local_infile = 1
Çalışan bir MySQL'de geçici olarak açmak istiyorsanız:
SET GLOBAL local_infile = 1;
İstemci tarafı da unutulmamalı. mysql CLI'sı ile bağlanırken bayrağı geçiyoruz:
mysql --local-infile=1 -u app -p mydb
İlk kurulumda en sık takıldığım yer burası olur. Sunucuda açıyorum, sonra ERROR 3950 (42000): The used command is not allowed görüp bir saat boş yere debug ediyorum, halbuki istemcide bayrak yok. Bence ilk başta her iki tarafı birden kontrol etmek alışkanlık olmalı.
Basit bir CSV yüklemesi
Diyelim ki customers.csv elimizde, içinde başlık satırı, virgülle ayrılmış alanlar ve tırnaklı string'ler var:
id,name,email,city
1,Alice,alice@example.com,'New York'
2,Bob,bob@example.com,Chicago
Hedef tabloya yüklemek için tam komutu yazalım:
LOAD DATA LOCAL INFILE '/home/mert/customers.csv'
INTO TABLE customers
FIELDS TERMINATED BY ','
OPTIONALLY ENCLOSED BY '"'
LINES TERMINATED BY '\n'
IGNORE 1 ROWS
(id, name, email, city);
Buradaki kritik parçalar şunlar: FIELDS TERMINATED BY ',' alanları virgüle göre ayırıyor; OPTIONALLY ENCLOSED BY '"' ise tırnak içine alınmış string'leri (özellikle içinde virgül geçenleri) doğru parse ediyor. IGNORE 1 ROWS ise başlık satırını atlıyor. CSV'lerde header tutmak yaygın olduğu için bu satırı neredeyse her zaman ekliyorum.
Veri donusturme: degisken kullanimi
Bazen tarihler 2026-05-29 gibi gelmiyor da 29/05/2026 formatında geliyor. Ya da hesaplanmış bir alan eklemek istiyoruz. Bu durumda @degisken formunu kullanıp SET ile dönüştürüyoruz:
LOAD DATA LOCAL INFILE '/tmp/orders.csv'
INTO TABLE orders
FIELDS TERMINATED BY ','
LINES TERMINATED BY '\n'
IGNORE 1 ROWS
(id, user_id, amount, @raw_date)
SET created_at = STR_TO_DATE(@raw_date, '%d/%m/%Y');
Performans: transaction ve indexler
Milyon satırlık dosyalarda iki şeyi öneririm. Birincisi yüklemeyi bir transaction'a sarın; her satırda fsync yemekten kurtulursunuz. İkincisi, hedef tablonun ikincil indexleri varsa yükleme öncesi ALTER TABLE customers DISABLE KEYS deyip yükleme bitince ENABLE KEYS ile geri açın. InnoDB'de bu daha sınırlı etki yapar ama MyISAM'da fark uçurum gibidir. Foreign key kontrolünü geçici kapatmak (SET FOREIGN_KEY_CHECKS = 0;) da batch işlerde mantıklı, fakat tutarlılığı sonradan kendiniz doğrulamak şartıyla.
Sık karşılaşılan hatalar
- İstemcide bayrağı unutmak: Sunucuda
local_infile=1yetmiyor, istemci de izin vermek zorunda. Yukarıdaki--local-infile=1veya driver tarafındaallow_local_infile=Trueşart. - Guvenlik riskini hafife almak:
LOCAL INFILEaktifken bağlandığınız sunucu sizden istediği herhangi bir dosyayı isteyebilir. Tanımadığınız sunucularalocal_infile=1ile bağlanmayın; production'da ihtiyacınız bittiğinde kapatın. - Satır sonu karakteri: Windows'tan gelen CSV'lerde
\r\nolur.LINES TERMINATED BY '\r\n'yazmazsanız son alana\ryapışır ve sonradanWHERE city='Chicago'çalışmaz.
Kapanış
Bence orta-büyük dosyalarda LOAD DATA LOCAL INFILE hâlâ MySQL'in en hızlı toplu yükleme aracı. Doğru ayar bir kere yapıldıktan sonra geri dönmek istemiyorsunuz. Umarım faydalı olur, bir sonraki yazıda görüşmek üzere.
