2010-03-10 29 views
32

Tôi có một số câu hỏi về cách sử dụng MySQLi, truy vấn và quản lý bộ nhớ có liên quan. Đoạn mã ở đây là chỉ để làm rõ câu hỏi của tôi, vì vậy đừng đổ vào nó để kiểm tra lỗi, vv Tôi biết rằng cần phải làm :)Kết quả truy vấn MySQLi: Cách tiếp cận tốt nhất, bạn có đóng, miễn phí, cả hai?

Giả sử tôi có một cái gì đó như thế này:

@ $db = new mysqli($dbhost, $un, $ps, $dbname); 
$query = "SELECT field1, field2 ". 
     "FROM table1 ". 
     "WHERE field1={$some_value}"; 
$results = $db->query($query); 

while ($result = $results->fetch_object()) { 
    // Do something with the results 
} 

$query = "SELECT field1, field2 ". 
     "FROM table2 ". 
     "WHERE field1={$some_value2}"; 
// question 1 
$results = $db->query($query); 

while ($result = $results->fetch_object()) { 
    // Do something with the second set of results 
} 

// Tidy up, question 2 
if ($results) { 
    $results->free(); 
} 
if ($db) { 
    $db->close(); 
} 

// Question 3, a general one 

Vì vậy, dựa trên các ý kiến ​​trong đoạn mã trên, sau đây là câu hỏi của tôi:

  1. Khi tôi gán kết quả của truy vấn thứ hai để $results, những gì xảy ra với bộ nhớ gắn liền với kết quả trước đó? Tôi có nên giải phóng kết quả đó trước khi chỉ định kết quả mới không?

  2. Liên quan đến 1, khi tôi dọn dẹp ở cuối, chỉ làm sạch kết quả cuối cùng vừa đủ?

  3. Khi tôi cố gắng làm sạch kết quả, tôi có nên giải phóng nó như trên không, tôi có nên đóng nó hay cả hai?

Tôi hỏi câu hỏi 3 vì tài liệu PHP cho mysqli::query có một ví dụ sử dụng chặt chẽ, mặc dù gần không phải là một phần của mysqli_result (xem ví dụ 1 trong mysqli::query). Và ngược lại, văn bản tham chiếu PHP thông thường của tôi sử dụng free (Phát triển web PHP và MySQL, Ấn bản thứ tư, Welling và Thomson).

+3

Bạn biết khoảnh khắc đó từ khi bạn làm việc trong một dự án và bạn quyết định kiểm tra Stackoverflow cho câu trả lời cho câu hỏi chỉ để thấy rằng bạn đã hỏi và đã trả lời câu hỏi đó vài năm trước ...? Ugh ... Tôi phải già đi :) Cảm ơn tất cả một lần nữa vì những câu trả lời tuyệt vời bên dưới. –

Trả lời

48

Khi tôi gán kết quả của truy vấn thứ hai để $results, những gì xảy ra vào bộ nhớ gắn liền với kết quả trước đó?

Khi bạn thực hiện điều này:

$results = $db->query($query); 

Nếu có điều gì đó trong $results trước, nội dung cũ này không thể truy cập được nữa, vì không có tài liệu tham khảo còn lại để nó.

Trong trường hợp này, PHP sẽ đánh dấu nội dung cũ của biến là "không cần thiết nữa" "- và nó sẽ bị xóa khỏi bộ nhớ khi PHP cần một số bộ nhớ.

Điều này, ít nhất, là đúng đối với các biến PHP chung; Tuy nhiên, trong trường hợp các kết quả từ một truy vấn SQL, một số dữ liệu có thể được lưu giữ trong bộ nhớ ở cấp trình điều khiển - qua đó PHP không có nhiều quyền kiểm soát.


Tôi có nên giải phóng kết quả mà trước gán cái mới?

tôi không bao giờ làm điều đó - nhưng, trích dẫn các trang hướng dẫn của mysqli_result::free:

Lưu ý: Bạn nên luôn luôn miễn phí bạn kết quả với mysqli_free_result(), khi đối tượng kết quả của bạn là không cần thiết thêm

Nó có lẽ không quan trọng đối với một tập lệnh nhỏ ... Và cách duy nhất để chắc chắn sẽ kiểm tra, sử dụng memory_get_usage trước và sau khi gọi điện thoại đó là metho d, để xem liệu có sự khác biệt hay không.


liên quan đến 1, khi tôi làm dọn dẹp ở Cuối cùng, được dọn dẹp chỉ là kết quả cuối cùng đủ?

Khi kịch bản kết thúc:

  • Kết nối đến cơ sở dữ liệu sẽ được đóng lại - có nghĩa là bất kỳ bộ nhớ có thể được sử dụng bởi người lái xe nên được trả tự do
  • Tất cả các biến được sử dụng bởi PHP tập lệnh sẽ bị hủy - nghĩa là bộ nhớ họ đang sử dụng sẽ được giải phóng.

Vì vậy, ở cuối tập lệnh, có thể thực sự không cần phải giải phóng kết quả.


Khi tôi cố gắng dọn dẹp Kết quả là, tôi nên được giải phóng nó như trên, tôi nên đóng nó, hay cả hai?

Nếu bạn đóng kết nối cơ sở dữ liệu (sử dụng mysqli::close như bạn đề nghị), điều này sẽ ngắt kết nối bạn từ cơ sở dữ liệu.

Điều này có nghĩa là bạn sẽ phải kết nối lại nếu bạn muốn thực hiện một truy vấn khác! Đó là không tốt ở tất cả (mất một thời gian, tài nguyên, ...)

Nói chung, tôi sẽ không đóng kết nối với cơ sở dữ liệu cho đến khi tôi thực sự chắc chắn rằng tôi sẽ không cần nó nữa - có nghĩa là tôi sẽ không ngắt kết nối trước khi kết thúc kịch bản.

Và dưới dạng "kết thúc tập lệnh" có nghĩa là ", kết nối sẽ bị đóng" ngay cả khi bạn không chỉ định; Tôi gần như không bao giờ tự đóng kết nối.

+3

Chà! Đó là một câu trả lời tuyệt vời và rất tốt bằng văn bản và thông tin. Chính xác những gì tôi đang tìm kiếm. Cảm ơn! Tôi đoán tất cả trong tất cả tôi đang quá lo ngại cho một ngôn ngữ xử lý thu gom rác thải. Cảm ơn một lần nữa! –

+0

Bạn được chào đón :-) ;;; tốt, bạn có thể đúng về việc thu gom rác - nhưng đừng quên rằng việc sử dụng chính của PHP là dành cho các tập lệnh hiếm khi kéo dài hơn một vài giây - có nghĩa là ngay cả khi có rò rỉ bộ nhớ, rất tệ. –

+0

Điểm tốt. Tôi vẫn còn là một "non trẻ" trong thế giới phát triển web, vì vậy có lẽ quá khứ của tôi về việc sử dụng các ngôn ngữ biên dịch cũ đã khiến tôi e ngại một cách không cân xứng khi nói đến quản lý bộ nhớ! :) –

-3

Cách PHP chung là không đóng bất kỳ tài nguyên đã mở nào. Mọi thứ sẽ tự động được đóng lại ở cuối tập lệnh. Trường hợp chỉ trường hợp bạn phải quản lý đóng thủ công là nếu bạn có mã nặng dài để chạy, điều này không phổ biến lắm đối với PHP.

+2

Cảm ơn, điều đó làm cho nó đơn giản. Tuy nhiên, tại sao ngôn ngữ lại bao gồm miễn phí như một phần của mysqli_result? Đó có phải là một điều di sản? –

13

Câu trả lời đã cung cấp là tốt, nhưng tôi muốn thêm một điểm và làm rõ một điểm khác.

Đầu tiên, làm rõ. Về việc sử dụng phương thức close(), điều quan trọng cần lưu ý là OP đã tham chiếu đến phương thức close() của lớp mysqli_result, không phải lớp mysqli.Trong lớp kết quả, phương thức close() chỉ đơn giản là một bí danh cho phương thức free(), như được hiển thị trong documentation, trong khi trong lớp mysqli, nó đóng kết nối. Vì vậy, bạn có thể sử dụng close() trên kết quả thay cho free() nếu muốn.

Thứ hai, điểm bổ sung. Như đã được chỉ ra, mẫu thực hiện của PHP có nghĩa là mọi thứ cuối cùng sẽ được dọn dẹp phía sau bạn, và do đó, bạn không nhất thiết phải lo lắng về việc giải phóng bộ nhớ. Tuy nhiên, nếu bạn đang phân bổ rất nhiều đối tượng kết quả hoặc nếu bạn đang phân bổ các đối tượng kết quả lớn đặc biệt (ví dụ, tìm nạp một lượng lớn dữ liệu), thì có lẽ bạn nên giải phóng bộ nhớ khi hoàn tất xuống con đường thực hiện. Điều này trở nên đặc biệt quan trọng khi ứng dụng của bạn bắt đầu nhận được nhiều lưu lượng truy cập hơn, trong đó tổng số lượng bộ nhớ được kết nối giữa các phiên có thể nhanh chóng trở nên quan trọng.

0

Hiếm khi như vậy, trong rò rỉ bộ nhớ của tôi là một cơn ác mộng để tìm và sửa chữa. Tôi đi ra khỏi con đường của tôi để tránh chúng. Dưới đây là mô hình tôi sử dụng, dựa trên mã bạn cung cấp:

$db = NULL; 
try { 
    $dbPool = "p:$dbhost"; // question 3: use pooling 
    $db = new mysqli($dbPool, $un, $ps, $dbname); 
    if ($db->connect_errno) { 
     throw new Exception('' . $db->connect_error . ' ' . $db->connect_errno 
       . "\n" . $un . '@' . $dbhost . ' ' . $dbname); 
     // NOTE: It's commonly considered a security 
     // risk to output connection information e.g. 
     // host, user and database names. 
    } 

    $query = "SELECT field1, field2 ". 
      "FROM table1 ". 
      "WHERE field1={$some_value}"; 

    $results = NULL; 
    try { 

     if (!$results = $db->query($query)) { 
      throw new Exception($db->error . " " . $db->errno 
        . "\n" . $query); 
      // NOTE: It's commonly considered a security 
      // risk to output SQL ($query). 
     } 
     while ($result = $results->fetch_object()) { 
      // Do something with the results 
     } 

    } catch (Exception $ex) { 
     // log, report, or otherwise handle the error 
    } 
    if ($results) { 
     $results->free(); // question 1: why risk it? 
    } 

    $query = "SELECT field1, field2 ". 
      "FROM table2 ". 
      "WHERE field1={$some_value2}"; 

    $results = NULL; 
    try { 

     if (!$results = $db->query($query)) { 
      throw new Exception($db->error . " " . $db->errno 
        . "\n" . $query); 
      // NOTE: It's commonly considered a security 
      // risk to output SQL ($query). 
     }    
     while ($result = $results->fetch_object()) { 
      // Do something with the second set of results 
     } 

    } catch (Exception $ex) { 
     // log, report, or otherwise handle the error 
    } 
    if ($results) { 
     $results->free(); // question 2: again, why risk it? 
    } 

} catch (Exception $ex) { 
    // log, report, or otherwise handle the error 
} 
if ($db) { 
    $db->close(); 
} 

Theo tôi, kết nối tổng hợp làm tăng cơ hội cho một rò rỉ bộ nhớ, nhưng theo hướng dẫn, các thư viện kết nối tổng hợp làm nhiều dọn dẹp cho bạn tự động:

Kết nối liên tục của phần mở rộng mysqli tuy nhiên cung cấp mã xử lý dọn dẹp được cài sẵn . Các dọn dẹp được thực hiện bởi mysqli bao gồm:

Rollback hoạt động giao dịch

Đóng và thả các bảng tạm thời

bảng Mở khóa

biến Đặt lại phiên

chuẩn bị phát biểu Đóng (luôn luôn xảy ra với PHP)

Đóng trình xử lý

ổ khóa phát hành mua lại với GET_LOCK()

này đảm bảo rằng kết nối liên tục ở trong trạng thái sạch sẽ trên trở về từ hồ bơi kết nối, trước khi quá trình khách hàng sử dụng chúng.

nguồn:http://php.net/manual/en/mysqli.persistconns.php

Tôi cũng đồng ý với Pascal MARTIN rằng đó là một ý tưởng tốt để mở kết nối của bạn ở phần đầu của kịch bản của bạn và đóng nó ở cuối. Tôi nghĩ rằng kết nối tổng hợp làm cho điều đó ít quan trọng hơn, nhưng vẫn là một ý tưởng tốt.

Các vấn đề liên quan