Bellek israf olmadan PHP büyük MySQL INSERT sorgusu oluşturmak nasıl

5 Cevap php

Böyle bir şey arıyor kodu vardır:

$data = file_get_contents($tempFile); // perhaps 30MB of file data, now in PHP's memory
$hash = md5($data);
$query = "INSERT INTO some_table
          SET BlobData = '" . mysql_real_escape_string($data) . "',
          BlobHash = '$hash'
          ";
mysql_query($query);

Ben bu her olarak çok verimli olmadığını biliyorum. ' operatörleri daha büyük bir bellek bloğu yeniden tahsis edecek ve 30MB dize birkaç kez kopyalanır.

Aşağıdaki çözüm daha verimli bir şey var mı?

$data = file_get_contents($tempFile); // perhaps 30MB of file data, now in PHP's memory
$hash = md5($data);
$query = "INSERT INTO some_table SET BlobData = '%s', BlobHash = '$hash'";
mysql_query(sprintf($query, mysql_real_escape_string($data)));

5 Cevap

Burada iki konu var:

# 1, sen MD5 hash hesaplamak birkaç farklı yolu vardır:

  • Eğer gibi yapmak ve bir dizge olarak PHP içine yüklemek ve PHP'nin md5() kullanın
  • Kullanım PHP'nin md5_file()
  • PHP 5.1 + Eğer md5_file belleğe tamamen yüklenmesini engellemek için md5 ya da biriyle PHP'nin akışları API kullanabilirsiniz
  • exec() sistemin md5sum komutunu çağırmak için kullanın
  • Karma hesaplamak için MySQL'in MD5() işlevini kullanın

Bunların hepsi uygulamak için önemsiz olduğundan, bellek kullanımı ve hız için hepsini uygulamak ve benchmark için kolay olurdu. İşte md5_file exec PHP'nin çok daha hızlı olması yoluyla some benchmarks gösteren sistem md5 olan dosya boyutu arttıkça. O yol yapıyor kesinlikle kadarıyla bellek kullanımı söz konusu olduğunda kötü bir yoldur.

# 2, mysql_real_escape_string, bir veritabanı sorgusu gerçekleştirir, böylece aslında bir dize olarak geri alma ve yeniden iletme, veritabanına blob veri aktarımı ediyoruz (!) INSERT sorgusu ile. Yani için / DB sunucudan 3x yerine 1x seyahat ve 2x PHP bellek kullanıyor.

Bu PHP5 prepared statements kullanmak ve sadece bir kez bu verileri veritabanına göndermek için daha verimli olacak. Bağlanmış makale bölümünü okuyun, bunu parametreleri bağlayıcıdır zaman, parçalar DB blob veri akışı için blob tipini kullanabilirsiniz belirtiyor görürsünüz. PHP docs for mysqli_stmt::send_long_data senin gibi bir blob sütuna bir dosya ekler bu büyük bir basit bir örnek var.

Bunu yaparak, ve sistem md5 komutu ile akışları API, md5_file veya exec birini kullanarak, sen demektir, hiç belleğe dosyanın tamamını yüklemeden tüm INSERT yapabilirsiniz işlemleri sizin dizi için bellek kullanımını istediğiniz gibi düşük olabilir!

Eğer PDO ve prepared statments kullanıyorsanız PDO :: PARAM_LOB türünü kullanabilirsiniz. Dosya tanıtıcısı kullanarak bir veritabanına bir görüntü eklemek için nasıl gösteren LOB sayfada örnek # 2 bakın.

http://us2.php.net/manual/en/pdo.lobs.php

Eğer çıktı tamponlama hile kıstas mı?

ob_start();
echo 'INSERT INTO some_table SET BlobData = \'', mysql_real_escape_string( $data ), '\', BlobHash = \'', $hash, '\'';
mysql_query( ob_get_clean() );

Yapabileceğiniz başka bir şey bağlı parametreleri destekler veya mdb2, mysqli dönüştürmek olduğunu. Yani mysql_real_escape_string çağrı and dize concatenations atlamak için izin verecek.

Eğer başka bir değişkenin içine sorgu koymak, ancak bunun yerine MySQL komutu içine doğrudan geçti olmasaydı herhangi bir fark olur. Ben bu hiç denemedim, ama bütün dize başka bir değişken olarak depolanır değildir gibi bir fark yapabilir.

Bellek kullanımı senin sorunun ise Eh, sen parçalar büyük dosya okumak ve veritabanı alanına CONCAT ile bu parçaları ekleyebilirsiniz.

Örnek kod (test değil):

    $id = 1337;
$h = fopen("path/to/file.ext", "r");
while (!feof($h)) 
	{
	$buffer = fread($h, 4096);
	$sql = "UPDATE table SET my_field = CONCAT(my_field, '" . mysql_real_escape_string($buffer) . "') WHERE Id = " . $id;
	mysql_query($sql);
	}

Bu yöntem daha yavaş olacak ama sizin bellek sadece 4KB gerek duyarız.