簡介
MySQL 原生驅動程式管理記憶體的方式與 MySQL 用戶端程式庫不同。這些程式庫在記憶體配置和釋放的方式、從 MySQL 讀取結果時如何以區塊配置記憶體、存在哪些除錯和開發選項,以及如何將從 MySQL 讀取的結果連結到 PHP 使用者變數方面有所不同。
以下注意事項旨在作為對有興趣了解 C 程式碼層級的 MySQL 原生驅動程式的使用者之介紹和摘要。
使用的記憶體管理函式
所有記憶體配置與釋放皆使用 PHP 記憶體管理函式完成。因此,可以使用 PHP API 呼叫(例如 memory_get_usage())來追蹤 mysqlnd 的記憶體消耗量。由於記憶體的配置和釋放是使用 PHP 記憶體管理機制,因此這些變更可能不會立即在作業系統層級顯示出來。PHP 記憶體管理機制如同一個代理,可能會延遲將記憶體釋放回系統。因此,比較 MySQL 原生驅動程式和 MySQL 用戶端程式庫的記憶體使用量相當困難。MySQL 用戶端程式庫直接使用作業系統的記憶體管理呼叫,因此其影響可以在作業系統層級立即觀察到。
任何 PHP 強制執行的記憶體限制也會影響 MySQL 原生驅動程式。這可能會導致在擷取超過 PHP 可用剩餘記憶體大小的大型結果集時發生記憶體不足錯誤。由於 MySQL 用戶端程式庫未使用 PHP 記憶體管理函式,因此它不受任何 PHP 記憶體限制設定的約束。如果使用 MySQL 用戶端程式庫,根據部署模型,PHP 程序的記憶體佔用量可能會超出 PHP 記憶體限制。但也可能讓 PHP 腳本能夠處理更大的結果集,因為部分用於存放結果集的記憶體配置不受 PHP 引擎的控制。
MySQL 原生驅動程式透過一個輕量級的包裝器來呼叫 PHP 記憶體管理函式。除了其他功能外,此包裝器也讓除錯更加容易。
結果集的處理
各種 MySQL 伺服器和各種用戶端 API 會區分緩衝和非緩衝結果集。非緩衝結果集會在用戶端逐一讀取結果時,逐列從 MySQL 傳輸到用戶端。緩衝結果集則是由用戶端程式庫完整擷取後,再傳遞給用戶端。
MySQL 原生驅動程式使用 PHP Streams 與 MySQL 伺服器進行網路通訊。MySQL 傳送的結果會從 PHP Streams 網路緩衝區擷取到 mysqlnd 的結果緩衝區中。結果緩衝區由 zvals 組成。接著,結果會在第二個步驟中提供給 PHP 腳本。這個從結果緩衝區到 PHP 變數的最終傳輸會影響記憶體消耗量,尤其在使用緩衝結果集時更為明顯。
預設情況下,MySQL 原生驅動程式會盡量避免在記憶體中保存兩份緩衝結果。結果只會在內部結果緩衝區及其 zvals 中保存一次。當 PHP 腳本將結果擷取到 PHP 變數中時,這些變數會參考內部結果緩衝區。資料庫查詢結果不會被複製,只會在記憶體中保存一次。如果使用者修改了存放資料庫結果的變數內容,則必須執行寫入時複製(copy-on-write),以避免更改被參考的內部結果緩衝區。緩衝區的內容不得被修改,因為使用者可能會決定再次讀取結果集。寫入時複製機制是透過額外的參考管理清單和標準 zval 參考計數器來實作的。如果使用者將結果集讀取到 PHP 變數中,並在變數被 unset 之前釋放結果集,也必須執行寫入時複製。
一般來說,這種模式適用於讀取一次結果集且不修改保存結果變數的腳本。其主要缺點是額外的參考管理造成的記憶體負擔,這主要來自於持有結果的使用者變數在 mysqlnd 參考管理停止引用它們之前無法完全釋放。當結果集被釋放或執行寫入時複製操作時,MySQL 原生驅動程式會移除對使用者變數的參考。觀察者會看到總記憶體消耗持續增長,直到結果集被釋放。使用統計資訊來檢查腳本是否明確釋放結果集,或者驅動程式是否隱式釋放結果集,從而導致記憶體使用時間超過必要時間。統計資訊還有助於查看發生了多少次寫入時複製操作。
使用等效於或類似於 while ($row = $res->fetch_assoc()) { ... }
的程式碼片段從緩衝結果集中讀取許多小行的 PHP 腳本,可以透過請求副本而不是參考來最佳化記憶體消耗。雖然請求副本意味著在記憶體中保存兩份結果,但它允許 PHP 在迭代結果集期間以及在釋放結果集本身之前釋放 $row
中包含的副本。在負載較高的伺服器上,最佳化峰值記憶體使用率可能有助於提高整體系統效能,儘管對於個別腳本而言,由於額外的分配和記憶體複製操作,複製方法可能會較慢。
監控和除錯
有多種方法可以追蹤 MySQL 原生驅動程式的記憶體使用情況。如果目標是快速獲得高階概覽或驗證 PHP 腳本的記憶體效率,則請檢查程式庫收集的統計資訊。例如,統計資訊允許您捕捉產生比 PHP 腳本處理更多結果的 SQL 陳述式。
可以將除錯追蹤日誌設定為記錄記憶體管理呼叫。這有助於查看何時分配或釋放記憶體。但是,可能不會列出請求的記憶體區塊的大小。
某些最新版本的 MySQL 原生驅動程式具有模擬隨機記憶體不足情況的功能。此功能僅供程式庫的 C 開發人員或 mysqlnd 插件作者使用。請在原始程式碼中搜尋相應的 PHP 設定和更多詳細資訊。此功能被視為私有功能,並且可能隨時修改,恕不另行通知。