2010-05-05 51 views
59

Chỉ là một câu hỏi nhanh.PDO :: fetchAll so với PDO :: tìm nạp trong vòng lặp

Có sự khác biệt về hiệu suất nào giữa việc sử dụng PDO :: fetchAll() và PDO :: fetch() trong một vòng lặp (đối với các tập kết quả lớn) không?

Tôi đang tìm nạp các đối tượng của lớp do người dùng xác định, nếu điều đó tạo ra bất kỳ sự khác biệt nào.

Giả định ban đầu chưa được học của tôi là fetchAll có thể nhanh hơn vì PDO có thể thực hiện nhiều thao tác trong một câu lệnh trong khi mysql_query chỉ có thể thực hiện một câu lệnh. Tuy nhiên tôi có ít kiến ​​thức về các hoạt động bên trong của PDO và tài liệu không nói bất cứ điều gì về điều này, và có hay không fetchAll() chỉ đơn giản là một vòng lặp PHP được bán vào một mảng.

Bất kỳ trợ giúp nào?

+0

Tôi không biết, nhưng Tôi nghi ngờ nó sẽ là tầm thường để chuẩn. – Timothy

Trả lời

67

Ít điểm chuẩn với 200 nghìn bản ghi ngẫu nhiên. Theo dự kiến, phương thức fetchAll nhanh hơn nhưng yêu cầu nhiều bộ nhớ hơn.

Result : 
fetchAll : 0.35965991020203s, 100249408b 
fetch : 0.39197015762329s, 440b 

Mã benchmark được sử dụng:

<?php 
// First benchmark : speed 
$dbh = new PDO('mysql:dbname=testage;dbhost=localhost', 'root', ''); 
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 
$sql = 'SELECT * FROM test_table WHERE 1'; 
$stmt = $dbh->query($sql); 
$data = array(); 
$start_all = microtime(true); 
$data = $stmt->fetchAll(); 
$end_all = microtime(true); 

$stmt = $dbh->query($sql); 
$data = array(); 
$start_one = microtime(true); 
while($data = $stmt->fetch()){} 
$end_one = microtime(true); 

// Second benchmark : memory usage 
$stmt = $dbh->query($sql); 
$data = array(); 
$memory_start_all = memory_get_usage(); 
$data = $stmt->fetchAll(); 
$memory_end_all = memory_get_usage(); 

$stmt = $dbh->query($sql); 
$data = array(); 
$memory_end_one = 0; 
$memory_start_one = memory_get_usage(); 
while($data = $stmt->fetch()){ 
    $memory_end_one = max($memory_end_one, memory_get_usage()); 
} 

echo 'Result : <br/> 
fetchAll : ' . ($end_all - $start_all) . 's, ' . ($memory_end_all - $memory_start_all) . 'b<br/> 
fetch : ' . ($end_one - $start_one) . 's, ' . ($memory_end_one - $memory_start_one) . 'b<br/>'; 
+28

Điểm chuẩn của bạn hoàn toàn bị lỗi! Bạn không lưu trữ dữ liệu của bạn trong mảng dữ liệu $ trong tiêu chuẩn thứ 2 (trong 'while'). Bạn có thực sự nghĩ rằng sự khác biệt bộ nhớ là lớn? Wow đây là một ** CÂU LỆP BẤT CỨ THỰC HIỆN **! – Rudie

+60

Đúng vậy. Đó là mục tiêu của điểm chuẩn: đầu tiên là bạn làm một fetchAll THEN làm công việc trên dữ liệu. Thứ hai, bạn sẽ lấy một hàng, thực hiện công việc trên hàng này, sau đó tìm nạp hàng tiếp theo. Một ví dụ điển hình là khi hiển thị bảng dữ liệu, bạn có cần lưu trữ TẤT CẢ dữ liệu của bạn trước khi ghi vào bộ đệm hay không? – Arkh

+1

Xin lỗi vì đã necroing, tôi không hiểu tại sao mọi người lại nói đây là một điểm chuẩn tồi. Không có lý do gì để lưu toàn bộ tập dữ liệu trừ khi bạn trả lại dữ liệu đó cho người dùng ... điều này chỉ đơn giản là xấu ở địa điểm đầu tiên, hãy sử dụng phân trang trong trường hợp đó. Nếu bạn cần sửa đổi dữ liệu trên cơ sở dữ liệu, bạn nên thực hiện điều này trong cơ sở dữ liệu bằng tập lệnh hoặc thủ tục được lưu trữ, ví dụ: bảng tạm thời. – Populus

9

Một điều về PHP mà tôi thấy là đúng là hầu hết luôn là là một hàm bạn thực hiện chính mình sẽ hầu như luôn chậm hơn so với PHP tương đương. Điều này là do khi một cái gì đó được thực hiện trong PHP, nó không có tất cả các tối ưu hóa thời gian biên dịch mà C có (được viết bằng PHP) và có phí cao trong các cuộc gọi hàm PHP.

+0

Có những lúc đáng để sử dụng công cụ dựng sẵn PHP. Chẳng hạn như tìm kiếm một mảng được sắp xếp (tìm kiếm nhị phân ftw). – Reece45

+2

Tôi không chắc chắn tôi khá hiểu câu trả lời của bạn, nhưng tôi phải làm một vài hoạt động trên tất cả các đối tượng một lần nữa sau khi họ được lấy mà chắc chắn sẽ yêu cầu một vòng lặp foreach. Tôi có nên chỉ gắn bó với tìm nạp một đối tượng cùng một lúc và thực hiện các thao tác trên mỗi đối tượng khi nó được tìm nạp không? –

+0

@ AlReece45 Bạn mô tả hai chức năng hoàn toàn khác nhau. Tôi đã nói về reimplementing chức năng sắp xếp trong PHP vs bằng cách sử dụng '' '' của PHP. @Byron Tôi cá là bạn sẽ thấy rằng tìm nạp tất cả các kết quả bằng fetchAll() sẽ vẫn nhanh hơn, nhưng bạn có thể chuẩn nó bằng 'microtime (TRUE)' nếu bạn có nghi ngờ. –

8

@Arkh

// $data in this case is an array of rows; 

$data = $stmt->fetchAll(); 


// $data in this case is just one row after each loop; 

while($data = $stmt->fetch()){} 


// Try using 

$i = 0; 

while($data[$i++] = $stmt->fetch()){} 

Sự khác biệt bộ nhớ nên trở thành neglijable

+2

@stancu các biến thể trên cùng và dưới cùng có hiệu quả giống hệt nhau, và MEM bổ sung nhìn thấy bằng cách sử dụng fetch() có thể là một tạo phẩm của overhead của while(). Điểm fetch() là xử lý một hàng tại một thời điểm, sử dụng while() để thực hiện điều tương tự như fetchAll (PDO :: FETCH_NUM) là ngớ ngẩn, khi bạn mất các tối ưu hóa trình biên dịch C cấp diễn ra trong PDO mô-đun. – DavidScherer

4

Như Mihai Stancu đã nói, hầu như không có sự khác biệt bộ nhớ mặc dù fetchAll beats fetch + while.

Result : 
fetchAll : 0.160676956177s, 118539304b 
fetch : 0.121752023697s, 118544392b 

tôi có kết quả trên với chạy trong khi một cách chính xác:

$i = 0; 
while($data[$i++] = $stmt->fetch()){ 
    // 
} 

Vì vậy, các fetchAll tiêu thụ ít bộ nhớ, nhưng lấy + khi là nhanh hơn! :)

+6

Nhanh hơn? 0,16 ('fetchAll') so với 0,12 (' fetch') – Joost

+3

Rất tiếc, tôi mệt mỏi .. Đã chỉnh sửa. :} – Rihards

+1

Với tập kết quả lớn hơn đáng kể, bạn sẽ thấy sự khác biệt đáng kể giữa PDOStatement :: fetch() và PDOStatement :: fetchALL(). Việc xác định những gì đủ điều kiện là "Lớn hơn đáng kể" sẽ phụ thuộc vào kích thước của mỗi hàng. Ngoài ra, theo mặc định, PDOStatement :: Fetch()/fetchAll() sử dụng chế độ tìm nạp PDO :: FETCH_BOTH có hiệu quả gấp đôi kích thước của mỗi hàng, thay đổi điều này có thể giúp giảm thiểu việc sử dụng MEM trên các tập kết quả lớn. – DavidScherer

1

Tôi biết đây là chủ đề cũ, nhưng tôi chạy ngang qua câu hỏi này có cùng một câu hỏi. Sau khi chạy cái “điểm chuẩn” đơn giản của riêng tôi và đọc những gì người khác viết ở đây tôi đi đến kết luận rằng đây không phải là khoa học chính xác và trong khi người ta nên cố gắng viết chất lượng, mã sáng, không có điểm lãng phí quá nhiều thời gian khi bắt đầu của dự án.

Đề xuất của tôi là: Thu thập dữ liệu bằng cách chạy mã (trong bản beta?) Trong một thời gian và sau đó bắt đầu tối ưu hóa.

Trong điểm chuẩn đơn giản của tôi (chỉ thử nghiệm thời gian thực hiện) tôi đã có kết quả khác nhau giữa 5% và 50% BOTH cách. Tôi chạy cả hai tùy chọn trong cùng một tập lệnh, nhưng khi tôi chạy tìm nạp + trong khi đầu tiên nó đã nhanh hơn fetchall và ngược lại. (Tôi biết tôi nên chạy chúng đơn và vài trăm lần lấy trung bình và trung bình và sau đó so sánh, nhưng - như tôi đã nói lúc đầu - tôi kết luận rằng trong trường hợp của tôi còn quá sớm để bắt đầu làm như vậy.)

3

Nhưng chắc chắn nếu bạn đang lưu trữ dữ liệu đã tìm nạp trong một mảng, mức sử dụng bộ nhớ sẽ bằng nhau?

<?php 
define('DB_HOST', 'localhost'); 
define('DB_USER', 'root'); 
define('DB_PASS', ''); 
// database to use 
define('DB', 'test'); 
try 
{ 
    $dbh = new \PDO('mysql:dbname='. DB .';host='. DB_HOST, DB_USER, DB_PASS); $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 
    $sql = 'SELECT * FROM users WHERE 1'; 
    $stmt = $dbh->query($sql); 
    $data = array(); 
    $start_all = microtime(true); 
    $data = $stmt->fetchAll(); 
    $end_all = microtime(true); 

    $stmt = $dbh->query($sql); 
    $data = array(); 
    $start_one = microtime(true); 
    while($data = $stmt->fetch()){} 
    $end_one = microtime(true); 

    // Second benchmark : memory usage 
    $stmt = $dbh->query($sql); 
    $data = array(); 
    $memory_start_all = memory_get_usage(); 
    $data = $stmt->fetchAll(); 
    $memory_end_all = memory_get_usage(); 

    $stmt = $dbh->query($sql); 
    $data = array(); 
    $memory_end_one = 0; 
    $memory_start_one = memory_get_usage(); 
    while($data[] = $stmt->fetch()){ 
    $memory_end_one = max($memory_end_one, memory_get_usage()); 
    } 

    echo 'Result : <br/> 
    fetchAll : ' . ($end_all - $start_all) . 's, ' . ($memory_end_all - $memory_start_all) . 'b<br/> 
    fetch : ' . ($end_one - $start_one) . 's, ' . ($memory_end_one - $memory_start_one) . 'b<br/>'; 
} 
catch (PDOException $e) 
{ 
    echo $e->getMessage(); 
} 
?> 

Result : 
fetchAll : 2.6941299438477E-5s, 9824b 
fetch : 1.5974044799805E-5s, 9824b 
4

tất cả các điểm chuẩn ở trên đo lường "dấu chân bộ nhớ" thực sự không chính xác vì lý do rất đơn giản.

PDO theo mặc định không tải tất cả mọi thứ vào bộ nhớ và nó không quan tâm nếu bạn sử dụng tìm nạp hoặc fetchAll. Để thực sự có được lợi ích của việc truy vấn unbuffered bạn nên hướng dẫn PDO để sử dụng các truy vấn không có bộ đệm:

$db->setAttribute(PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);

Trong trường hợp đó, bạn sẽ thấy sự khác biệt lớn trong bộ nhớ dấu chân của kịch bản

+0

Sự khác biệt giữa việc sử dụng '$ stmt-> fetch()' trong khi sử dụng truy vấn đệm (mặc định) và sử dụng '$ stmt-> fetch()' với các truy vấn không được lọc ('PDO :: MYSQL_ATTR_USE_BUFFERED_QUERY' được đặt thành' false ')? Tôi thấy rằng ngay cả khi bạn sử dụng chế độ đệm mặc định, '$ stmt-> fetch()' làm việc cho các tập dữ liệu rất lớn trong khi '$ stmt-> fetchAll()' có thể trả về lỗi giới hạn bộ nhớ. Vì vậy, là '$ stmt-> fetch()' kinda 'unbuffered'? – tonix